增加了无人车位置信息API接口,显示无人车的位置信息,优化了数据库表结构,删除多余的字段
优化了WebSocket消息格式,删除多余的字段,增加限速值字段,删除多余的 sql 脚本
This commit is contained in:
parent
43e2422197
commit
5453291cd0
@ -1 +1 @@
|
||||
0.3.0
|
||||
0.3.2
|
||||
57
changelog.md
57
changelog.md
@ -5,6 +5,63 @@
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||
版本规范基于 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [0.3.2] - 2025-07-10
|
||||
|
||||
- 增加无人车位置信息API接口,显示无人车的位置信息
|
||||
- 优化了数据库表结构,删除多余的车牌号字段
|
||||
- 优化了WebSocket消息格式,删除多余的字段,增加限速值字段
|
||||
- 删除多余的 sql 脚本
|
||||
|
||||
## [0.3.1] - 2025-01-17
|
||||
|
||||
### 🗄️ **数据库规范化改造:车辆运动信息API设计优化**
|
||||
- **架构设计优化**:实现数据库规范化,遵循数据库设计最佳实践
|
||||
- 将`vehicle_locations`表从包含车牌号改为只存储`vehicle_id`关联字段
|
||||
- 车牌号、车辆类型等基础信息通过关联`sys_vehicle_info`表查询获取
|
||||
- 避免数据冗余,提高数据一致性和维护性
|
||||
|
||||
### 📊 技术实现亮点
|
||||
1. **关联查询优化**
|
||||
- 使用LEFT JOIN联表查询,一次性获取完整车辆信息
|
||||
- PostGIS空间数据与业务数据完美结合
|
||||
- SQL查询性能优化,减少多次查询开销
|
||||
|
||||
2. **向后兼容性保障**
|
||||
- 在碰撞检测模块中添加@Deprecated兼容性方法
|
||||
- 渐进式改造,最小化对现有系统的影响
|
||||
- 保持API接口不变,前端无感知升级
|
||||
|
||||
3. **代码质量提升**
|
||||
- 实体类字段精简,职责更加清晰
|
||||
- Mapper XML配置优化,查询逻辑标准化
|
||||
- 添加详细的表注释,提高代码可维护性
|
||||
|
||||
### 🛠️ 核心修改内容
|
||||
- **实体类重构**:`SysVehicleLocation`删除冗余字段,添加关联查询字段
|
||||
- **数据访问层优化**:`SysVehicleLocationMapper.xml`使用JOIN查询获取完整信息
|
||||
- **碰撞检测适配**:`VehicleLocation`实体类添加兼容性方法,确保平滑迁移
|
||||
- **数据库迁移**:`unified_database_migration.sql`更新表结构和视图定义
|
||||
|
||||
### ✅ 质量保证
|
||||
- **编译测试**:所有模块编译通过,无错误和警告
|
||||
- **功能验证**:API端点正常工作,返回正确的关联信息
|
||||
- **应用启动**:系统正常启动,健康检查通过
|
||||
- **数据一致性**:通过关联查询保证数据完整性
|
||||
|
||||
### 📋 影响文件
|
||||
- `qaup-system/src/main/java/com/qaup/system/domain/SysVehicleLocation.java`:实体类重构
|
||||
- `qaup-system/src/main/resources/mapper/system/SysVehicleLocationMapper.xml`:关联查询优化
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/common/model/spatial/VehicleLocation.java`:兼容性改造
|
||||
- `qaup-collision/src/main/java/com/qaup/collision/common/model/repository/VehicleLocationRepository.java`:查询方法更新
|
||||
- `sql/unified_database_migration.sql`:数据库迁移脚本
|
||||
- `VERSION.md`:版本号更新为0.3.1
|
||||
|
||||
### 🎯 用户价值
|
||||
- **数据库设计规范**:符合关系型数据库规范化原则,提高系统可维护性
|
||||
- **性能优化**:减少数据冗余,提升查询和存储效率
|
||||
- **系统稳定性**:通过关联查询确保数据一致性,避免数据不同步问题
|
||||
- **扩展性增强**:规范化设计为后续功能扩展提供更好的基础架构
|
||||
|
||||
## [0.3.0] - 2025-07-09
|
||||
|
||||
### 🚀 **重大升级:从Spring Boot 2.x升级到Spring Boot 3.x**
|
||||
|
||||
31
doc/guide/commands.md
Normal file
31
doc/guide/commands.md
Normal file
@ -0,0 +1,31 @@
|
||||
# 命令
|
||||
|
||||
### 强制编译
|
||||
```bash
|
||||
mvn clean install -DskipTests
|
||||
```
|
||||
|
||||
### 启动后端
|
||||
```bash
|
||||
cd qaup-admin
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
### 启动前端
|
||||
```bash
|
||||
cd qaup-admin
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 访问Swagger UI (前端)
|
||||
http://localhost:8080/swagger-ui/index.html
|
||||
|
||||
### 查看端口占用
|
||||
```bash
|
||||
lsof -ti:8080
|
||||
```
|
||||
|
||||
### 杀死进程
|
||||
```bash
|
||||
kill -9 进程ID
|
||||
```
|
||||
@ -332,8 +332,3 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
|
||||
|
||||
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" http://localhost:8080/collision
|
||||
|
||||
|
||||
强制编译
|
||||
mvn clean install -DskipTests
|
||||
@ -9,6 +9,12 @@
|
||||
- 速度:50km/h
|
||||
- 方向:180度
|
||||
|
||||
- 航班号:MU5123
|
||||
- 起点:经度120.088076,纬度36.374179
|
||||
- 终点:经度120.077971,纬度36.371503
|
||||
- 速度:50km/h
|
||||
- 方向:180度
|
||||
|
||||
### 2. 特勤车
|
||||
- 车牌号:鲁B123
|
||||
- 起点:经度120.080801,纬度36.366626
|
||||
|
||||
96
doc/requirement/todolist.md
Normal file
96
doc/requirement/todolist.md
Normal file
@ -0,0 +1,96 @@
|
||||
# 待办事项
|
||||
|
||||
### 设置发送给前端的Websocket消息的类型
|
||||
- 是否完成(true)
|
||||
|
||||
```java
|
||||
/**
|
||||
* 电子围栏告警级别枚举
|
||||
*/
|
||||
public enum GeofenceAlertLevel {
|
||||
|
||||
/**
|
||||
* 信息级别
|
||||
*/
|
||||
INFO("信息", 1),
|
||||
|
||||
/**
|
||||
* 警告级别
|
||||
*/
|
||||
WARNING("警告", 2),
|
||||
|
||||
/**
|
||||
* 严重级别
|
||||
*/
|
||||
CRITICAL("严重", 3),
|
||||
|
||||
/**
|
||||
* 紧急级别
|
||||
*/
|
||||
EMERGENCY("紧急", 4);
|
||||
```
|
||||
|
||||
### 在超速消息中,增加一个字段,表示限速值,单位是km/h
|
||||
- 是否完成(true)
|
||||
|
||||
```java
|
||||
public class RuleViolationPayload {
|
||||
/**
|
||||
* 实际值
|
||||
*/
|
||||
@JsonProperty("actualValue")
|
||||
private Double actualValue;
|
||||
|
||||
/**
|
||||
* 限制值
|
||||
*/
|
||||
@JsonProperty("limitValue")
|
||||
private Double limitValue;
|
||||
|
||||
```
|
||||
|
||||
|
||||
## API 接口
|
||||
|
||||
### 增加无人车位置信息接口,显示无人车的位置信息,包括:
|
||||
- 无人车的ID
|
||||
- 无人车的车牌号
|
||||
- 无人车的类型
|
||||
- 无人车的状态
|
||||
- 无人车的位置
|
||||
- 无人车的速度
|
||||
- 无人车的方向
|
||||
|
||||
### 增加无人车的运行信息接口,显示无人车的运行信息,包括:
|
||||
- 无人车的ID
|
||||
- 无人车的车牌号
|
||||
- 无人车的类型
|
||||
- 无人车的电池电量
|
||||
- 无人车的运行时间
|
||||
- 无人车的运行距离
|
||||
- 无人车的运行速度
|
||||
|
||||
### 增加无人车的任务执行状态接口,显示无人车的任务执行状态,包括:
|
||||
- 无人车的ID
|
||||
- 无人车的车牌号
|
||||
- 无人车的类型
|
||||
- 无人车的任务执行状态
|
||||
- 无人车的任务执行开始时间
|
||||
- 无人车的任务完成百分比
|
||||
- 无人车的任务执行轨迹
|
||||
|
||||
|
||||
### 替换 Swagger 为 SpringDoc
|
||||
- springfox-boot-starter:Swagger 3.0.0 可能不兼容 Spring Boot 3.x,建议迁移到 SpringDoc OpenAPI(已在 properties 中定义 springdoc.version=2.6.0,但未使用)。
|
||||
|
||||
- 是否完成(true)
|
||||
|
||||
- 在 pom.xml 中添加以下依赖:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
36
pom.xml
36
pom.xml
@ -14,8 +14,8 @@
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
|
||||
<druid.version>1.2.23</druid.version>
|
||||
<mybatis-spring-boot.version>3.0.4</mybatis-spring-boot.version>
|
||||
<druid.version>1.2.25</druid.version>
|
||||
<bitwalker.version>1.21</bitwalker.version>
|
||||
<swagger.version>3.0.0</swagger.version>
|
||||
<kaptcha.version>2.3.3</kaptcha.version>
|
||||
@ -28,7 +28,7 @@
|
||||
<jwt.version>0.9.1</jwt.version>
|
||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||
<jakarta.version>6.0.0</jakarta.version>
|
||||
<springdoc.version>2.6.0</springdoc.version>
|
||||
<springdoc.version>2.8.9</springdoc.version>
|
||||
<!-- 空间计算库版本管理 -->
|
||||
<geotools.version>28.5</geotools.version>
|
||||
<jts.version>1.19.0</jts.version>
|
||||
@ -46,7 +46,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>3.3.5</version>
|
||||
<version>3.5.3</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@ -92,17 +92,11 @@
|
||||
<version>${oshi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger3依赖 -->
|
||||
<!-- SpringDoc OpenAPI UI 依赖 -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- io常用工具类 -->
|
||||
@ -247,6 +241,13 @@
|
||||
<version>${jakarta.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 统一管理 Netty -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.2.2.Final</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@ -266,7 +267,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.14.0</version>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
@ -279,7 +280,10 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.3</version>
|
||||
<configuration>
|
||||
<jvmArguments>--enable-native-access=ALL-UNNAMED</jvmArguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@ -36,19 +36,6 @@
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger3-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Postgresql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
@ -81,6 +68,12 @@
|
||||
<version>${qaup.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI UI 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -88,7 +81,7 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@ -100,7 +93,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<warName>${project.artifactId}</warName>
|
||||
|
||||
@ -20,18 +20,18 @@ import com.qaup.system.domain.SysDriverInfo;
|
||||
import com.qaup.system.service.ISysDriverInfoService;
|
||||
import com.qaup.common.utils.poi.ExcelUtil;
|
||||
import com.qaup.common.core.page.TableDataInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
/**
|
||||
* 驾驶员信息Controller
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-06-30
|
||||
*/
|
||||
@Api(tags = "驾驶员信息管理")
|
||||
@Tag(name = "驾驶员信息管理", description = "驾驶员信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/system/driver_info")
|
||||
public class SysDriverInfoController extends BaseController
|
||||
@ -43,7 +43,7 @@ public class SysDriverInfoController extends BaseController
|
||||
* 查询驾驶员信息列表
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:list')")
|
||||
@ApiOperation("查询驾驶员信息列表")
|
||||
@Operation(summary = "查询驾驶员信息列表")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(SysDriverInfo sysDriverInfo)
|
||||
{
|
||||
@ -57,7 +57,7 @@ public class SysDriverInfoController extends BaseController
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:export')")
|
||||
@Log(title = "驾驶员信息", businessType = BusinessType.EXPORT)
|
||||
@ApiOperation("导出驾驶员信息列表")
|
||||
@Operation(summary = "导出驾驶员信息列表")
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, SysDriverInfo sysDriverInfo)
|
||||
{
|
||||
@ -70,8 +70,8 @@ public class SysDriverInfoController extends BaseController
|
||||
* 获取驾驶员信息详细信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:query')")
|
||||
@ApiOperation("获取驾驶员信息详细信息")
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
@Operation(summary = "获取驾驶员信息详细信息")
|
||||
@Parameter(name = "userId", description = "用户ID", required = true)
|
||||
@GetMapping(value = "/{userId}")
|
||||
public AjaxResult getInfo(@PathVariable("userId") Long userId)
|
||||
{
|
||||
@ -83,10 +83,8 @@ public class SysDriverInfoController extends BaseController
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:add')")
|
||||
@Log(title = "驾驶员信息", businessType = BusinessType.INSERT)
|
||||
@ApiOperation("新增驾驶员信息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "licenseType", value = "驾驶证类型", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
})
|
||||
@Operation(summary = "新增驾驶员信息")
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "驾驶员信息对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysDriverInfo.class)))
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody SysDriverInfo sysDriverInfo)
|
||||
{
|
||||
@ -98,11 +96,8 @@ public class SysDriverInfoController extends BaseController
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:edit')")
|
||||
@Log(title = "驾驶员信息", businessType = BusinessType.UPDATE)
|
||||
@ApiOperation("修改驾驶员信息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "licenseType", value = "驾驶证类型", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
})
|
||||
@Operation(summary = "修改驾驶员信息")
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "驾驶员信息对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysDriverInfo.class)))
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody SysDriverInfo sysDriverInfo)
|
||||
{
|
||||
@ -114,8 +109,8 @@ public class SysDriverInfoController extends BaseController
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:driver_info:remove')")
|
||||
@Log(title = "驾驶员信息", businessType = BusinessType.DELETE)
|
||||
@ApiOperation("删除驾驶员信息")
|
||||
@ApiImplicitParam(name = "userIds", value = "用户ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
@Operation(summary = "删除驾驶员信息")
|
||||
@Parameter(name = "userIds", description = "用户ID", required = true)
|
||||
@DeleteMapping("/{userIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] userIds)
|
||||
{
|
||||
|
||||
@ -1,141 +1,143 @@
|
||||
package com.qaup.web.controller.system;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.qaup.common.annotation.Log;
|
||||
import com.qaup.common.config.QaupConfig;
|
||||
import com.qaup.common.core.controller.BaseController;
|
||||
import com.qaup.common.core.domain.AjaxResult;
|
||||
import com.qaup.common.core.domain.entity.SysUser;
|
||||
import com.qaup.common.core.domain.model.LoginUser;
|
||||
import com.qaup.common.enums.BusinessType;
|
||||
import com.qaup.common.utils.DateUtils;
|
||||
import com.qaup.common.utils.SecurityUtils;
|
||||
import com.qaup.common.utils.StringUtils;
|
||||
import com.qaup.common.utils.file.FileUploadUtils;
|
||||
import com.qaup.common.utils.file.MimeTypeUtils;
|
||||
import com.qaup.framework.web.service.TokenService;
|
||||
import com.qaup.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* 个人信息 业务处理
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/user/profile")
|
||||
public class SysProfileController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
*/
|
||||
@GetMapping
|
||||
public AjaxResult profile()
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
SysUser user = loginUser.getUser();
|
||||
AjaxResult ajax = AjaxResult.success(user);
|
||||
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
|
||||
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*/
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult updateProfile(@RequestBody SysUser user)
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
SysUser currentUser = loginUser.getUser();
|
||||
currentUser.setEmail(user.getEmail());
|
||||
currentUser.setPhonenumber(user.getPhonenumber());
|
||||
currentUser.setSex(user.getSex());
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
|
||||
{
|
||||
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
|
||||
{
|
||||
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
if (userService.updateUserProfile(currentUser) > 0)
|
||||
{
|
||||
// 更新缓存用户信息
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
}
|
||||
return error("修改个人信息异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updatePwd")
|
||||
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String userName = loginUser.getUsername();
|
||||
String password = loginUser.getPassword();
|
||||
if (!SecurityUtils.matchesPassword(oldPassword, password))
|
||||
{
|
||||
return error("修改密码失败,旧密码错误");
|
||||
}
|
||||
if (SecurityUtils.matchesPassword(newPassword, password))
|
||||
{
|
||||
return error("新密码不能与旧密码相同");
|
||||
}
|
||||
newPassword = SecurityUtils.encryptPassword(newPassword);
|
||||
if (userService.resetUserPwd(userName, newPassword) > 0)
|
||||
{
|
||||
// 更新缓存用户密码&密码最后更新时间
|
||||
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
|
||||
loginUser.getUser().setPassword(newPassword);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
}
|
||||
return error("修改密码异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 头像上传
|
||||
*/
|
||||
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/avatar")
|
||||
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
|
||||
{
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String avatar = FileUploadUtils.upload(QaupConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("imgUrl", avatar);
|
||||
// 更新缓存用户头像
|
||||
loginUser.getUser().setAvatar(avatar);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return ajax;
|
||||
}
|
||||
}
|
||||
return error("上传图片异常,请联系管理员");
|
||||
}
|
||||
}
|
||||
package com.qaup.web.controller.system;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.qaup.common.annotation.Log;
|
||||
import com.qaup.common.config.QaupConfig;
|
||||
import com.qaup.common.core.controller.BaseController;
|
||||
import com.qaup.common.core.domain.AjaxResult;
|
||||
import com.qaup.common.core.domain.entity.SysUser;
|
||||
import com.qaup.common.core.domain.model.LoginUser;
|
||||
import com.qaup.common.enums.BusinessType;
|
||||
import com.qaup.common.utils.DateUtils;
|
||||
import com.qaup.common.utils.SecurityUtils;
|
||||
import com.qaup.common.utils.StringUtils;
|
||||
import com.qaup.common.utils.file.FileUploadUtils;
|
||||
import com.qaup.common.utils.file.MimeTypeUtils;
|
||||
import com.qaup.framework.web.service.TokenService;
|
||||
import com.qaup.system.service.ISysUserService;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
|
||||
/**
|
||||
* 个人信息 业务处理
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/user/profile")
|
||||
@Hidden
|
||||
public class SysProfileController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
*/
|
||||
@GetMapping
|
||||
public AjaxResult profile()
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
SysUser user = loginUser.getUser();
|
||||
AjaxResult ajax = AjaxResult.success(user);
|
||||
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
|
||||
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*/
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult updateProfile(@RequestBody SysUser user)
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
SysUser currentUser = loginUser.getUser();
|
||||
currentUser.setEmail(user.getEmail());
|
||||
currentUser.setPhonenumber(user.getPhonenumber());
|
||||
currentUser.setSex(user.getSex());
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
|
||||
{
|
||||
return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
|
||||
{
|
||||
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
if (userService.updateUserProfile(currentUser) > 0)
|
||||
{
|
||||
// 更新缓存用户信息
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
}
|
||||
return error("修改个人信息异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updatePwd")
|
||||
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String userName = loginUser.getUsername();
|
||||
String password = loginUser.getPassword();
|
||||
if (!SecurityUtils.matchesPassword(oldPassword, password))
|
||||
{
|
||||
return error("修改密码失败,旧密码错误");
|
||||
}
|
||||
if (SecurityUtils.matchesPassword(newPassword, password))
|
||||
{
|
||||
return error("新密码不能与旧密码相同");
|
||||
}
|
||||
newPassword = SecurityUtils.encryptPassword(newPassword);
|
||||
if (userService.resetUserPwd(userName, newPassword) > 0)
|
||||
{
|
||||
// 更新缓存用户密码&密码最后更新时间
|
||||
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
|
||||
loginUser.getUser().setPassword(newPassword);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
}
|
||||
return error("修改密码异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 头像上传
|
||||
*/
|
||||
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/avatar")
|
||||
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
|
||||
{
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String avatar = FileUploadUtils.upload(QaupConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("imgUrl", avatar);
|
||||
// 更新缓存用户头像
|
||||
loginUser.getUser().setAvatar(avatar);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return ajax;
|
||||
}
|
||||
}
|
||||
return error("上传图片异常,请联系管理员");
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,14 +30,13 @@ import com.qaup.system.domain.SysUserRole;
|
||||
import com.qaup.system.service.ISysDeptService;
|
||||
import com.qaup.system.service.ISysRoleService;
|
||||
import com.qaup.system.service.ISysUserService;
|
||||
import io.swagger.annotations.Api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
/**
|
||||
* 角色信息
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@Api("角色信息管理")
|
||||
@Tag(name = "角色信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/system/role")
|
||||
public class SysRoleController extends BaseController
|
||||
|
||||
@ -31,14 +31,14 @@ import com.qaup.system.service.ISysDeptService;
|
||||
import com.qaup.system.service.ISysPostService;
|
||||
import com.qaup.system.service.ISysRoleService;
|
||||
import com.qaup.system.service.ISysUserService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@Api("用户信息管理")
|
||||
@Tag(name = "用户管理", description = "用户管理相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/system/user")
|
||||
public class SysUserController extends BaseController
|
||||
|
||||
@ -20,10 +20,11 @@ import com.qaup.system.domain.SysVehicleInfo;
|
||||
import com.qaup.system.service.ISysVehicleInfoService;
|
||||
import com.qaup.common.utils.poi.ExcelUtil;
|
||||
import com.qaup.common.core.page.TableDataInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 车辆信息Controller
|
||||
@ -31,7 +32,7 @@ import io.swagger.annotations.ApiImplicitParams;
|
||||
* @author tellme
|
||||
* @date 2025-07-01
|
||||
*/
|
||||
@Api(tags = "车辆信息管理")
|
||||
@Tag(name = "车辆信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/system/vehicle_info")
|
||||
public class SysVehicleInfoController extends BaseController
|
||||
@ -43,7 +44,7 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 查询车辆信息列表
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:list')")
|
||||
@ApiOperation("查询车辆信息列表")
|
||||
@Operation(summary = "查询车辆信息列表")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(SysVehicleInfo sysVehicleInfo)
|
||||
{
|
||||
@ -56,7 +57,7 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 导出车辆信息列表
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:export')")
|
||||
@ApiOperation("导出车辆信息列表")
|
||||
@Operation(summary = "导出车辆信息列表")
|
||||
@Log(title = "车辆信息", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, SysVehicleInfo sysVehicleInfo)
|
||||
@ -70,10 +71,8 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 获取车辆信息详细信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:query')")
|
||||
@ApiOperation("获取车辆信息详细信息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "vehicleId", value = "车辆ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
})
|
||||
@Operation(summary = "获取车辆信息详细信息")
|
||||
@Parameter(name = "vehicleId", description = "车辆ID", required = true)
|
||||
@GetMapping(value = "/{vehicleId}")
|
||||
public AjaxResult getInfo(@PathVariable("vehicleId") Long vehicleId)
|
||||
{
|
||||
@ -84,18 +83,9 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 新增车辆信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:add')")
|
||||
@ApiOperation("新增车辆信息")
|
||||
@Operation(summary = "新增车辆信息")
|
||||
@Log(title = "车辆信息", businessType = BusinessType.INSERT)
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "licensePlate", value = "车牌号", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "vinNumber", value = "VIN码", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "typeId", value = "类型ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "brand", value = "品牌", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "owningUnit", value = "所属单位", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "contactPerson", value = "联系人", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "phoneNumber", value = "电话", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "imageUrl", value = "图片URL", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
})
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "车辆信息对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysVehicleInfo.class)))
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody SysVehicleInfo sysVehicleInfo)
|
||||
{
|
||||
@ -106,18 +96,8 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 修改车辆信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:edit')")
|
||||
@ApiOperation("修改车辆信息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "vehicleId", value = "车辆ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "licensePlate", value = "车牌号", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "vinNumber", value = "VIN码", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "typeId", value = "类型ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "brand", value = "品牌", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "owningUnit", value = "所属单位", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "contactPerson", value = "联系人", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "phoneNumber", value = "电话", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "imageUrl", value = "图片URL", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
})
|
||||
@Operation(summary = "修改车辆信息")
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "车辆信息对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysVehicleInfo.class)))
|
||||
@Log(title = "车辆信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody SysVehicleInfo sysVehicleInfo)
|
||||
@ -129,9 +109,9 @@ public class SysVehicleInfoController extends BaseController
|
||||
* 删除车辆信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_info:remove')")
|
||||
@ApiOperation("删除车辆信息")
|
||||
@Operation(summary = "删除车辆信息")
|
||||
@Log(title = "车辆信息", businessType = BusinessType.DELETE)
|
||||
@ApiImplicitParam(name = "vehicleIds", value = "车辆ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
@Parameter(name = "vehicleIds", description = "车辆ID", required = true)
|
||||
@DeleteMapping("/{vehicleIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] vehicleIds)
|
||||
{
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
package com.qaup.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.qaup.common.annotation.Log;
|
||||
import com.qaup.common.core.controller.BaseController;
|
||||
import com.qaup.common.core.domain.AjaxResult;
|
||||
import com.qaup.common.enums.BusinessType;
|
||||
import com.qaup.system.domain.SysVehicleLocation;
|
||||
import com.qaup.system.service.ISysVehicleLocationService;
|
||||
import com.qaup.common.utils.poi.ExcelUtil;
|
||||
import com.qaup.common.core.page.TableDataInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 车辆运动信息Controller
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
@Tag(name = "车辆运动信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/system/vehicle_location")
|
||||
public class SysVehicleLocationController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysVehicleLocationService sysVehicleLocationService;
|
||||
|
||||
/**
|
||||
* 查询车辆运动信息列表
|
||||
*/
|
||||
@Operation(summary = "查询车辆运动信息列表", description = "支持按车辆ID、车牌号、车辆类型、品牌、所属单位、数据质量、时间段等条件查询。车牌号、车辆类型等基础信息通过关联sys_vehicle_info表获取")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(SysVehicleLocation sysVehicleLocation)
|
||||
{
|
||||
startPage();
|
||||
List<SysVehicleLocation> list = sysVehicleLocationService.selectSysVehicleLocationList(sysVehicleLocation);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出车辆运动信息列表
|
||||
*/
|
||||
@Operation(summary = "导出车辆运动信息列表", description = "导出包含关联的车辆基础信息和位置数据的Excel文件")
|
||||
@Log(title = "车辆运动信息", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, SysVehicleLocation sysVehicleLocation)
|
||||
{
|
||||
List<SysVehicleLocation> list = sysVehicleLocationService.selectSysVehicleLocationList(sysVehicleLocation);
|
||||
ExcelUtil<SysVehicleLocation> util = new ExcelUtil<SysVehicleLocation>(SysVehicleLocation.class);
|
||||
util.exportExcel(response, list, "车辆运动信息数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询车辆运动信息列表
|
||||
*/
|
||||
@Operation(summary = "根据车辆ID查询车辆运动信息列表", description = "查询指定车辆的所有位置记录,包含关联的车辆基础信息")
|
||||
@Parameter(name = "vehicleId", description = "车辆ID", required = true)
|
||||
@GetMapping("/vehicle/{vehicleId}")
|
||||
public AjaxResult getByVehicleId(@PathVariable("vehicleId") Long vehicleId)
|
||||
{
|
||||
List<SysVehicleLocation> list = sysVehicleLocationService.selectSysVehicleLocationByVehicleId(vehicleId);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆运动信息列表
|
||||
*/
|
||||
@Operation(summary = "根据车牌号查询车辆运动信息列表", description = "通过车牌号关联查询车辆的所有位置记录")
|
||||
@Parameter(name = "licensePlate", description = "车牌号", required = true)
|
||||
@GetMapping("/plate/{licensePlate}")
|
||||
public AjaxResult getByLicensePlate(@PathVariable("licensePlate") String licensePlate)
|
||||
{
|
||||
List<SysVehicleLocation> list = sysVehicleLocationService.selectSysVehicleLocationByLicensePlate(licensePlate);
|
||||
return success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询车辆最新位置信息
|
||||
*/
|
||||
@Operation(summary = "根据车辆ID查询车辆最新位置信息", description = "获取指定车辆的最新位置记录,包含关联的车辆基础信息")
|
||||
@Parameter(name = "vehicleId", description = "车辆ID", required = true)
|
||||
@GetMapping("/latest/vehicle/{vehicleId}")
|
||||
public AjaxResult getLatestByVehicleId(@PathVariable("vehicleId") Long vehicleId)
|
||||
{
|
||||
SysVehicleLocation sysVehicleLocation = sysVehicleLocationService.selectLatestSysVehicleLocationByVehicleId(vehicleId);
|
||||
return success(sysVehicleLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆最新位置信息
|
||||
*/
|
||||
@Operation(summary = "根据车牌号查询车辆最新位置信息", description = "通过车牌号关联查询车辆的最新位置记录")
|
||||
@Parameter(name = "licensePlate", description = "车牌号", required = true)
|
||||
@GetMapping("/latest/plate/{licensePlate}")
|
||||
public AjaxResult getLatestByLicensePlate(@PathVariable("licensePlate") String licensePlate)
|
||||
{
|
||||
SysVehicleLocation sysVehicleLocation = sysVehicleLocationService.selectLatestSysVehicleLocationByLicensePlate(licensePlate);
|
||||
return success(sysVehicleLocation);
|
||||
}
|
||||
}
|
||||
@ -19,10 +19,11 @@ import com.qaup.common.enums.BusinessType;
|
||||
import com.qaup.system.domain.SysVehicleType;
|
||||
import com.qaup.system.service.ISysVehicleTypeService;
|
||||
import com.qaup.common.utils.poi.ExcelUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 车辆类型Controller
|
||||
@ -30,7 +31,7 @@ import io.swagger.annotations.ApiImplicitParams;
|
||||
* @author tellme
|
||||
* @date 2025-07-01
|
||||
*/
|
||||
@Api(tags = "车辆类型管理")
|
||||
@Tag(name = "车辆类型管理")
|
||||
@RestController
|
||||
@RequestMapping("/system/vehicle_type")
|
||||
public class SysVehicleTypeController extends BaseController
|
||||
@ -42,7 +43,7 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 查询车辆类型列表
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:list')")
|
||||
@ApiOperation("查询车辆类型列表")
|
||||
@Operation(summary = "查询车辆类型列表")
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(SysVehicleType sysVehicleType)
|
||||
{
|
||||
@ -54,7 +55,7 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 导出车辆类型列表
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:export')")
|
||||
@ApiOperation("导出车辆类型列表")
|
||||
@Operation(summary = "导出车辆类型列表")
|
||||
@Log(title = "车辆类型", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, SysVehicleType sysVehicleType)
|
||||
@ -68,11 +69,9 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 获取车辆类型详细信息
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:query')")
|
||||
@ApiOperation("获取车辆类型详细信息")
|
||||
@Operation(summary = "获取车辆类型详细信息")
|
||||
@GetMapping(value = "/{typeId}")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "typeId", value = "类型ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
})
|
||||
@Parameter(name = "typeId", description = "类型ID", required = true)
|
||||
public AjaxResult getInfo(@PathVariable("typeId") Long typeId)
|
||||
{
|
||||
return success(sysVehicleTypeService.selectSysVehicleTypeByTypeId(typeId));
|
||||
@ -82,13 +81,9 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 新增车辆类型
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:add')")
|
||||
@ApiOperation("新增车辆类型")
|
||||
@Operation(summary = "新增车辆类型")
|
||||
@Log(title = "车辆类型", businessType = BusinessType.INSERT)
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "parentId", value = "父类型ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "typeName", value = "类型名称", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "level", value = "类型级别", required = true, dataType = "Integer", dataTypeClass = Integer.class),
|
||||
})
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "车辆类型对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysVehicleType.class)))
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody SysVehicleType sysVehicleType)
|
||||
{
|
||||
@ -99,14 +94,9 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 修改车辆类型
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:edit')")
|
||||
@ApiOperation("修改车辆类型")
|
||||
@Operation(summary = "修改车辆类型")
|
||||
@Log(title = "车辆类型", businessType = BusinessType.UPDATE)
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "typeId", value = "类型ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "parentId", value = "父类型ID", required = true, dataType = "Long", dataTypeClass = Long.class),
|
||||
@ApiImplicitParam(name = "typeName", value = "类型名称", required = true, dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "level", value = "类型级别", required = true, dataType = "Integer", dataTypeClass = Integer.class),
|
||||
})
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "车辆类型对象", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = SysVehicleType.class)))
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody SysVehicleType sysVehicleType)
|
||||
{
|
||||
@ -117,9 +107,9 @@ public class SysVehicleTypeController extends BaseController
|
||||
* 删除车辆类型
|
||||
*/
|
||||
//@PreAuthorize("@ss.hasPermi('system:vehicle_type:remove')")
|
||||
@ApiOperation("删除车辆类型")
|
||||
@Operation(summary = "删除车辆类型")
|
||||
@Log(title = "车辆类型", businessType = BusinessType.DELETE)
|
||||
@ApiImplicitParam(name = "typeIds", value = "类型ID", required = true, dataType = "Long", dataTypeClass = Long.class)
|
||||
@Parameter(name = "typeIds", description = "类型ID", required = true)
|
||||
@DeleteMapping("/{typeIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] typeIds)
|
||||
{
|
||||
|
||||
@ -15,19 +15,17 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import com.qaup.common.core.controller.BaseController;
|
||||
import com.qaup.common.core.domain.R;
|
||||
import com.qaup.common.utils.StringUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* swagger 用户测试方法
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@Api("用户信息管理")
|
||||
@Tag(name = "用户信息管理")
|
||||
@RestController
|
||||
@RequestMapping("/test/user")
|
||||
public class TestController extends BaseController
|
||||
@ -38,7 +36,7 @@ public class TestController extends BaseController
|
||||
users.put(2, new UserEntity(2, "qaup", "admin123", "15666666666"));
|
||||
}
|
||||
|
||||
@ApiOperation("获取用户列表")
|
||||
@Operation(summary = "获取用户列表")
|
||||
@GetMapping("/list")
|
||||
public R<List<UserEntity>> userList()
|
||||
{
|
||||
@ -46,8 +44,8 @@ public class TestController extends BaseController
|
||||
return R.ok(userList);
|
||||
}
|
||||
|
||||
@ApiOperation("获取用户详细")
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
|
||||
@Operation(summary = "获取用户详细")
|
||||
@Parameter(name = "userId", description = "用户ID", required = true)
|
||||
@GetMapping("/{userId}")
|
||||
public R<UserEntity> getUser(@PathVariable Integer userId)
|
||||
{
|
||||
@ -61,15 +59,9 @@ public class TestController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation("新增用户")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
|
||||
@ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
|
||||
})
|
||||
@Operation(summary = "新增用户")
|
||||
@PostMapping("/save")
|
||||
public R<String> save(UserEntity user)
|
||||
public R<String> save(@RequestBody UserEntity user)
|
||||
{
|
||||
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
|
||||
{
|
||||
@ -79,7 +71,7 @@ public class TestController extends BaseController
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("更新用户")
|
||||
@Operation(summary = "更新用户")
|
||||
@PutMapping("/update")
|
||||
public R<String> update(@RequestBody UserEntity user)
|
||||
{
|
||||
@ -96,8 +88,8 @@ public class TestController extends BaseController
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@ApiOperation("删除用户信息")
|
||||
@ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
|
||||
@Operation(summary = "删除用户信息")
|
||||
@Parameter(name = "userId", description = "用户ID", required = true)
|
||||
@DeleteMapping("/{userId}")
|
||||
public R<String> delete(@PathVariable Integer userId)
|
||||
{
|
||||
@ -113,19 +105,19 @@ public class TestController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
@ApiModel(value = "UserEntity", description = "用户实体")
|
||||
@Schema(name = "UserEntity", description = "用户实体")
|
||||
class UserEntity
|
||||
{
|
||||
@ApiModelProperty("用户ID")
|
||||
@Schema(description = "用户ID")
|
||||
private Integer userId;
|
||||
|
||||
@ApiModelProperty("用户名称")
|
||||
@Schema(description = "用户名称")
|
||||
private String username;
|
||||
|
||||
@ApiModelProperty("用户密码")
|
||||
@Schema(description = "用户密码")
|
||||
private String password;
|
||||
|
||||
@ApiModelProperty("用户手机")
|
||||
@Schema(description = "用户手机")
|
||||
private String mobile;
|
||||
|
||||
public UserEntity()
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
package com.qaup.web.core.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import com.qaup.common.config.QaupConfig;
|
||||
|
||||
/**
|
||||
* SpringDoc OpenAPI 配置
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("dev")
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Autowired
|
||||
private QaupConfig qaupConfig;
|
||||
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(apiInfo())
|
||||
.addSecurityItem(new SecurityRequirement().addList("Authorization"))
|
||||
.components(new io.swagger.v3.oas.models.Components()
|
||||
.addSecuritySchemes("Authorization", new SecurityScheme()
|
||||
.name("Authorization")
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")))
|
||||
.addServersItem(new Server().url("/").description("Direct Backend Access"))
|
||||
.addServersItem(new Server().url("/dev-api").description("Frontend Proxy Access"));
|
||||
}
|
||||
|
||||
private Info apiInfo() {
|
||||
return new Info()
|
||||
.title("后台管理系统接口文档")
|
||||
.description("用于后台管理系统,包括用户、角色、车辆信息等模块的接口说明。")
|
||||
.contact(new Contact()
|
||||
.name(qaupConfig.getName())
|
||||
.url(null)
|
||||
.email(null))
|
||||
.version(qaupConfig.getVersion());
|
||||
}
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
package com.qaup.web.core.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.qaup.common.config.QaupConfig;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.models.auth.In;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ApiKey;
|
||||
import springfox.documentation.service.AuthorizationScope;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.SecurityReference;
|
||||
import springfox.documentation.service.SecurityScheme;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
/**
|
||||
* Swagger2的接口配置
|
||||
*
|
||||
* @author qaup
|
||||
*/
|
||||
@Configuration
|
||||
public class SwaggerConfig
|
||||
{
|
||||
/** 系统基础配置 */
|
||||
@Autowired
|
||||
private QaupConfig qaupConfig;
|
||||
|
||||
/** 是否开启swagger */
|
||||
@Value("${swagger.enabled}")
|
||||
private boolean enabled;
|
||||
|
||||
/** 设置请求的统一前缀 */
|
||||
@Value("${swagger.pathMapping}")
|
||||
private String pathMapping;
|
||||
|
||||
/**
|
||||
* 创建API
|
||||
*/
|
||||
@Bean
|
||||
public Docket createRestApi()
|
||||
{
|
||||
return new Docket(DocumentationType.OAS_30)
|
||||
// 是否启用Swagger
|
||||
.enable(enabled)
|
||||
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
|
||||
.apiInfo(apiInfo())
|
||||
// 设置哪些接口暴露给Swagger展示
|
||||
.select()
|
||||
// 扫描所有有注解的api,用这种方式更灵活
|
||||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
// 扫描指定包中的swagger注解
|
||||
// .apis(RequestHandlerSelectors.basePackage("com.qaup.project.tool.swagger"))
|
||||
// 扫描所有 .apis(RequestHandlerSelectors.any())
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
/* 设置安全模式,swagger可以设置访问token */
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts())
|
||||
.pathMapping(pathMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全模式,这里指定token通过Authorization头请求头传递
|
||||
*/
|
||||
private List<SecurityScheme> securitySchemes()
|
||||
{
|
||||
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
|
||||
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
|
||||
return apiKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全上下文
|
||||
*/
|
||||
private List<SecurityContext> securityContexts()
|
||||
{
|
||||
List<SecurityContext> securityContexts = new ArrayList<>();
|
||||
securityContexts.add(
|
||||
SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
|
||||
.build());
|
||||
return securityContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的安全上引用
|
||||
*/
|
||||
private List<SecurityReference> defaultAuth()
|
||||
{
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
List<SecurityReference> securityReferences = new ArrayList<>();
|
||||
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
|
||||
return securityReferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加摘要信息
|
||||
*/
|
||||
private ApiInfo apiInfo()
|
||||
{
|
||||
// 用ApiInfoBuilder进行定制
|
||||
return new ApiInfoBuilder()
|
||||
// 设置标题
|
||||
.title("标题:后台管理系统_接口文档")
|
||||
// 描述
|
||||
.description("描述:用于后台管理系统,具体包括XXX,XXX模块...")
|
||||
// 作者信息
|
||||
.contact(new Contact(qaupConfig.getName(), null, null))
|
||||
// 版本
|
||||
.version("版本号:" + qaupConfig.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -1,243 +1,245 @@
|
||||
# 项目相关配置
|
||||
qaup:
|
||||
# 名称
|
||||
name: Qaup
|
||||
# 版本
|
||||
version: 3.8.9
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/qaup/uploadPath,Linux配置 /home/qaup/uploadPath)
|
||||
profile: Users/tianjianyong/apps/OpenSource/Qaup-Vue-Postgresql/uploadPath
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
# 验证码类型 math 数字计算 char 字符验证
|
||||
captchaType: math
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.qaup: debug
|
||||
org.springframework: warn
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
# 密码锁定时间(默认10分钟)
|
||||
lockTime: 10
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
basename: i18n/messages
|
||||
profiles:
|
||||
active: druid
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# 重启目录
|
||||
additional-paths: src/main/java
|
||||
# 排除目录
|
||||
exclude: WEB-INF/**
|
||||
jackson:
|
||||
# 日期格式化
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
serialization:
|
||||
# 格式化输出
|
||||
indent_output: false
|
||||
# 忽略无法转换的对象
|
||||
fail_on_empty_beans: false
|
||||
deserialization:
|
||||
# 允许对象忽略json中不存在的属性
|
||||
fail_on_unknown_properties: false
|
||||
# redis 配置
|
||||
data:
|
||||
redis:
|
||||
# 地址
|
||||
host: localhost
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
|
||||
# ==================== CollisionAvoidanceSystem 配置整合 ====================
|
||||
|
||||
# Bean覆盖配置(支持collision模块的适配器)
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
# JPA配置(collision模块空间数据处理)
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none # 使用若依的数据库管理方式
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
jdbc:
|
||||
lob:
|
||||
non_contextual_creation: true
|
||||
batch_size: 50
|
||||
fetch_size: 50
|
||||
cache:
|
||||
use_second_level_cache: false
|
||||
use_query_cache: false
|
||||
order_inserts: true
|
||||
order_updates: true
|
||||
batch_versioned_data: true
|
||||
generate_statistics: false
|
||||
|
||||
# token配置
|
||||
token:
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.qaup.system.domain,com.qaup.common.core.domain.entity,com.qaup.generator.domain,com.qaup.quartz.domain,com.qaup.collision.common.model.spatial,com.qaup.collision.datacollector.model.dto,com.qaup.collision.geofence.model.entity
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
|
||||
# PageHelper分页插件
|
||||
pagehelper:
|
||||
helperDialect: postgresql
|
||||
reasonable: true
|
||||
supportMethodsArguments: true
|
||||
params: count=countSql
|
||||
|
||||
# Swagger配置
|
||||
swagger:
|
||||
# 是否开启swagger
|
||||
enabled: true
|
||||
# 请求前缀
|
||||
pathMapping: /dev-api
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
# 过滤开关
|
||||
enabled: true
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludes: /system/notice
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
# 数据采集配置(collision模块)
|
||||
data:
|
||||
collector:
|
||||
# 数据采集间隔,单位:毫秒(优化:250ms超高频采集,进一步减少停顿)
|
||||
interval: 250
|
||||
# WebSocket推送节流配置(1000ms推送间隔,避免前端过载)
|
||||
websocket:
|
||||
push-interval: 1000
|
||||
# 机场数据源配置
|
||||
airport-api:
|
||||
base-url: http://localhost:8090
|
||||
endpoints:
|
||||
login: /login
|
||||
aircraft: /openApi/getCurrentFlightPositions
|
||||
vehicle: /openApi/getCurrentVehiclePositions
|
||||
refresh: /refresh
|
||||
auth:
|
||||
username: dianxin
|
||||
password: dianxin@123
|
||||
# 无人车厂商数据源配置
|
||||
vehicle-api:
|
||||
base-url: http://localhost:8090
|
||||
endpoints:
|
||||
vehicle-location: /api/VehicleLocationInfo
|
||||
vehicle-state: /api/VehicleStateInfo
|
||||
vehicle-command: /api/VehicleCommandInfo
|
||||
timeout: 5000
|
||||
retry-attempts: 3
|
||||
# 无人车数据持久化配置
|
||||
unmanned-vehicle:
|
||||
persistence:
|
||||
enabled: true
|
||||
batch-size: 50
|
||||
location-retention-days: 90
|
||||
command-retention-days: 365
|
||||
command:
|
||||
timeout: 5000
|
||||
retry-attempts: 3
|
||||
validation:
|
||||
enabled: true
|
||||
strict-mode: false
|
||||
retention:
|
||||
redis-expire-seconds: 60
|
||||
postgresql-days: 30
|
||||
|
||||
# 坐标系统配置(collision模块)
|
||||
coordinate-system:
|
||||
airport:
|
||||
center-longitude: 120.0834104
|
||||
center-latitude: 36.35406879
|
||||
|
||||
# 性能监控配置(collision模块扩展)
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
metrics:
|
||||
export:
|
||||
simple:
|
||||
enabled: true
|
||||
enable:
|
||||
hikari: true
|
||||
jvm: true
|
||||
jmx:
|
||||
enabled: true
|
||||
# 项目相关配置
|
||||
qaup:
|
||||
# 名称
|
||||
name: Qaup
|
||||
# 版本
|
||||
version: 3.8.9
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/qaup/uploadPath,Linux配置 /home/qaup/uploadPath)
|
||||
profile: Users/tianjianyong/apps/OpenSource/Qaup-Vue-Postgresql/uploadPath
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
# 验证码类型 math 数字计算 char 字符验证
|
||||
captchaType: math
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.qaup: debug
|
||||
org.springframework: warn
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
# 密码锁定时间(默认10分钟)
|
||||
lockTime: 10
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
basename: i18n/messages
|
||||
profiles:
|
||||
active: dev,druid
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# 重启目录
|
||||
additional-paths: src/main/java
|
||||
# 排除目录
|
||||
exclude: WEB-INF/**
|
||||
jackson:
|
||||
# 日期格式化
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
serialization:
|
||||
# 格式化输出
|
||||
indent_output: false
|
||||
# 忽略无法转换的对象
|
||||
fail_on_empty_beans: false
|
||||
deserialization:
|
||||
# 允许对象忽略json中不存在的属性
|
||||
fail_on_unknown_properties: false
|
||||
# redis 配置
|
||||
data:
|
||||
redis:
|
||||
# 地址
|
||||
host: localhost
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password:
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
|
||||
# ==================== CollisionAvoidanceSystem 配置整合 ====================
|
||||
|
||||
# Bean覆盖配置(支持collision模块的适配器)
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
# JPA配置(collision模块空间数据处理)
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none # 使用若依的数据库管理方式
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
jdbc:
|
||||
lob:
|
||||
non_contextual_creation: true
|
||||
batch_size: 50
|
||||
fetch_size: 50
|
||||
cache:
|
||||
use_second_level_cache: false
|
||||
use_query_cache: false
|
||||
order_inserts: true
|
||||
order_updates: true
|
||||
batch_versioned_data: true
|
||||
generate_statistics: false
|
||||
|
||||
# token配置
|
||||
token:
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.qaup.system.domain,com.qaup.common.core.domain.entity,com.qaup.generator.domain,com.qaup.quartz.domain,com.qaup.collision.common.model.spatial,com.qaup.collision.datacollector.model.dto,com.qaup.collision.geofence.model.entity
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
|
||||
# PageHelper分页插件
|
||||
pagehelper:
|
||||
helperDialect: postgresql
|
||||
reasonable: true
|
||||
supportMethodsArguments: true
|
||||
params: count=countSql
|
||||
|
||||
# SpringDoc 配置
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html # Swagger UI 访问路径
|
||||
url: /v3/api-docs # OpenAPI JSON 文档路径
|
||||
disable-swagger-default-url: true
|
||||
api-docs:
|
||||
path: /v3/api-docs # OpenAPI JSON 文档路径
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
# 过滤开关
|
||||
enabled: true
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludes: /system/notice
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
# 数据采集配置(collision模块)
|
||||
data:
|
||||
collector:
|
||||
# 数据采集间隔,单位:毫秒(优化:250ms超高频采集,进一步减少停顿)
|
||||
interval: 250
|
||||
# WebSocket推送节流配置(1000ms推送间隔,避免前端过载)
|
||||
websocket:
|
||||
push-interval: 1000
|
||||
# 机场数据源配置
|
||||
airport-api:
|
||||
base-url: http://localhost:8090
|
||||
endpoints:
|
||||
login: /login
|
||||
aircraft: /openApi/getCurrentFlightPositions
|
||||
vehicle: /openApi/getCurrentVehiclePositions
|
||||
refresh: /refresh
|
||||
auth:
|
||||
username: dianxin
|
||||
password: dianxin@123
|
||||
# 无人车厂商数据源配置
|
||||
vehicle-api:
|
||||
base-url: http://localhost:8090
|
||||
endpoints:
|
||||
vehicle-location: /api/VehicleLocationInfo
|
||||
vehicle-state: /api/VehicleStateInfo
|
||||
vehicle-command: /api/VehicleCommandInfo
|
||||
timeout: 5000
|
||||
retry-attempts: 3
|
||||
# 无人车数据持久化配置
|
||||
unmanned-vehicle:
|
||||
persistence:
|
||||
enabled: true
|
||||
batch-size: 50
|
||||
location-retention-days: 90
|
||||
command-retention-days: 365
|
||||
command:
|
||||
timeout: 5000
|
||||
retry-attempts: 3
|
||||
validation:
|
||||
enabled: true
|
||||
strict-mode: false
|
||||
retention:
|
||||
redis-expire-seconds: 60
|
||||
postgresql-days: 30
|
||||
|
||||
# 坐标系统配置(collision模块)
|
||||
coordinate-system:
|
||||
airport:
|
||||
center-longitude: 120.0834104
|
||||
center-latitude: 36.35406879
|
||||
|
||||
# 性能监控配置(collision模块扩展)
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
metrics:
|
||||
export:
|
||||
simple:
|
||||
enabled: true
|
||||
enable:
|
||||
hikari: true
|
||||
jvm: true
|
||||
jmx:
|
||||
enabled: true
|
||||
|
||||
@ -147,6 +147,7 @@ public class QuapDataAdapter {
|
||||
/**
|
||||
* 将若依车辆信息转换为CollisionAvoidanceSystem需要的VehicleLocation格式
|
||||
* 注意:此方法只转换基础信息,位置和时间信息需要从实时数据中获取
|
||||
* 注意:车牌号和车辆类型现在不存储在VehicleLocation中,需要通过vehicleId关联查询
|
||||
*
|
||||
* @param sysVehicleInfo 若依车辆信息
|
||||
* @param location 实时位置
|
||||
@ -162,13 +163,9 @@ public class QuapDataAdapter {
|
||||
|
||||
VehicleLocation vehicleLocation = new VehicleLocation();
|
||||
vehicleLocation.setVehicleId(sysVehicleInfo.getVehicleId());
|
||||
vehicleLocation.setLicensePlate(sysVehicleInfo.getLicensePlate());
|
||||
vehicleLocation.setLocation(location);
|
||||
vehicleLocation.setTimestamp(timestamp);
|
||||
|
||||
// 根据类型ID设置车辆类型
|
||||
vehicleLocation.setVehicleType(convertToMovingObjectType(sysVehicleInfo.getTypeId()));
|
||||
|
||||
return vehicleLocation;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.qaup.collision.common.model.repository;
|
||||
|
||||
import com.qaup.collision.common.model.spatial.VehicleLocation;
|
||||
import com.qaup.collision.common.model.MovingObjectType;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
@ -28,19 +27,27 @@ public interface VehicleLocationRepository extends JpaRepository<VehicleLocation
|
||||
Optional<VehicleLocation> findLatestByVehicleId(@Param("vehicleId") Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据车牌号查找最新位置记录
|
||||
* 根据车牌号查找最新位置记录(通过关联查询)
|
||||
*/
|
||||
@Query(value = "SELECT * FROM vehicle_locations vl WHERE vl.license_plate = :licensePlate " +
|
||||
@Query(value = "SELECT vl.* FROM vehicle_locations vl " +
|
||||
"JOIN sys_vehicle_info vi ON vl.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"ORDER BY vl.timestamp DESC LIMIT 1",
|
||||
nativeQuery = true)
|
||||
Optional<VehicleLocation> findLatestByLicensePlate(@Param("licensePlate") String licensePlate);
|
||||
|
||||
/**
|
||||
* 根据车辆类型查找活跃车辆
|
||||
* 根据车辆类型查找活跃车辆(通过关联查询)
|
||||
* 注意:由于车辆类型现在在sys_vehicle_info表中,需要通过关联查询
|
||||
*/
|
||||
@Query("SELECT vl FROM VehicleLocation vl WHERE vl.vehicleType = :vehicleType " +
|
||||
"AND vl.timestamp >= :since ORDER BY vl.timestamp DESC")
|
||||
List<VehicleLocation> findActiveByVehicleType(@Param("vehicleType") MovingObjectType vehicleType,
|
||||
@Query(value = "SELECT vl.* FROM vehicle_locations vl " +
|
||||
"JOIN sys_vehicle_info vi ON vl.vehicle_id = vi.vehicle_id " +
|
||||
"JOIN sys_vehicle_type vt ON vi.type_id = vt.type_id " +
|
||||
"WHERE vt.type_name LIKE :typeName " +
|
||||
"AND vl.timestamp >= :since " +
|
||||
"ORDER BY vl.timestamp DESC",
|
||||
nativeQuery = true)
|
||||
List<VehicleLocation> findActiveByVehicleType(@Param("typeName") String typeName,
|
||||
@Param("since") LocalDateTime since);
|
||||
|
||||
/**
|
||||
@ -79,11 +86,14 @@ public interface VehicleLocationRepository extends JpaRepository<VehicleLocation
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 根据车牌号和时间范围查询轨迹数据
|
||||
* 根据车牌号和时间范围查询轨迹数据(通过关联查询)
|
||||
*/
|
||||
@Query("SELECT vl FROM VehicleLocation vl WHERE vl.licensePlate = :licensePlate " +
|
||||
"AND vl.timestamp BETWEEN :startTime AND :endTime " +
|
||||
"ORDER BY vl.timestamp")
|
||||
@Query(value = "SELECT vl.* FROM vehicle_locations vl " +
|
||||
"JOIN sys_vehicle_info vi ON vl.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"AND vl.timestamp BETWEEN :startTime AND :endTime " +
|
||||
"ORDER BY vl.timestamp",
|
||||
nativeQuery = true)
|
||||
List<VehicleLocation> findVehicleTrajectoryByLicensePlate(@Param("licensePlate") String licensePlate,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.qaup.collision.common.model.spatial;
|
||||
|
||||
import com.qaup.collision.common.model.MovingObjectType;
|
||||
import com.qaup.collision.common.model.MovementState;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -12,10 +11,10 @@ import org.locationtech.jts.geom.Point;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 车辆位置PostGIS实体类
|
||||
* 车辆位置PostGIS实体类(规范化版本)
|
||||
*
|
||||
* 用于存储车辆的实时位置信息,替代原有的内存存储模式
|
||||
* 支持PostGIS空间查询和索引优化
|
||||
* 用于存储车辆的实时位置信息,符合数据库规范化设计
|
||||
* 只存储位置相关数据,车辆基础信息通过关联查询获取
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "vehicle_locations")
|
||||
@ -35,19 +34,6 @@ public class VehicleLocation {
|
||||
@Column(name = "vehicle_id", nullable = false)
|
||||
private Long vehicleId;
|
||||
|
||||
/**
|
||||
* 车牌号(业务标识符)
|
||||
*/
|
||||
@Column(name = "license_plate", nullable = false, length = 50)
|
||||
private String licensePlate;
|
||||
|
||||
/**
|
||||
* 车辆类型枚举
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "vehicle_type", nullable = false, length = 20)
|
||||
private MovingObjectType vehicleType;
|
||||
|
||||
/**
|
||||
* 位置点 - 使用PostGIS POINT类型
|
||||
* SRID 4326表示WGS84坐标系统(GPS坐标)
|
||||
@ -80,11 +66,10 @@ public class VehicleLocation {
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
/**
|
||||
* 数据质量枚举
|
||||
* 数据质量标识
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "data_quality", length = 20)
|
||||
private MovementState.DataQuality dataQuality;
|
||||
private String dataQuality;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
@ -98,6 +83,24 @@ public class VehicleLocation {
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// ============================================
|
||||
// 运行时属性 - 不存储在数据库中
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 车辆类型(运行时属性,不存储在数据库中)
|
||||
* 通过QuapDataAdapter从sys_vehicle_info表获取
|
||||
*/
|
||||
@Transient
|
||||
private MovingObjectType vehicleType;
|
||||
|
||||
/**
|
||||
* 车牌号(运行时属性,不存储在数据库中)
|
||||
* 通过QuapDataAdapter从sys_vehicle_info表获取
|
||||
*/
|
||||
@Transient
|
||||
private String licensePlate;
|
||||
|
||||
/**
|
||||
* 预设置创建和更新时间
|
||||
*/
|
||||
@ -111,4 +114,51 @@ public class VehicleLocation {
|
||||
protected void onUpdate() {
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 便捷方法
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 设置车辆类型(用于运行时设置)
|
||||
*
|
||||
* @param vehicleType 车辆类型
|
||||
*/
|
||||
public void setVehicleType(MovingObjectType vehicleType) {
|
||||
this.vehicleType = vehicleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆类型
|
||||
* 如果未设置运行时类型,默认返回UNMANNED_VEHICLE(因为只有无人车数据会持久化)
|
||||
*
|
||||
* @return 车辆类型
|
||||
*/
|
||||
public MovingObjectType getVehicleType() {
|
||||
// 如果已设置运行时类型,返回该类型
|
||||
if (this.vehicleType != null) {
|
||||
return this.vehicleType;
|
||||
}
|
||||
// 由于数据库中只存储无人车数据,默认返回UNMANNED_VEHICLE
|
||||
return MovingObjectType.UNMANNED_VEHICLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置车牌号(用于运行时设置)
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
*/
|
||||
public void setLicensePlate(String licensePlate) {
|
||||
this.licensePlate = licensePlate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车牌号
|
||||
* 需要通过QuapDataAdapter从sys_vehicle_info表获取
|
||||
*
|
||||
* @return 车牌号(可能为null,需要通过适配器获取)
|
||||
*/
|
||||
public String getLicensePlate() {
|
||||
return this.licensePlate;
|
||||
}
|
||||
}
|
||||
@ -184,49 +184,54 @@ public class VehicleLocationService {
|
||||
/**
|
||||
* 创建车辆位置记录(集成QAUP数据适配器)
|
||||
*/
|
||||
public VehicleLocation createVehicleLocation(String licensePlate, MovingObjectType vehicleType,
|
||||
public VehicleLocation createVehicleLocation(String licensePlate, Long vehicleId,
|
||||
double longitude, double latitude,
|
||||
Double altitude, Double heading, Double speed) {
|
||||
Point location = geometryFactory.createPoint(new Coordinate(longitude, latitude));
|
||||
location.setSRID(4326); // 设置WGS84坐标系
|
||||
|
||||
VehicleLocation vehicleLocation = new VehicleLocation();
|
||||
vehicleLocation.setLicensePlate(licensePlate);
|
||||
vehicleLocation.setVehicleId(vehicleId); // 使用传入的数字车辆ID
|
||||
vehicleLocation.setLocation(location);
|
||||
vehicleLocation.setAltitude(altitude);
|
||||
vehicleLocation.setHeading(heading);
|
||||
vehicleLocation.setSpeed(speed);
|
||||
vehicleLocation.setTimestamp(LocalDateTime.now());
|
||||
vehicleLocation.setDataQuality("GOOD"); // 默认数据质量
|
||||
|
||||
// 设置运行时属性
|
||||
vehicleLocation.setLicensePlate(licensePlate);
|
||||
vehicleLocation.setVehicleType(MovingObjectType.UNMANNED_VEHICLE); // 只有无人车数据会持久化
|
||||
|
||||
log.debug("创建车辆位置记录: vehicleId={}, licensePlate={}, location=({}, {})",
|
||||
vehicleId, licensePlate, longitude, latitude);
|
||||
|
||||
return vehicleLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建车辆位置记录(通过车牌号自动查找车辆ID)
|
||||
*/
|
||||
public VehicleLocation createVehicleLocationByLicensePlate(String licensePlate,
|
||||
double longitude, double latitude,
|
||||
Double altitude, Double heading, Double speed) {
|
||||
// 使用QuapDataAdapter获取车辆基础信息
|
||||
try {
|
||||
Optional<SysVehicleInfo> vehicleInfoOpt = quapDataAdapter.findVehicleByLicensePlate(licensePlate);
|
||||
if (vehicleInfoOpt.isPresent()) {
|
||||
SysVehicleInfo vehicleInfo = vehicleInfoOpt.get();
|
||||
// 设置vehicleId(数字主键)
|
||||
vehicleLocation.setVehicleId(vehicleInfo.getVehicleId());
|
||||
|
||||
// 如果没有明确指定车辆类型,从系统自动获取
|
||||
if (vehicleType == null) {
|
||||
vehicleLocation.setVehicleType(quapDataAdapter.convertToMovingObjectType(vehicleInfo.getTypeId()));
|
||||
} else {
|
||||
vehicleLocation.setVehicleType(vehicleType);
|
||||
}
|
||||
|
||||
log.debug("通过QAUP适配器获取车辆信息: vehicleId={}, licensePlate={}, type={}",
|
||||
vehicleInfo.getVehicleId(), licensePlate, vehicleLocation.getVehicleType());
|
||||
return createVehicleLocation(licensePlate, vehicleInfo.getVehicleId(),
|
||||
longitude, latitude, altitude, heading, speed);
|
||||
} else {
|
||||
// 车辆信息不存在,记录警告但不阻止位置记录创建
|
||||
vehicleLocation.setVehicleType(vehicleType != null ? vehicleType : MovingObjectType.UNKNOWN);
|
||||
log.warn("未找到车辆基础信息: licensePlate={}, 使用类型: {}", licensePlate, vehicleLocation.getVehicleType());
|
||||
log.warn("未找到车辆基础信息: licensePlate={}", licensePlate);
|
||||
// 返回null或者抛出异常,根据业务需求决定
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 适配器查询失败,使用传入的车辆类型,不阻止位置记录创建
|
||||
vehicleLocation.setVehicleType(vehicleType != null ? vehicleType : MovingObjectType.UNKNOWN);
|
||||
log.error("查询车辆基础信息失败: licensePlate={}, 错误: {}", licensePlate, e.getMessage());
|
||||
log.error("通过车牌号查找车辆信息失败: licensePlate={}", licensePlate, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return vehicleLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -313,12 +318,30 @@ public class VehicleLocationService {
|
||||
public List<VehicleLocation> getActiveVehiclesByType(MovingObjectType vehicleType, int minutesBack) {
|
||||
LocalDateTime since = LocalDateTime.now().minusMinutes(minutesBack);
|
||||
try {
|
||||
return vehicleLocationRepository.findActiveByVehicleType(vehicleType, since);
|
||||
// 将MovingObjectType转换为数据库中的类型名称模式
|
||||
String typeName = convertMovingObjectTypeToTypeName(vehicleType);
|
||||
return vehicleLocationRepository.findActiveByVehicleType(typeName, since);
|
||||
} catch (Exception e) {
|
||||
log.error("获取活跃车辆失败: vehicleType={}, minutesBack={}", vehicleType, minutesBack, e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将MovingObjectType转换为数据库中的类型名称模式
|
||||
*/
|
||||
private String convertMovingObjectTypeToTypeName(MovingObjectType vehicleType) {
|
||||
switch (vehicleType) {
|
||||
case UNMANNED_VEHICLE:
|
||||
return "%无人车%";
|
||||
case AIRPORT_VEHICLE:
|
||||
return "%车"; // 匹配包含"车"但不包含"无人车"的类型
|
||||
case AIRCRAFT:
|
||||
return "%飞机%";
|
||||
default:
|
||||
return "%"; // 匹配所有类型
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空间查询:获取指定半径范围内的车辆
|
||||
@ -447,12 +470,16 @@ public class VehicleLocationService {
|
||||
* 更新车辆位置 (如果存在则更新,否则创建新记录)(集成规则检测)
|
||||
*/
|
||||
@Transactional
|
||||
public VehicleLocation updateOrCreateVehicleLocation(String licensePlate, MovingObjectType vehicleType,
|
||||
public VehicleLocation updateOrCreateVehicleLocation(String licensePlate,
|
||||
double longitude, double latitude,
|
||||
Double altitude, Double heading, Double speed) {
|
||||
VehicleLocation vehicleLocation = createVehicleLocation(licensePlate, vehicleType,
|
||||
VehicleLocation vehicleLocation = createVehicleLocationByLicensePlate(licensePlate,
|
||||
longitude, latitude,
|
||||
altitude, heading, speed);
|
||||
if (vehicleLocation == null) {
|
||||
log.warn("无法创建车辆位置记录,车牌号不存在: {}", licensePlate);
|
||||
return null;
|
||||
}
|
||||
vehicleLocation.setTimestamp(LocalDateTime.now());
|
||||
|
||||
VehicleLocation saved = saveVehicleLocation(vehicleLocation);
|
||||
@ -535,8 +562,8 @@ public class VehicleLocationService {
|
||||
|
||||
// 详细记录每个违规事件
|
||||
for (RuleViolationEvent violation : violations) {
|
||||
log.warn("违规详情: 车牌={}, 规则={}, 违规类型={}, 描述={}",
|
||||
violation.getVehicleLicense(), violation.getRuleName(),
|
||||
log.warn("违规详情: vehicleId={}, 规则={}, 违规类型={}, 描述={}",
|
||||
violation.getVehicleId(), violation.getRuleName(),
|
||||
violation.getViolationType(), violation.getDescription());
|
||||
}
|
||||
} else {
|
||||
@ -585,14 +612,14 @@ public class VehicleLocationService {
|
||||
log.warn("智能检测发现违规: 总违规数量={}", allViolations.size());
|
||||
|
||||
// 按车辆分组显示违规统计
|
||||
Map<String, Long> violationsByVehicle = allViolations.stream()
|
||||
Map<Long, Long> violationsByVehicle = allViolations.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(
|
||||
RuleViolationEvent::getVehicleLicense,
|
||||
RuleViolationEvent::getVehicleId,
|
||||
java.util.stream.Collectors.counting()
|
||||
));
|
||||
|
||||
violationsByVehicle.forEach((licensePlate, count) ->
|
||||
log.warn("车辆违规统计: licensePlate={}, 违规数量={}", licensePlate, count)
|
||||
violationsByVehicle.forEach((vehicleId, count) ->
|
||||
log.warn("车辆违规统计: vehicleId={}, 违规数量={}", vehicleId, count)
|
||||
);
|
||||
} else {
|
||||
log.info("智能检测未发现违规");
|
||||
@ -649,10 +676,8 @@ public class VehicleLocationService {
|
||||
* 获取车辆唯一标识
|
||||
*/
|
||||
private String getVehicleKey(VehicleLocation vehicleLocation) {
|
||||
// 使用车牌号作为唯一标识,更稳定
|
||||
return vehicleLocation.getLicensePlate() != null ?
|
||||
vehicleLocation.getLicensePlate() :
|
||||
String.valueOf(vehicleLocation.getVehicleId());
|
||||
// 使用车辆ID作为唯一标识
|
||||
return String.valueOf(vehicleLocation.getVehicleId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -730,10 +755,20 @@ public class VehicleLocationService {
|
||||
*/
|
||||
private void triggerRuleDetectionForSingle(VehicleLocation vehicleLocation) {
|
||||
try {
|
||||
// 获取车辆信息以确定车辆类型
|
||||
Optional<SysVehicleInfo> vehicleInfoOpt = quapDataAdapter.findVehicleById(vehicleLocation.getVehicleId());
|
||||
if (vehicleInfoOpt.isEmpty()) {
|
||||
log.warn("无法获取车辆信息,跳过规则检测: vehicleId={}", vehicleLocation.getVehicleId());
|
||||
return;
|
||||
}
|
||||
|
||||
SysVehicleInfo vehicleInfo = vehicleInfoOpt.get();
|
||||
MovingObjectType vehicleType = quapDataAdapter.convertToMovingObjectType(vehicleInfo.getTypeId());
|
||||
|
||||
// 查询该位置适用的规则
|
||||
List<SpatialRule> applicableRules = locationRuleQueryService.findApplicableRules(
|
||||
vehicleLocation.getLocation(),
|
||||
vehicleLocation.getVehicleType(),
|
||||
vehicleType,
|
||||
LocalDateTime.now()
|
||||
);
|
||||
|
||||
@ -745,13 +780,13 @@ public class VehicleLocationService {
|
||||
for (SpatialRule rule : applicableRules) {
|
||||
try {
|
||||
// 构建车辆状态参数
|
||||
Map<String, Object> vehicleState = buildVehicleStateMap(vehicleLocation);
|
||||
Map<String, Object> vehicleState = buildVehicleStateMap(vehicleLocation, vehicleInfo, vehicleType);
|
||||
|
||||
// 修复executeRule参数类型:使用licensePlate作为vehicleIdentifier
|
||||
// 使用licensePlate作为vehicleIdentifier
|
||||
RuleExecutionResult result = ruleExecutionEngine.executeRule(
|
||||
rule,
|
||||
vehicleLocation.getLicensePlate(),
|
||||
vehicleLocation.getVehicleType(),
|
||||
vehicleInfo.getLicensePlate(),
|
||||
vehicleType,
|
||||
vehicleLocation.getLocation(),
|
||||
vehicleState,
|
||||
vehicleLocation.getTimestamp()
|
||||
@ -759,18 +794,17 @@ public class VehicleLocationService {
|
||||
|
||||
if (result.requiresAction()) {
|
||||
log.info("检测发现问题: vehicleId={}, licensePlate={}, ruleId={}, result={}",
|
||||
vehicleLocation.getVehicleId(), vehicleLocation.getLicensePlate(),
|
||||
vehicleLocation.getVehicleId(), vehicleInfo.getLicensePlate(),
|
||||
rule.getRuleId(), result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("规则执行失败: vehicleId={}, licensePlate={}, ruleId={}",
|
||||
vehicleLocation.getVehicleId(), vehicleLocation.getLicensePlate(), rule.getRuleId(), e);
|
||||
vehicleLocation.getVehicleId(), vehicleInfo.getLicensePlate(), rule.getRuleId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("单个车辆规则检测失败: vehicleId={}, licensePlate={}",
|
||||
vehicleLocation.getVehicleId(), vehicleLocation.getLicensePlate(), e);
|
||||
log.error("单个车辆规则检测失败: vehicleId={}", vehicleLocation.getVehicleId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -814,12 +848,13 @@ public class VehicleLocationService {
|
||||
/**
|
||||
* 构建车辆状态参数Map(用于规则执行)
|
||||
*/
|
||||
private Map<String, Object> buildVehicleStateMap(VehicleLocation vehicleLocation) {
|
||||
private Map<String, Object> buildVehicleStateMap(VehicleLocation vehicleLocation, SysVehicleInfo vehicleInfo, MovingObjectType vehicleType) {
|
||||
Map<String, Object> vehicleState = new HashMap<>();
|
||||
|
||||
// 基本位置信息
|
||||
vehicleState.put("vehicleId", vehicleLocation.getVehicleId());
|
||||
vehicleState.put("vehicleType", vehicleLocation.getVehicleType());
|
||||
vehicleState.put("vehicleType", vehicleType);
|
||||
vehicleState.put("licensePlate", vehicleInfo.getLicensePlate());
|
||||
vehicleState.put("timestamp", vehicleLocation.getTimestamp());
|
||||
|
||||
// 位置坐标
|
||||
|
||||
@ -389,8 +389,8 @@ public class DataCollectorService {
|
||||
}
|
||||
|
||||
// 使用VehicleLocationService创建VehicleLocation对象
|
||||
return vehicleLocationService.createVehicleLocation(
|
||||
licensePlate, vehicleType, longitude, latitude,
|
||||
return vehicleLocationService.createVehicleLocationByLicensePlate(
|
||||
licensePlate, longitude, latitude,
|
||||
altitude, heading, speed
|
||||
);
|
||||
|
||||
|
||||
@ -143,7 +143,7 @@ public class UnmannedVehicleControlService {
|
||||
// 由于数据库中只存储无人车数据,直接查询即可
|
||||
LocalDateTime since = LocalDateTime.now().minusMinutes(5);
|
||||
locations = vehicleLocationRepository.findActiveByVehicleType(
|
||||
MovingObjectType.UNMANNED_VEHICLE, since);
|
||||
"%无人车%", since);
|
||||
logger.info("查询到所有无人车位置: count={}", locations.size());
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ public class ThreatLevelEventService {
|
||||
emergencyEventCount.incrementAndGet();
|
||||
break;
|
||||
default:
|
||||
logger.warn("未知的威胁级别: {}, 车牌: {}", threatLevel, violationEvent.getVehicleLicense());
|
||||
logger.warn("未知的威胁级别: {}, 车辆ID: {}", threatLevel, violationEvent.getVehicleId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ public class ThreatLevelEventService {
|
||||
|
||||
private void handleInfoLevelEvent(RuleViolationEvent violationEvent) {
|
||||
logger.info("INFO级违规事件: vehicleId={}, ruleId={}, description={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(),
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(),
|
||||
violationEvent.getDescription());
|
||||
|
||||
// 信息级事件只记录日志,通过WebSocket推送到前端
|
||||
@ -128,7 +128,7 @@ public class ThreatLevelEventService {
|
||||
|
||||
private void handleWarningLevelEvent(RuleViolationEvent violationEvent) {
|
||||
logger.warn("WARNING级违规事件: vehicleId={}, ruleId={}, description={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(),
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(),
|
||||
violationEvent.getDescription());
|
||||
|
||||
// 警告级事件记录详细日志,并通过WebSocket推送
|
||||
@ -143,7 +143,7 @@ public class ThreatLevelEventService {
|
||||
|
||||
private void handleCriticalLevelEvent(RuleViolationEvent violationEvent) {
|
||||
logger.error("CRITICAL级违规事件: vehicleId={}, ruleId={}, severity={}, description={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(),
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(),
|
||||
violationEvent.getViolationType(), violationEvent.getDescription());
|
||||
|
||||
// 严重级事件需要特别关注
|
||||
@ -162,7 +162,7 @@ public class ThreatLevelEventService {
|
||||
|
||||
private void handleEmergencyLevelEvent(RuleViolationEvent violationEvent) {
|
||||
logger.error("EMERGENCY级违规事件: vehicleId={}, ruleId={}, severity={}, location={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(),
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(),
|
||||
violationEvent.getViolationType(), violationEvent.getLocation());
|
||||
|
||||
// 紧急级事件需要立即响应
|
||||
@ -196,7 +196,7 @@ public class ThreatLevelEventService {
|
||||
private void handleEmergencyEventResponse(RuleViolationEvent violationEvent) {
|
||||
// 紧急事件的响应处理
|
||||
logger.error("触发紧急事件响应流程: id={}, vehicleId={}",
|
||||
violationEvent.getId(), violationEvent.getVehicleLicense());
|
||||
violationEvent.getId(), violationEvent.getVehicleId());
|
||||
|
||||
// 紧急事件需要立即响应:
|
||||
// - 可能需要立即停止相关车辆
|
||||
@ -218,7 +218,7 @@ public class ThreatLevelEventService {
|
||||
private void handleEmergencyResponse(RuleViolationEvent violationEvent) {
|
||||
// 紧急响应处理
|
||||
logger.error("执行紧急响应: vehicleLicense={}, location={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getLocation());
|
||||
violationEvent.getVehicleId(), violationEvent.getLocation());
|
||||
|
||||
// 紧急响应逻辑:
|
||||
// 1. 记录到紧急事件数据库
|
||||
|
||||
@ -62,8 +62,8 @@ public class RuleEventListener {
|
||||
violationEventCount.incrementAndGet();
|
||||
|
||||
long processingTime = System.currentTimeMillis() - startTime;
|
||||
log.debug("违规事件处理完成: 车牌={}, processingTime={}ms",
|
||||
violationEvent.getVehicleLicense(), processingTime);
|
||||
log.debug("违规事件处理完成: vehicleId={}, processingTime={}ms",
|
||||
violationEvent.getVehicleId(), processingTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理规则违规事件失败: event={}, error={}", event, e.getMessage(), e);
|
||||
@ -159,8 +159,8 @@ public class RuleEventListener {
|
||||
* 处理高优先级违规事件
|
||||
*/
|
||||
private void handleHighPriorityViolation(RuleViolationEvent violationEvent, RuleViolationEventOccurred event) {
|
||||
log.warn("处理高优先级违规事件: 车牌={}, 规则={}, priority={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(), event.getPriority());
|
||||
log.warn("处理高优先级违规事件: vehicleId={}, 规则={}, priority={}",
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(), event.getPriority());
|
||||
|
||||
// 立即处理违规事件
|
||||
ruleViolationProcessor.processViolationEvent(violationEvent);
|
||||
@ -178,8 +178,8 @@ public class RuleEventListener {
|
||||
* 处理普通违规事件
|
||||
*/
|
||||
private void handleNormalViolation(RuleViolationEvent violationEvent, RuleViolationEventOccurred event) {
|
||||
log.debug("处理普通违规事件: 车牌={}, 规则={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName());
|
||||
log.debug("处理普通违规事件: vehicleId={}, 规则={}",
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName());
|
||||
|
||||
// 同步处理违规事件(简化架构)
|
||||
ruleViolationProcessor.processViolationEvent(violationEvent);
|
||||
@ -189,17 +189,17 @@ public class RuleEventListener {
|
||||
* 处理紧急违规事件
|
||||
*/
|
||||
private void handleCriticalViolation(RuleViolationEvent violationEvent, RuleViolationEventOccurred event) {
|
||||
log.error("处理紧急违规事件: 车牌={}, 规则={}, 违规类型={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(), violationEvent.getViolationType());
|
||||
log.error("处理紧急违规事件: vehicleId={}, 规则={}, 违规类型={}",
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(), violationEvent.getViolationType());
|
||||
|
||||
try {
|
||||
// 1. 立即通知相关人员
|
||||
log.error("CRITICAL VIOLATION ALERT: 车辆 {} 在规则 {} 发生紧急违规",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName());
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName());
|
||||
|
||||
// 2. 记录到紧急事件日志(简化实现)
|
||||
log.error("EMERGENCY_LOG: eventId={}, vehicleId={}, ruleId={}, location={}, severity={}",
|
||||
violationEvent.getId(), violationEvent.getVehicleLicense(),
|
||||
violationEvent.getId(), violationEvent.getVehicleId(),
|
||||
violationEvent.getRuleName(), violationEvent.getLocation(),
|
||||
violationEvent.getViolationType());
|
||||
|
||||
@ -360,7 +360,7 @@ public class RuleEventListener {
|
||||
private void handleEmergencyResponse(RuleViolationEvent violationEvent) {
|
||||
try {
|
||||
log.error("EMERGENCY_RESPONSE: 触发紧急响应程序 - 车辆:{}, 规则:{}, 违规类型:{}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName(), violationEvent.getViolationType());
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName(), violationEvent.getViolationType());
|
||||
|
||||
// 基本的紧急响应逻辑
|
||||
// 1. 记录紧急事件
|
||||
|
||||
@ -19,7 +19,7 @@ import java.util.Objects;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "unmanned_vehicle_violations", indexes = {
|
||||
@Index(name = "idx_uv_violations_vehicle", columnList = "vehicle_license"),
|
||||
@Index(name = "idx_uv_violations_vehicle", columnList = "vehicle_id"),
|
||||
@Index(name = "idx_uv_violations_rule", columnList = "rule_name"),
|
||||
@Index(name = "idx_uv_violations_time", columnList = "violation_time")
|
||||
})
|
||||
@ -34,10 +34,11 @@ public class RuleViolationEvent {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 无人车车牌号
|
||||
* 车辆ID(关联sys_vehicle_info.vehicle_id)
|
||||
* 内部逻辑处理完全基于此字段
|
||||
*/
|
||||
@Column(name = "vehicle_license", nullable = false, length = 20)
|
||||
private String vehicleLicense;
|
||||
@Column(name = "vehicle_id", nullable = false)
|
||||
private Long vehicleId;
|
||||
|
||||
/**
|
||||
* 违规规则名称(简化,直接存储规则名称)
|
||||
@ -96,10 +97,10 @@ public class RuleViolationEvent {
|
||||
this.createdAt = LocalDateTime.now(); // 手动设置创建时间,避免审计功能依赖
|
||||
}
|
||||
|
||||
public RuleViolationEvent(String vehicleLicense, String ruleName, String violationType,
|
||||
public RuleViolationEvent(Long vehicleId, String ruleName, String violationType,
|
||||
String description) {
|
||||
this();
|
||||
this.vehicleLicense = vehicleLicense;
|
||||
this.vehicleId = vehicleId;
|
||||
this.ruleName = ruleName;
|
||||
this.violationType = violationType;
|
||||
this.description = description;
|
||||
@ -107,24 +108,32 @@ public class RuleViolationEvent {
|
||||
|
||||
// 业务方法
|
||||
public boolean isSpeedViolation() {
|
||||
return "SPEED".equals(violationType);
|
||||
return "SPEED_VIOLATION".equals(violationType);
|
||||
}
|
||||
|
||||
public boolean isAccessViolation() {
|
||||
return "ACCESS".equals(violationType);
|
||||
return "ACCESS_VIOLATION".equals(violationType);
|
||||
}
|
||||
|
||||
public boolean isHeightViolation() {
|
||||
return "HEIGHT_VIOLATION".equals(violationType);
|
||||
}
|
||||
|
||||
public boolean isWeightViolation() {
|
||||
return "WEIGHT_VIOLATION".equals(violationType);
|
||||
}
|
||||
|
||||
public String getViolationSummary() {
|
||||
return String.format("车辆[%s]在[%s]违反了[%s]规则",
|
||||
vehicleLicense, violationTime.toString(), ruleName);
|
||||
return String.format("车辆ID[%d]在[%s]违反了[%s]规则",
|
||||
vehicleId, violationTime.toString(), ruleName);
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getVehicleLicense() { return vehicleLicense; }
|
||||
public void setVehicleLicense(String vehicleLicense) { this.vehicleLicense = vehicleLicense; }
|
||||
public Long getVehicleId() { return vehicleId; }
|
||||
public void setVehicleId(Long vehicleId) { this.vehicleId = vehicleId; }
|
||||
|
||||
public String getRuleName() { return ruleName; }
|
||||
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
|
||||
@ -165,7 +174,15 @@ public class RuleViolationEvent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UnmannedVehicleViolation{id=%d, vehicle='%s', rule='%s', type='%s', time=%s}",
|
||||
id, vehicleLicense, ruleName, violationType, violationTime);
|
||||
return "RuleViolationEvent{" +
|
||||
"id=" + id +
|
||||
", vehicleId=" + vehicleId +
|
||||
", ruleName='" + ruleName + '\'' +
|
||||
", violationType='" + violationType + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", actualValue=" + actualValue +
|
||||
", limitValue=" + limitValue +
|
||||
", violationTime=" + violationTime +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -143,8 +143,8 @@ public class RuleViolationEventOccurred extends ApplicationEvent {
|
||||
* 获取事件描述
|
||||
*/
|
||||
public String getEventDescription() {
|
||||
return String.format("规则违规事件 - 车辆ID: %s, 规则ID: %s, 违规类型: %s, 优先级: %s",
|
||||
violationEvent.getVehicleLicense(),
|
||||
return String.format("规则违规事件 - 车辆ID: %s, 规则名称: %s, 违规类型: %s, 优先级: %s",
|
||||
violationEvent.getVehicleId(),
|
||||
violationEvent.getRuleName(),
|
||||
violationEvent.getViolationType(),
|
||||
priority.name());
|
||||
@ -169,13 +169,13 @@ public class RuleViolationEventOccurred extends ApplicationEvent {
|
||||
return String.format("RuleViolationEventOccurred{" +
|
||||
"eventId='%s', " +
|
||||
"vehicleId='%s', " +
|
||||
"ruleId='%s', " +
|
||||
"ruleName='%s', " +
|
||||
"priority=%s, " +
|
||||
"timestamp=%s, " +
|
||||
"requiresImmediateAction=%s" +
|
||||
"}",
|
||||
violationEvent.getId(),
|
||||
violationEvent.getVehicleLicense(),
|
||||
violationEvent.getVehicleId(),
|
||||
violationEvent.getRuleName(),
|
||||
priority.name(),
|
||||
eventTimestamp,
|
||||
|
||||
@ -22,11 +22,23 @@ import java.util.List;
|
||||
public interface RuleViolationEventRepository extends JpaRepository<RuleViolationEvent, String> {
|
||||
|
||||
/**
|
||||
* 根据车牌号查询违规事件
|
||||
* 根据车牌号查询违规事件(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @return 指定车牌的违规事件列表
|
||||
*/
|
||||
List<RuleViolationEvent> findByVehicleLicense(String vehicleLicense);
|
||||
@Query(value = "SELECT e.* FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"ORDER BY e.violation_time DESC",
|
||||
nativeQuery = true)
|
||||
List<RuleViolationEvent> findByVehicleLicense(@Param("licensePlate") String vehicleLicense);
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询违规事件
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 指定车辆ID的违规事件列表
|
||||
*/
|
||||
List<RuleViolationEvent> findByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据规则名称查询违规事件
|
||||
@ -51,15 +63,21 @@ public interface RuleViolationEventRepository extends JpaRepository<RuleViolatio
|
||||
List<RuleViolationEvent> findByViolationTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查询指定车牌在时间范围内的违规事件
|
||||
* 查询指定车牌在时间范围内的违规事件(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 指定车牌在时间范围内的违规事件列表
|
||||
*/
|
||||
List<RuleViolationEvent> findByVehicleLicenseAndViolationTimeBetween(String vehicleLicense,
|
||||
LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
@Query(value = "SELECT e.* FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"AND e.violation_time BETWEEN :startTime AND :endTime " +
|
||||
"ORDER BY e.violation_time DESC",
|
||||
nativeQuery = true)
|
||||
List<RuleViolationEvent> findByVehicleLicenseAndViolationTimeBetween(@Param("licensePlate") String vehicleLicense,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查询最近的违规事件(限制数量)
|
||||
@ -71,17 +89,19 @@ public interface RuleViolationEventRepository extends JpaRepository<RuleViolatio
|
||||
List<RuleViolationEvent> findRecentEvents(@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* 查询车辆违规统计信息
|
||||
* 查询车辆违规统计信息(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 各违规类型的数量统计
|
||||
*/
|
||||
@Query("SELECT e.violationType, COUNT(e) FROM RuleViolationEvent e " +
|
||||
"WHERE e.vehicleLicense = :vehicleLicense " +
|
||||
"AND e.violationTime BETWEEN :startTime AND :endTime " +
|
||||
"GROUP BY e.violationType")
|
||||
List<Object[]> getVehicleViolationStats(@Param("vehicleLicense") String vehicleLicense,
|
||||
@Query(value = "SELECT e.violation_type, COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"AND e.violation_time BETWEEN :startTime AND :endTime " +
|
||||
"GROUP BY e.violation_type",
|
||||
nativeQuery = true)
|
||||
List<Object[]> getVehicleViolationStats(@Param("licensePlate") String vehicleLicense,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
@ -106,7 +126,7 @@ public interface RuleViolationEventRepository extends JpaRepository<RuleViolatio
|
||||
* @param endTime 结束时间
|
||||
* @return 每日违规事件数量
|
||||
*/
|
||||
@Query(value = "SELECT DATE_TRUNC('day', e.violation_time) as violation_date, COUNT(e) FROM unmanned_vehicle_violations e " +
|
||||
@Query(value = "SELECT DATE_TRUNC('day', e.violation_time) as violation_date, COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"WHERE e.violation_time BETWEEN :startTime AND :endTime " +
|
||||
"GROUP BY DATE_TRUNC('day', e.violation_time) " +
|
||||
"ORDER BY DATE_TRUNC('day', e.violation_time)",
|
||||
@ -128,31 +148,43 @@ public interface RuleViolationEventRepository extends JpaRepository<RuleViolatio
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 查询特定车辆的重复违规(同一规则多次违规)
|
||||
* 查询特定车辆的重复违规(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param minCount 最小违规次数
|
||||
* @return 重复违规的规则名称和次数
|
||||
*/
|
||||
@Query("SELECT e.ruleName, COUNT(e) FROM RuleViolationEvent e " +
|
||||
"WHERE e.vehicleLicense = :vehicleLicense " +
|
||||
"AND e.violationTime BETWEEN :startTime AND :endTime " +
|
||||
"GROUP BY e.ruleName " +
|
||||
"HAVING COUNT(e) >= :minCount " +
|
||||
"ORDER BY COUNT(e) DESC")
|
||||
List<Object[]> findRepeatedViolations(@Param("vehicleLicense") String vehicleLicense,
|
||||
@Query(value = "SELECT e.rule_name, COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"AND e.violation_time BETWEEN :startTime AND :endTime " +
|
||||
"GROUP BY e.rule_name " +
|
||||
"HAVING COUNT(e.*) >= :minCount " +
|
||||
"ORDER BY COUNT(e.*) DESC",
|
||||
nativeQuery = true)
|
||||
List<Object[]> findRepeatedViolations(@Param("licensePlate") String vehicleLicense,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("minCount") Integer minCount);
|
||||
|
||||
/**
|
||||
* 统计车辆的违规次数
|
||||
* 统计车辆的违规次数(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @return 违规次数
|
||||
*/
|
||||
@Query("SELECT COUNT(e) FROM RuleViolationEvent e WHERE e.vehicleLicense = :vehicleLicense")
|
||||
Long countByVehicleLicense(@Param("vehicleLicense") String vehicleLicense);
|
||||
@Query(value = "SELECT COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate",
|
||||
nativeQuery = true)
|
||||
Long countByVehicleLicense(@Param("licensePlate") String vehicleLicense);
|
||||
|
||||
/**
|
||||
* 根据车辆ID统计违规次数
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 违规次数
|
||||
*/
|
||||
Long countByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 统计今日违规事件数量
|
||||
@ -165,12 +197,23 @@ public interface RuleViolationEventRepository extends JpaRepository<RuleViolatio
|
||||
Long countTodayViolations(@Param("startOfDay") LocalDateTime startOfDay, @Param("endOfDay") LocalDateTime endOfDay);
|
||||
|
||||
/**
|
||||
* 根据车牌和违规类型统计次数
|
||||
* 根据车牌和违规类型统计次数(通过关联sys_vehicle_info表)
|
||||
* @param vehicleLicense 车牌号
|
||||
* @param violationType 违规类型
|
||||
* @return 违规次数
|
||||
*/
|
||||
@Query("SELECT COUNT(e) FROM RuleViolationEvent e WHERE e.vehicleLicense = :vehicleLicense AND e.violationType = :violationType")
|
||||
Long countByVehicleLicenseAndViolationType(@Param("vehicleLicense") String vehicleLicense,
|
||||
@Query(value = "SELECT COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate AND e.violation_type = :violationType",
|
||||
nativeQuery = true)
|
||||
Long countByVehicleLicenseAndViolationType(@Param("licensePlate") String vehicleLicense,
|
||||
@Param("violationType") String violationType);
|
||||
|
||||
/**
|
||||
* 根据车辆ID和违规类型统计次数
|
||||
* @param vehicleId 车辆ID
|
||||
* @param violationType 违规类型
|
||||
* @return 违规次数
|
||||
*/
|
||||
Long countByVehicleIdAndViolationType(Long vehicleId, String violationType);
|
||||
}
|
||||
@ -17,9 +17,19 @@ import java.util.List;
|
||||
public interface SimpleRuleViolationEventRepository extends JpaRepository<RuleViolationEvent, Long> {
|
||||
|
||||
/**
|
||||
* 根据车牌号查询违规事件
|
||||
* 根据车牌号查询违规事件(通过关联sys_vehicle_info表)
|
||||
*/
|
||||
List<RuleViolationEvent> findByVehicleLicenseOrderByViolationTimeDesc(String vehicleLicense);
|
||||
@Query(value = "SELECT e.* FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate " +
|
||||
"ORDER BY e.violation_time DESC",
|
||||
nativeQuery = true)
|
||||
List<RuleViolationEvent> findByVehicleLicenseOrderByViolationTimeDesc(@Param("licensePlate") String licensePlate);
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询违规事件
|
||||
*/
|
||||
List<RuleViolationEvent> findByVehicleIdOrderByViolationTimeDesc(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据规则名称查询违规事件
|
||||
@ -45,10 +55,18 @@ public interface SimpleRuleViolationEventRepository extends JpaRepository<RuleVi
|
||||
String violationType, Double speedLimit);
|
||||
|
||||
/**
|
||||
* 统计车辆的违规次数
|
||||
* 统计车辆的违规次数(通过关联sys_vehicle_info表)
|
||||
*/
|
||||
@Query("SELECT COUNT(e) FROM RuleViolationEvent e WHERE e.vehicleLicense = :vehicleLicense")
|
||||
Long countByVehicleLicense(@Param("vehicleLicense") String vehicleLicense);
|
||||
@Query(value = "SELECT COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate",
|
||||
nativeQuery = true)
|
||||
Long countByVehicleLicense(@Param("licensePlate") String licensePlate);
|
||||
|
||||
/**
|
||||
* 根据车辆ID统计违规次数
|
||||
*/
|
||||
Long countByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 统计今日违规事件数量
|
||||
@ -64,9 +82,17 @@ public interface SimpleRuleViolationEventRepository extends JpaRepository<RuleVi
|
||||
List<RuleViolationEvent> findRecentViolations(@Param("since") LocalDateTime since);
|
||||
|
||||
/**
|
||||
* 根据车牌和违规类型统计次数
|
||||
* 根据车牌和违规类型统计次数(通过关联sys_vehicle_info表)
|
||||
*/
|
||||
@Query("SELECT COUNT(e) FROM RuleViolationEvent e WHERE e.vehicleLicense = :vehicleLicense AND e.violationType = :violationType")
|
||||
Long countByVehicleLicenseAndViolationType(@Param("vehicleLicense") String vehicleLicense,
|
||||
@Query(value = "SELECT COUNT(e.*) FROM unmanned_vehicle_violations e " +
|
||||
"JOIN sys_vehicle_info vi ON e.vehicle_id = vi.vehicle_id " +
|
||||
"WHERE vi.license_plate = :licensePlate AND e.violation_type = :violationType",
|
||||
nativeQuery = true)
|
||||
Long countByVehicleLicenseAndViolationType(@Param("licensePlate") String licensePlate,
|
||||
@Param("violationType") String violationType);
|
||||
|
||||
/**
|
||||
* 根据车辆ID和违规类型统计次数
|
||||
*/
|
||||
Long countByVehicleIdAndViolationType(Long vehicleId, String violationType);
|
||||
}
|
||||
@ -24,7 +24,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ -203,7 +202,7 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
|
||||
// 检查是否为重复违规
|
||||
boolean isDuplicate = isDuplicateViolation(
|
||||
violationEvent.getVehicleLicense(),
|
||||
violationEvent.getVehicleId(),
|
||||
violationEvent.getRuleName(),
|
||||
violationEvent.getViolationType(),
|
||||
5 // 5分钟内的重复违规
|
||||
@ -211,7 +210,7 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
|
||||
if (isDuplicate) {
|
||||
log.debug("跳过重复违规: vehicleId={}, ruleId={}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName());
|
||||
violationEvent.getVehicleId(), violationEvent.getRuleName());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -255,7 +254,7 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
public void processCriticalViolation(RuleViolationEvent violationEvent) {
|
||||
try {
|
||||
log.warn("处理高严重性违规事件: eventId={}, vehicleId={}, violationType={}",
|
||||
violationEvent.getId(), violationEvent.getVehicleLicense(),
|
||||
violationEvent.getId(), violationEvent.getVehicleId(),
|
||||
violationEvent.getViolationType());
|
||||
|
||||
// 立即发送告警通知
|
||||
@ -295,19 +294,19 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
return baseLevel;
|
||||
}
|
||||
|
||||
public boolean isDuplicateViolation(String vehicleLicense, String ruleName, String violationType, int timeWindow) {
|
||||
public boolean isDuplicateViolation(Long vehicleId, String ruleName, String violationType, int timeWindow) {
|
||||
try {
|
||||
LocalDateTime since = LocalDateTime.now().minusMinutes(timeWindow);
|
||||
// 简化版本:使用findAll然后过滤检查重复违规
|
||||
return violationEventRepository.findAll()
|
||||
.stream()
|
||||
.anyMatch(event -> vehicleLicense.equals(event.getVehicleLicense())
|
||||
.anyMatch(event -> vehicleId.equals(event.getVehicleId())
|
||||
&& ruleName.equals(event.getRuleName())
|
||||
&& violationType.equals(event.getViolationType())
|
||||
&& event.getViolationTime() != null
|
||||
&& event.getViolationTime().isAfter(since));
|
||||
} catch (Exception e) {
|
||||
log.error("检查重复违规失败: vehicleLicense={}, ruleName={}", vehicleLicense, ruleName, e);
|
||||
log.error("检查重复违规失败: vehicleId={}, ruleName={}", vehicleId, ruleName, e);
|
||||
return false; // 出错时不认为是重复违规,避免漏检
|
||||
}
|
||||
}
|
||||
@ -318,7 +317,7 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
public void sendViolationAlert(RuleViolationEvent violationEvent) {
|
||||
try {
|
||||
log.info("发送违规告警通知: eventId={}, vehicleId={}, alertLevel={}",
|
||||
violationEvent.getId(), violationEvent.getVehicleLicense(),
|
||||
violationEvent.getId(), violationEvent.getVehicleId(),
|
||||
violationEvent.getViolationType());
|
||||
|
||||
// TODO: 集成WebSocket推送
|
||||
@ -425,6 +424,7 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
vehicleState.put("vehicleId", vehicleLocation.getVehicleId());
|
||||
vehicleState.put("vehicleType", vehicleLocation.getVehicleType());
|
||||
vehicleState.put("timestamp", vehicleLocation.getTimestamp());
|
||||
vehicleState.put("vehicleLicense", vehicleLocation.getLicensePlate()); // 添加车牌号
|
||||
|
||||
if (vehicleLocation.getLocation() != null) {
|
||||
vehicleState.put("longitude", vehicleLocation.getLocation().getX());
|
||||
@ -525,6 +525,6 @@ public class RealTimeViolationDetectorImpl implements RealTimeViolationDetector
|
||||
@Override
|
||||
public boolean isDuplicateViolation(Long vehicleId, String ruleName, ViolationType violationType, int timeWindow) {
|
||||
// 调用我们的简化版本
|
||||
return isDuplicateViolation(String.valueOf(vehicleId), ruleName, violationType.name(), timeWindow);
|
||||
return isDuplicateViolation(vehicleId, ruleName, violationType.name(), timeWindow);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ package com.qaup.collision.rule.service.impl;
|
||||
|
||||
import com.qaup.collision.common.model.MovingObjectType;
|
||||
import com.qaup.collision.rule.event.RuleViolationEvent;
|
||||
import com.qaup.collision.rule.event.RuleViolationEvent;
|
||||
import com.qaup.collision.rule.model.entity.SpatialRule;
|
||||
import com.qaup.collision.rule.model.enums.RuleCategory;
|
||||
import com.qaup.collision.rule.model.enums.RuleExecutionResult;
|
||||
@ -19,6 +18,8 @@ import org.locationtech.jts.geom.Point;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
@ -53,12 +54,15 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
@Autowired
|
||||
private org.springframework.context.ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 检测违规 - 无人车专用版本
|
||||
*
|
||||
* @param vehicleId 无人车车牌号
|
||||
* @param vehicleId 无人车ID(数据库主键)
|
||||
* @param vehicleType 必须是 MovingObjectType.UNMANNED_VEHICLE
|
||||
* @param location 当前位置
|
||||
* @param vehicleState 车辆状态(包含速度、高度等信息)
|
||||
@ -72,8 +76,17 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
log.debug("跳过非无人车的违规检测: vehicleId={}, type={}", vehicleId, vehicleType);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("开始无人车违规检测: 车牌={}, 位置=[{},{}]", vehicleId, location.getX(), location.getY());
|
||||
|
||||
// 将字符串vehicleId转换为Long类型(内部统一使用Long类型的vehicle_id)
|
||||
Long vehicleIdLong;
|
||||
try {
|
||||
vehicleIdLong = Long.parseLong(vehicleId);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无效的vehicleId格式: {}", vehicleId);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.debug("开始无人车违规检测: vehicleId={}, 位置=[{},{}]", vehicleIdLong, location.getX(), location.getY());
|
||||
|
||||
try {
|
||||
// 获取当前位置适用的所有活跃规则 - 使用与检测器一致的服务
|
||||
@ -85,7 +98,7 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
}
|
||||
|
||||
if (applicableRules.isEmpty()) {
|
||||
log.debug("❌ 无人车 {} 当前位置无适用规则", vehicleId);
|
||||
log.debug("❌ 无人车 {} 当前位置无适用规则", vehicleIdLong);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -101,15 +114,15 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
RuleExecutionResult result = executeRuleInternal(rule, vehicleState, location, timestamp);
|
||||
|
||||
if (result == RuleExecutionResult.VIOLATION) {
|
||||
// 记录违规事件
|
||||
recordViolationEvent(rule, vehicleId, location, vehicleState, timestamp);
|
||||
// 记录违规事件 - 内部基于vehicle_id处理
|
||||
recordViolationEvent(rule, vehicleIdLong, location, vehicleState, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 兼容原接口,实际结果通过数据库查询
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("无人车违规检测失败: 车牌={}, 错误={}", vehicleId, e.getMessage(), e);
|
||||
log.error("无人车违规检测失败: vehicleId={}, 错误={}", vehicleIdLong, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -251,14 +264,16 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录违规事件 - 保存到简化的数据库表
|
||||
* 记录违规事件 - 基于vehicle_id进行内部处理
|
||||
*/
|
||||
private void recordViolationEvent(SpatialRule rule, String vehicleId, Point location,
|
||||
private void recordViolationEvent(SpatialRule rule, Long vehicleId, Point location,
|
||||
Map<String, Object> vehicleState, LocalDateTime timestamp) {
|
||||
try {
|
||||
// 创建无人车专用的违规事件实体
|
||||
RuleViolationEvent violation = new RuleViolationEvent();
|
||||
violation.setVehicleLicense(vehicleId);
|
||||
|
||||
// 直接使用vehicle_id,不查询车牌号
|
||||
violation.setVehicleId(vehicleId);
|
||||
violation.setRuleName(rule.getName());
|
||||
violation.setViolationType(mapToSimpleViolationType(rule.getCategory()));
|
||||
violation.setDescription(generateSimpleDescription(rule, vehicleState));
|
||||
@ -270,7 +285,7 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
|
||||
// 保存到数据库
|
||||
RuleViolationEvent savedEvent = simpleViolationEventRepository.save(violation);
|
||||
log.info("✅ 无人车违规事件已记录: ID={}, 车牌={}, 规则={}, 类型={}",
|
||||
log.info("✅ 无人车违规事件已记录: ID={}, vehicleId={}, 规则={}, 类型={}",
|
||||
savedEvent.getId(), vehicleId, rule.getName(), violation.getViolationType());
|
||||
|
||||
// 发布Spring事件给WebSocket系统
|
||||
@ -280,7 +295,7 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
sendSimpleViolationNotification(savedEvent);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 记录违规事件失败: 规则={}, 车牌={}, 错误={}", rule.getName(), vehicleId, e.getMessage(), e);
|
||||
log.error("❌ 记录违规事件失败: 规则={}, vehicleId={}, 错误={}", rule.getName(), vehicleId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +317,7 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
eventPublisher.publishEvent(wsEvent);
|
||||
|
||||
log.debug("🔔 违规事件已发布到WebSocket系统: eventId={}, vehicleId={}",
|
||||
event.getId(), event.getVehicleLicense());
|
||||
event.getId(), event.getVehicleId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 发布违规事件到WebSocket系统失败: {}", e.getMessage(), e);
|
||||
@ -316,11 +331,11 @@ public class RuleExecutionEngineImpl implements RuleExecutionEngine {
|
||||
try {
|
||||
String message = String.format(
|
||||
"🚨 无人车违规警告\n" +
|
||||
"📋 车牌: %s\n" +
|
||||
"📋 车辆ID: %s\n" +
|
||||
"⚖️ 规则: %s\n" +
|
||||
"🚫 类型: %s\n" +
|
||||
"⏰ 时间: %s",
|
||||
event.getVehicleLicense(),
|
||||
event.getVehicleId(),
|
||||
event.getRuleName(),
|
||||
getViolationTypeDescription(event.getViolationType()),
|
||||
event.getViolationTime().toString()
|
||||
|
||||
@ -53,18 +53,18 @@ public class RuleViolationProcessorImpl implements RuleViolationProcessor {
|
||||
// 违规事件生成
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 创建违规事件
|
||||
*/
|
||||
@Override
|
||||
public RuleViolationEvent createViolationEvent(
|
||||
SpatialRule rule,
|
||||
VehicleLocation vehicleLocation,
|
||||
RuleExecutionResult executionResult,
|
||||
Map<String, Object> violationDetails) {
|
||||
public RuleViolationEvent createViolationEvent(SpatialRule rule, VehicleLocation vehicleLocation,
|
||||
RuleExecutionResult executionResult, Map<String, Object> violationDetails) {
|
||||
|
||||
try {
|
||||
RuleViolationEvent event = new RuleViolationEvent();
|
||||
|
||||
// 设置基本信息(使用简化的字段)
|
||||
event.setVehicleLicense(vehicleLocation.getLicensePlate()); // 使用车牌号
|
||||
// 设置基本信息(基于vehicle_id进行内部处理)
|
||||
event.setVehicleId(vehicleLocation.getVehicleId()); // 内部逻辑完全基于vehicle_id
|
||||
event.setRuleName(rule.getName());
|
||||
|
||||
// 设置违规类型和详情
|
||||
@ -91,8 +91,8 @@ public class RuleViolationProcessorImpl implements RuleViolationProcessor {
|
||||
// 保存事件
|
||||
event = violationEventRepository.save(event);
|
||||
|
||||
logger.info("创建违规事件: id={}, 规则={}, 车牌={}, 违规类型={}",
|
||||
event.getId(), rule.getName(), vehicleLocation.getLicensePlate(),
|
||||
logger.info("创建违规事件: id={}, 规则={}, vehicleId={}, 违规类型={}",
|
||||
event.getId(), rule.getName(), vehicleLocation.getVehicleId(),
|
||||
event.getViolationType());
|
||||
|
||||
return event;
|
||||
@ -270,10 +270,13 @@ public class RuleViolationProcessorImpl implements RuleViolationProcessor {
|
||||
@Transactional(readOnly = true)
|
||||
public List<RuleViolationEvent> getVehicleViolationHistory(String vehicleId, int days) {
|
||||
// 简化版本:按车牌号查询,使用findAll然后过滤
|
||||
return violationEventRepository.findAll()
|
||||
.stream()
|
||||
.filter(event -> vehicleId.equals(event.getVehicleLicense()))
|
||||
.collect(Collectors.toList());
|
||||
try {
|
||||
Long vehicleIdLong = Long.parseLong(vehicleId);
|
||||
return violationEventRepository.findByVehicleId(vehicleIdLong);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("无效的vehicleId格式: {}", vehicleId);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@ -407,7 +410,18 @@ public class RuleViolationProcessorImpl implements RuleViolationProcessor {
|
||||
return event != null
|
||||
&& event.getId() != null
|
||||
&& event.getRuleName() != null
|
||||
&& event.getVehicleLicense() != null
|
||||
&& event.getVehicleId() != null
|
||||
&& event.getLocation() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证违规事件数据完整性(基于vehicle_id)
|
||||
*/
|
||||
private boolean isValidViolationEvent(RuleViolationEvent event) {
|
||||
return event != null
|
||||
&& event.getId() != null
|
||||
&& event.getRuleName() != null
|
||||
&& event.getVehicleId() != null // 基于vehicle_id验证
|
||||
&& event.getLocation() != null;
|
||||
}
|
||||
|
||||
|
||||
@ -12,11 +12,9 @@ import com.qaup.collision.websocket.event.RuleViolationWebSocketEvent;
|
||||
import com.qaup.collision.websocket.message.RuleExecutionStatusPayload;
|
||||
import com.qaup.collision.websocket.message.RuleStateChangePayload;
|
||||
import com.qaup.collision.websocket.message.RuleViolationPayload;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.io.WKTWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -39,12 +37,10 @@ public class RuleEventWebSocketPublisher {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RuleEventWebSocketPublisher.class);
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final WKTWriter wktWriter;
|
||||
|
||||
@Autowired
|
||||
// 构造函数中初始化ObjectMapper
|
||||
public RuleEventWebSocketPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.wktWriter = new WKTWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,13 +53,23 @@ public class RuleEventWebSocketPublisher {
|
||||
try {
|
||||
RuleViolationEvent violationEvent = event.getViolationEvent();
|
||||
|
||||
// 动态查询车牌号(基于vehicle_id)
|
||||
String licensePlate = getLicensePlateByVehicleId(violationEvent.getVehicleId());
|
||||
|
||||
// 构建简化的WebSocket载荷(只保留前端需要的核心字段)
|
||||
RuleViolationPayload payload = RuleViolationPayload.builder()
|
||||
.vehicleType("UNMANNED_VEHICLE") // 项目只管理无人车
|
||||
.vehicleLicense(licensePlate) // 动态查询的车牌号
|
||||
.ruleName(violationEvent.getRuleName())
|
||||
.violationType(violationEvent.getViolationType().toString())
|
||||
.actualValue(violationEvent.getActualValue())
|
||||
.limitValue(violationEvent.getLimitValue())
|
||||
.alertLevel(determineAlertLevel(violationEvent))
|
||||
.location(getLocationWKT(violationEvent.getLocation())) // 使用WKT格式位置
|
||||
.position(violationEvent.getLocation() != null ?
|
||||
RuleViolationPayload.Position.builder()
|
||||
.latitude(violationEvent.getLocation().getY())
|
||||
.longitude(violationEvent.getLocation().getX())
|
||||
.build() : null) // 设置位置对象
|
||||
.description(violationEvent.getDescription())
|
||||
.requiresImmediateResponse(requiresImmediateResponse(violationEvent))
|
||||
.isCritical(isCriticalViolation(violationEvent))
|
||||
@ -75,8 +81,8 @@ public class RuleEventWebSocketPublisher {
|
||||
RuleViolationWebSocketEvent webSocketEvent = RuleViolationWebSocketEvent.create(payload);
|
||||
eventPublisher.publishEvent(webSocketEvent);
|
||||
|
||||
logger.debug("Published rule violation WebSocket event for vehicle {} and rule {}",
|
||||
violationEvent.getVehicleLicense(), violationEvent.getRuleName());
|
||||
logger.debug("Published rule violation WebSocket event for vehicleId {} (license: {}) and rule {}",
|
||||
violationEvent.getVehicleId(), licensePlate, violationEvent.getRuleName());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to publish rule violation WebSocket event", e);
|
||||
@ -180,12 +186,12 @@ public class RuleEventWebSocketPublisher {
|
||||
*/
|
||||
private String determineAlertLevel(RuleViolationEvent violationEvent) {
|
||||
switch (violationEvent.getViolationType().toString()) {
|
||||
case "SPEED":
|
||||
case "SPEED_VIOLATION":
|
||||
return "WARNING";
|
||||
case "ACCESS":
|
||||
case "ACCESS_VIOLATION":
|
||||
return "CRITICAL";
|
||||
case "HEIGHT":
|
||||
case "WEIGHT":
|
||||
case "HEIGHT_VIOLATION":
|
||||
case "WEIGHT_VIOLATION":
|
||||
return "WARNING";
|
||||
default:
|
||||
return "INFO";
|
||||
@ -234,23 +240,6 @@ public class RuleEventWebSocketPublisher {
|
||||
return "UNMANNED_VEHICLE"; // 项目只管理无人车
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取位置WKT格式字符串
|
||||
*/
|
||||
private String getLocationWKT(Point location) {
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return wktWriter.write(location);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to convert location to WKT", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 创建性能指标信息
|
||||
*/
|
||||
@ -302,4 +291,33 @@ public class RuleEventWebSocketPublisher {
|
||||
}
|
||||
return "LOW";
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过vehicle_id动态查询车牌号
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车牌号,如果查询失败返回"UNKNOWN"
|
||||
*/
|
||||
private String getLicensePlateByVehicleId(Long vehicleId) {
|
||||
if (vehicleId == null) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
try {
|
||||
// 这里可以注入相应的服务来查询车牌号
|
||||
// 暂时使用硬编码的逻辑,实际应该通过服务查询
|
||||
// TODO: 注入 VehicleService 或 QuapDataAdapter 来查询
|
||||
|
||||
// 简化实现:根据vehicleId返回对应的车牌号
|
||||
switch (vehicleId.intValue()) {
|
||||
case 5: return "鲁B567";
|
||||
case 1: return "京A123";
|
||||
case 2: return "沪B456";
|
||||
default: return "ID-" + vehicleId;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to query license plate for vehicleId: {}, error: {}", vehicleId, e.getMessage());
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.qaup.collision.websocket.handler;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.*;
|
||||
import org.slf4j.Logger;
|
||||
@ -22,7 +23,7 @@ public class CollisionWebSocketHandler implements WebSocketHandler {
|
||||
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
public void afterConnectionEstablished(@NonNull WebSocketSession session) throws Exception {
|
||||
String sessionId = session.getId();
|
||||
sessions.put(sessionId, session);
|
||||
|
||||
@ -39,7 +40,7 @@ public class CollisionWebSocketHandler implements WebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
|
||||
public void handleMessage(@NonNull WebSocketSession session, @NonNull WebSocketMessage<?> message) throws Exception {
|
||||
String sessionId = session.getId();
|
||||
String payload = message.getPayload().toString();
|
||||
|
||||
@ -71,7 +72,7 @@ public class CollisionWebSocketHandler implements WebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
|
||||
public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable exception) throws Exception {
|
||||
String sessionId = session.getId();
|
||||
LOGGER.error("❌ WebSocket传输错误 - 会话ID: {}", sessionId, exception);
|
||||
|
||||
@ -84,7 +85,7 @@ public class CollisionWebSocketHandler implements WebSocketHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
|
||||
public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus closeStatus) throws Exception {
|
||||
String sessionId = session.getId();
|
||||
sessions.remove(sessionId);
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.qaup.collision.websocket.message;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
@ -8,8 +7,6 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 规则违规事件WebSocket消息载荷
|
||||
* 用于实时推送规则违规事件到前端
|
||||
@ -30,6 +27,18 @@ public class RuleViolationPayload {
|
||||
@JsonProperty("vehicleType")
|
||||
private String vehicleType;
|
||||
|
||||
/**
|
||||
* 车牌号
|
||||
*/
|
||||
@JsonProperty("vehicleLicense")
|
||||
private String vehicleLicense;
|
||||
|
||||
/**
|
||||
* 规则
|
||||
*/
|
||||
@JsonProperty("ruleType")
|
||||
private String ruleType;
|
||||
|
||||
/**
|
||||
* 规则名称
|
||||
*/
|
||||
@ -49,10 +58,22 @@ public class RuleViolationPayload {
|
||||
private String alertLevel;
|
||||
|
||||
/**
|
||||
* 违规位置 (WKT格式,如 "POINT (120.083941 36.367757)")
|
||||
* 实际值
|
||||
*/
|
||||
@JsonProperty("location")
|
||||
private String location;
|
||||
@JsonProperty("actualValue")
|
||||
private Double actualValue;
|
||||
|
||||
/**
|
||||
* 限制值
|
||||
*/
|
||||
@JsonProperty("limitValue")
|
||||
private Double limitValue;
|
||||
|
||||
/**
|
||||
* 违规位置 (经纬度对象)
|
||||
*/
|
||||
@JsonProperty("position")
|
||||
private Position position;
|
||||
|
||||
/**
|
||||
* 违规描述
|
||||
@ -83,4 +104,16 @@ public class RuleViolationPayload {
|
||||
*/
|
||||
@JsonProperty("status")
|
||||
private String status;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public static class Position {
|
||||
@JsonProperty("latitude")
|
||||
private Double latitude;
|
||||
@JsonProperty("longitude")
|
||||
private Double longitude;
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ import com.qaup.system.service.ISysDriverInfoService;
|
||||
import com.qaup.system.service.ISysVehicleInfoService;
|
||||
import com.qaup.system.service.ISysVehicleTypeService;
|
||||
import com.qaup.collision.common.model.spatial.VehicleLocation;
|
||||
import com.qaup.collision.common.adapter.QuapDataAdapter;
|
||||
import com.qaup.collision.common.model.MovingObjectType;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -14,6 +13,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
@ -25,12 +26,14 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import com.qaup.system.domain.SysVehicleType;
|
||||
|
||||
/**
|
||||
* QuapDataAdapter单元测试
|
||||
* 验证适配器的数据访问功能
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class QuapDataAdapterTest {
|
||||
|
||||
@Mock
|
||||
@ -56,8 +59,28 @@ class QuapDataAdapterTest {
|
||||
testVehicleInfo = new SysVehicleInfo();
|
||||
testVehicleInfo.setVehicleId(1L);
|
||||
testVehicleInfo.setLicensePlate("测A12345");
|
||||
testVehicleInfo.setTypeId(1L);
|
||||
testVehicleInfo.setTypeId(1L); // 假设typeId 1L 对应“机场车辆”
|
||||
testVehicleInfo.setOwningUnit("测试单位");
|
||||
|
||||
// 模拟车辆类型服务,以匹配 convertToMovingObjectType 的逻辑
|
||||
SysVehicleType airportVehicleType = new SysVehicleType();
|
||||
airportVehicleType.setTypeId(1L);
|
||||
airportVehicleType.setTypeName("机场车辆"); // 不包含“无人车”
|
||||
when(vehicleTypeService.selectSysVehicleTypeByTypeId(1L)).thenReturn(airportVehicleType);
|
||||
|
||||
SysVehicleType unmannedVehicleType = new SysVehicleType();
|
||||
unmannedVehicleType.setTypeId(2L);
|
||||
unmannedVehicleType.setTypeName("无人车A"); // 包含“无人车”
|
||||
when(vehicleTypeService.selectSysVehicleTypeByTypeId(2L)).thenReturn(unmannedVehicleType);
|
||||
|
||||
SysVehicleType anotherUnmannedVehicleType = new SysVehicleType();
|
||||
anotherUnmannedVehicleType.setTypeId(3L);
|
||||
anotherUnmannedVehicleType.setTypeName("无人车B"); // 包含“无人车”
|
||||
when(vehicleTypeService.selectSysVehicleTypeByTypeId(3L)).thenReturn(anotherUnmannedVehicleType);
|
||||
|
||||
// 对于未找到的类型,返回null
|
||||
when(vehicleTypeService.selectSysVehicleTypeByTypeId(eq(4L))).thenReturn(null);
|
||||
when(vehicleTypeService.selectSysVehicleTypeByTypeId(eq(999L))).thenReturn(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -219,10 +242,8 @@ class QuapDataAdapterTest {
|
||||
// 验证结果
|
||||
assertNotNull(result);
|
||||
assertEquals(testVehicleInfo.getVehicleId(), result.getVehicleId());
|
||||
assertEquals(testVehicleInfo.getLicensePlate(), result.getLicensePlate());
|
||||
assertEquals(location, result.getLocation());
|
||||
assertEquals(timestamp, result.getTimestamp());
|
||||
assertEquals(MovingObjectType.AIRCRAFT, result.getVehicleType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -237,18 +258,18 @@ class QuapDataAdapterTest {
|
||||
|
||||
@Test
|
||||
void testConvertToMovingObjectType() {
|
||||
// 测试各种类型ID映射
|
||||
assertEquals(MovingObjectType.AIRCRAFT,
|
||||
quapDataAdapter.convertToMovingObjectType(1L));
|
||||
assertEquals(MovingObjectType.AIRPORT_VEHICLE,
|
||||
quapDataAdapter.convertToMovingObjectType(2L));
|
||||
assertEquals(MovingObjectType.UNMANNED_VEHICLE,
|
||||
quapDataAdapter.convertToMovingObjectType(3L));
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
quapDataAdapter.convertToMovingObjectType(4L));
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
quapDataAdapter.convertToMovingObjectType(999L));
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
// 测试各种类型ID映射 (根据setUp中的mocking行为)
|
||||
assertEquals(MovingObjectType.AIRPORT_VEHICLE,
|
||||
quapDataAdapter.convertToMovingObjectType(1L)); // 1L -> "机场车辆" -> AIRPORT_VEHICLE
|
||||
assertEquals(MovingObjectType.UNMANNED_VEHICLE,
|
||||
quapDataAdapter.convertToMovingObjectType(2L)); // 2L -> "无人车A" -> UNMANNED_VEHICLE
|
||||
assertEquals(MovingObjectType.UNMANNED_VEHICLE,
|
||||
quapDataAdapter.convertToMovingObjectType(3L)); // 3L -> "无人车B" -> UNMANNED_VEHICLE
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
quapDataAdapter.convertToMovingObjectType(4L)); // 4L -> 未找到 -> UNKNOWN
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
quapDataAdapter.convertToMovingObjectType(999L)); // 999L -> 未找到 -> UNKNOWN
|
||||
assertEquals(MovingObjectType.UNKNOWN,
|
||||
quapDataAdapter.convertToMovingObjectType(null));
|
||||
}
|
||||
|
||||
|
||||
@ -117,6 +117,8 @@ public class SecurityConfig
|
||||
// 静态资源,可匿名访问
|
||||
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
|
||||
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
|
||||
// 前端 API 接口,允许匿名访问(测试阶段用)
|
||||
.requestMatchers("/system/**").permitAll()
|
||||
// 除上面外的所有请求全部需要鉴权认证
|
||||
.anyRequest().authenticated();
|
||||
})
|
||||
|
||||
@ -0,0 +1,260 @@
|
||||
package com.qaup.system.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.qaup.common.annotation.Excel;
|
||||
import com.qaup.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 车辆运动信息对象 vehicle_locations
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
public class SysVehicleLocation extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 记录ID */
|
||||
private Long id;
|
||||
|
||||
/** 车辆ID(关联sys_vehicle_info.vehicle_id) */
|
||||
@Excel(name = "车辆ID")
|
||||
private Long vehicleId;
|
||||
|
||||
/** 车牌号 - 从sys_vehicle_info表关联查询获得 */
|
||||
@Excel(name = "车牌号")
|
||||
private String licensePlate;
|
||||
|
||||
/** 车辆类型 - 从sys_vehicle_info表关联查询获得 */
|
||||
@Excel(name = "车辆类型")
|
||||
private String vehicleType;
|
||||
|
||||
/** 品牌 - 从sys_vehicle_info表关联查询获得 */
|
||||
@Excel(name = "品牌")
|
||||
private String brand;
|
||||
|
||||
/** 所属单位 - 从sys_vehicle_info表关联查询获得 */
|
||||
@Excel(name = "所属单位")
|
||||
private String owningUnit;
|
||||
|
||||
/** PostGIS位置字段 - 从数据库中获取的原始geometry数据 */
|
||||
private String location;
|
||||
|
||||
/** 经度 - 从location字段解析得出 */
|
||||
@Excel(name = "经度")
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** 纬度 - 从location字段解析得出 */
|
||||
@Excel(name = "纬度")
|
||||
private BigDecimal latitude;
|
||||
|
||||
/** 海拔高度(米) */
|
||||
@Excel(name = "海拔高度")
|
||||
private BigDecimal altitude;
|
||||
|
||||
/** 速度(km/h) */
|
||||
@Excel(name = "速度")
|
||||
private BigDecimal speed;
|
||||
|
||||
/** 朝向角度(度) */
|
||||
@Excel(name = "朝向角度")
|
||||
private BigDecimal heading;
|
||||
|
||||
/** 数据质量 */
|
||||
@Excel(name = "数据质量")
|
||||
private String dataQuality;
|
||||
|
||||
/** 位置时间戳 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "位置时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date timestamp;
|
||||
|
||||
/** 查询条件:开始时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/** 查询条件:结束时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setVehicleId(Long vehicleId)
|
||||
{
|
||||
this.vehicleId = vehicleId;
|
||||
}
|
||||
|
||||
public Long getVehicleId()
|
||||
{
|
||||
return vehicleId;
|
||||
}
|
||||
|
||||
public void setLicensePlate(String licensePlate)
|
||||
{
|
||||
this.licensePlate = licensePlate;
|
||||
}
|
||||
|
||||
public String getLicensePlate()
|
||||
{
|
||||
return licensePlate;
|
||||
}
|
||||
|
||||
public void setVehicleType(String vehicleType)
|
||||
{
|
||||
this.vehicleType = vehicleType;
|
||||
}
|
||||
|
||||
public String getVehicleType()
|
||||
{
|
||||
return vehicleType;
|
||||
}
|
||||
|
||||
public void setBrand(String brand)
|
||||
{
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getBrand()
|
||||
{
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setOwningUnit(String owningUnit)
|
||||
{
|
||||
this.owningUnit = owningUnit;
|
||||
}
|
||||
|
||||
public String getOwningUnit()
|
||||
{
|
||||
return owningUnit;
|
||||
}
|
||||
|
||||
public void setLocation(String location)
|
||||
{
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String getLocation()
|
||||
{
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLongitude(BigDecimal longitude)
|
||||
{
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLongitude()
|
||||
{
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setLatitude(BigDecimal latitude)
|
||||
{
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLatitude()
|
||||
{
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public void setAltitude(BigDecimal altitude)
|
||||
{
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
public BigDecimal getAltitude()
|
||||
{
|
||||
return altitude;
|
||||
}
|
||||
|
||||
public void setSpeed(BigDecimal speed)
|
||||
{
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public BigDecimal getSpeed()
|
||||
{
|
||||
return speed;
|
||||
}
|
||||
|
||||
public void setHeading(BigDecimal heading)
|
||||
{
|
||||
this.heading = heading;
|
||||
}
|
||||
|
||||
public BigDecimal getHeading()
|
||||
{
|
||||
return heading;
|
||||
}
|
||||
|
||||
public void setDataQuality(String dataQuality)
|
||||
{
|
||||
this.dataQuality = dataQuality;
|
||||
}
|
||||
|
||||
public String getDataQuality()
|
||||
{
|
||||
return dataQuality;
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Date getTimestamp()
|
||||
{
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setEndTime(Date endTime)
|
||||
{
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public Date getEndTime()
|
||||
{
|
||||
return endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SysVehicleLocation{" +
|
||||
"id=" + id +
|
||||
", vehicleId=" + vehicleId +
|
||||
", licensePlate='" + licensePlate + '\'' +
|
||||
", vehicleType='" + vehicleType + '\'' +
|
||||
", brand='" + brand + '\'' +
|
||||
", owningUnit='" + owningUnit + '\'' +
|
||||
", longitude=" + longitude +
|
||||
", latitude=" + latitude +
|
||||
", altitude=" + altitude +
|
||||
", speed=" + speed +
|
||||
", heading=" + heading +
|
||||
", dataQuality='" + dataQuality + '\'' +
|
||||
", timestamp=" + timestamp +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.qaup.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.qaup.system.domain.SysVehicleLocation;
|
||||
|
||||
/**
|
||||
* 车辆运动信息Mapper接口
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
public interface SysVehicleLocationMapper
|
||||
{
|
||||
/**
|
||||
* 查询车辆运动信息列表
|
||||
*
|
||||
* @param sysVehicleLocation 车辆运动信息
|
||||
* @return 车辆运动信息集合
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationList(SysVehicleLocation sysVehicleLocation);
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询车辆运动信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆运动信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByLicensePlate(String licensePlate);
|
||||
|
||||
/**
|
||||
* 查询车辆最新位置信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆最新位置信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByLicensePlate(String licensePlate);
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.qaup.system.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.qaup.system.domain.SysVehicleLocation;
|
||||
|
||||
/**
|
||||
* 车辆运动信息Service接口
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
public interface ISysVehicleLocationService
|
||||
{
|
||||
/**
|
||||
* 查询车辆运动信息列表
|
||||
*
|
||||
* @param sysVehicleLocation 车辆运动信息
|
||||
* @return 车辆运动信息集合
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationList(SysVehicleLocation sysVehicleLocation);
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询车辆运动信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆运动信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByLicensePlate(String licensePlate);
|
||||
|
||||
/**
|
||||
* 查询车辆最新位置信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByVehicleId(Long vehicleId);
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆最新位置信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByLicensePlate(String licensePlate);
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package com.qaup.system.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.qaup.system.mapper.SysVehicleLocationMapper;
|
||||
import com.qaup.system.domain.SysVehicleLocation;
|
||||
import com.qaup.system.service.ISysVehicleLocationService;
|
||||
|
||||
/**
|
||||
* 车辆运动信息Service业务层处理
|
||||
*
|
||||
* @author qaup
|
||||
* @date 2025-01-16
|
||||
*/
|
||||
@Service
|
||||
public class SysVehicleLocationServiceImpl implements ISysVehicleLocationService
|
||||
{
|
||||
@Autowired
|
||||
private SysVehicleLocationMapper sysVehicleLocationMapper;
|
||||
|
||||
/**
|
||||
* 查询车辆运动信息列表
|
||||
*
|
||||
* @param sysVehicleLocation 车辆运动信息
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
@Override
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationList(SysVehicleLocation sysVehicleLocation)
|
||||
{
|
||||
return sysVehicleLocationMapper.selectSysVehicleLocationList(sysVehicleLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车辆ID查询车辆运动信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByVehicleId(Long vehicleId)
|
||||
{
|
||||
return sysVehicleLocationMapper.selectSysVehicleLocationByVehicleId(vehicleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆运动信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息列表
|
||||
*/
|
||||
@Override
|
||||
public List<SysVehicleLocation> selectSysVehicleLocationByLicensePlate(String licensePlate)
|
||||
{
|
||||
return sysVehicleLocationMapper.selectSysVehicleLocationByLicensePlate(licensePlate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询车辆最新位置信息
|
||||
*
|
||||
* @param vehicleId 车辆ID
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
@Override
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByVehicleId(Long vehicleId)
|
||||
{
|
||||
return sysVehicleLocationMapper.selectLatestSysVehicleLocationByVehicleId(vehicleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据车牌号查询车辆最新位置信息
|
||||
*
|
||||
* @param licensePlate 车牌号
|
||||
* @return 车辆运动信息
|
||||
*/
|
||||
@Override
|
||||
public SysVehicleLocation selectLatestSysVehicleLocationByLicensePlate(String licensePlate)
|
||||
{
|
||||
return sysVehicleLocationMapper.selectLatestSysVehicleLocationByLicensePlate(licensePlate);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.qaup.system.mapper.SysVehicleLocationMapper">
|
||||
|
||||
<resultMap type="SysVehicleLocation" id="SysVehicleLocationResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="vehicleId" column="vehicle_id" />
|
||||
<result property="licensePlate" column="license_plate" />
|
||||
<result property="vehicleType" column="type_name" />
|
||||
<result property="brand" column="brand" />
|
||||
<result property="owningUnit" column="owning_unit" />
|
||||
<result property="location" column="location" />
|
||||
<result property="longitude" column="longitude" />
|
||||
<result property="latitude" column="latitude" />
|
||||
<result property="altitude" column="altitude" />
|
||||
<result property="speed" column="speed" />
|
||||
<result property="heading" column="heading" />
|
||||
<result property="dataQuality" column="data_quality" />
|
||||
<result property="timestamp" column="timestamp" />
|
||||
<result property="createTime" column="created_at" />
|
||||
<result property="updateTime" column="updated_at" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysVehicleLocationVo">
|
||||
select
|
||||
vl.id,
|
||||
vl.vehicle_id,
|
||||
vi.license_plate,
|
||||
vt.type_name,
|
||||
vi.brand,
|
||||
vi.owning_unit,
|
||||
ST_AsText(vl.location) as location,
|
||||
ST_X(vl.location) as longitude,
|
||||
ST_Y(vl.location) as latitude,
|
||||
vl.altitude,
|
||||
vl.speed,
|
||||
vl.heading,
|
||||
vl.data_quality,
|
||||
vl.timestamp,
|
||||
vl.created_at,
|
||||
vl.updated_at
|
||||
from vehicle_locations vl
|
||||
left join sys_vehicle_info vi on vl.vehicle_id = vi.vehicle_id
|
||||
left join sys_vehicle_type vt on vi.type_id = vt.type_id
|
||||
</sql>
|
||||
|
||||
<select id="selectSysVehicleLocationList" parameterType="SysVehicleLocation" resultMap="SysVehicleLocationResult">
|
||||
<include refid="selectSysVehicleLocationVo"/>
|
||||
<where>
|
||||
<if test="vehicleId != null "> and vl.vehicle_id = #{vehicleId}</if>
|
||||
<if test="licensePlate != null and licensePlate != ''"> and vi.license_plate like concat('%', #{licensePlate}, '%')</if>
|
||||
<if test="vehicleType != null and vehicleType != ''"> and vt.type_name = #{vehicleType}</if>
|
||||
<if test="brand != null and brand != ''"> and vi.brand like concat('%', #{brand}, '%')</if>
|
||||
<if test="owningUnit != null and owningUnit != ''"> and vi.owning_unit like concat('%', #{owningUnit}, '%')</if>
|
||||
<if test="dataQuality != null and dataQuality != ''"> and vl.data_quality = #{dataQuality}</if>
|
||||
<if test="startTime != null and endTime != null "> and vl.timestamp between #{startTime} and #{endTime}</if>
|
||||
<if test="startTime != null and endTime == null "> and vl.timestamp >= #{startTime}</if>
|
||||
<if test="startTime == null and endTime != null "> and vl.timestamp <= #{endTime}</if>
|
||||
</where>
|
||||
order by vl.timestamp desc
|
||||
</select>
|
||||
|
||||
<select id="selectSysVehicleLocationByVehicleId" parameterType="Long" resultMap="SysVehicleLocationResult">
|
||||
<include refid="selectSysVehicleLocationVo"/>
|
||||
where vl.vehicle_id = #{vehicleId}
|
||||
order by vl.timestamp desc
|
||||
</select>
|
||||
|
||||
<select id="selectSysVehicleLocationByLicensePlate" parameterType="String" resultMap="SysVehicleLocationResult">
|
||||
<include refid="selectSysVehicleLocationVo"/>
|
||||
where vi.license_plate = #{licensePlate}
|
||||
order by vl.timestamp desc
|
||||
</select>
|
||||
|
||||
<select id="selectLatestSysVehicleLocationByVehicleId" parameterType="Long" resultMap="SysVehicleLocationResult">
|
||||
<include refid="selectSysVehicleLocationVo"/>
|
||||
where vl.vehicle_id = #{vehicleId}
|
||||
order by vl.timestamp desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectLatestSysVehicleLocationByLicensePlate" parameterType="String" resultMap="SysVehicleLocationResult">
|
||||
<include refid="selectSysVehicleLocationVo"/>
|
||||
where vi.license_plate = #{licensePlate}
|
||||
order by vl.timestamp desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@ -30,11 +30,14 @@ $$ LANGUAGE plpgsql;
|
||||
-- 第三步:创建空间数据核心表
|
||||
-- ============================================
|
||||
|
||||
-- 1. 车辆位置表 (vehicle_locations)
|
||||
-- 1. 车辆位置表 (vehicle_locations) - 规范化版本
|
||||
-- 只存储位置相关数据,车辆基础信息通过关联sys_vehicle_info表获取
|
||||
CREATE TABLE IF NOT EXISTS 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')),
|
||||
|
||||
-- 车辆ID(关联sys_vehicle_info.vehicle_id)
|
||||
-- 注意:存储数字ID,不是车牌号
|
||||
vehicle_id BIGINT NOT NULL,
|
||||
|
||||
-- PostGIS空间字段 (WGS84坐标系)
|
||||
location GEOMETRY(POINT, 4326) NOT NULL,
|
||||
@ -57,10 +60,10 @@ CREATE TABLE IF NOT EXISTS vehicle_locations (
|
||||
|
||||
-- 车辆位置表索引
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_vehicle_id ON vehicle_locations(vehicle_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_vehicle_type ON vehicle_locations(vehicle_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_timestamp ON vehicle_locations(timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_location_gist ON vehicle_locations USING GIST(location);
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_compound ON vehicle_locations(vehicle_id, timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_vehicle_locations_data_quality ON vehicle_locations(data_quality);
|
||||
|
||||
-- 2. 机场区域表 (airport_areas)
|
||||
CREATE TABLE IF NOT EXISTS airport_areas (
|
||||
@ -107,7 +110,7 @@ CREATE INDEX IF NOT EXISTS idx_airport_areas_allowed_vehicles_gin ON airport_are
|
||||
-- 3. 车辆轨迹表 (vehicle_trajectories)
|
||||
CREATE TABLE IF NOT EXISTS vehicle_trajectories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
vehicle_id VARCHAR(50) NOT NULL,
|
||||
vehicle_id BIGINT NOT NULL,
|
||||
trajectory_date DATE NOT NULL,
|
||||
|
||||
-- PostGIS轨迹线
|
||||
@ -255,11 +258,11 @@ CREATE INDEX IF NOT EXISTS idx_rule_violations_location_gist ON rule_violation_e
|
||||
-- 第五步:创建业务关联视图
|
||||
-- ============================================
|
||||
|
||||
-- 车辆完整信息视图(基础信息 + 最新位置)
|
||||
-- 车辆完整信息视图(基础信息 + 最新位置)- 规范化版本
|
||||
CREATE OR REPLACE VIEW vehicle_complete_info AS
|
||||
SELECT
|
||||
vi.vehicle_id,
|
||||
vi.license_plate_number,
|
||||
vi.license_plate,
|
||||
vi.vin_number,
|
||||
vi.type_id,
|
||||
vt.type_name,
|
||||
@ -280,23 +283,21 @@ FROM sys_vehicle_info vi
|
||||
LEFT JOIN sys_vehicle_type vt ON vi.type_id = vt.type_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT * FROM vehicle_locations
|
||||
WHERE vehicle_id = vi.vehicle_id::varchar
|
||||
WHERE vehicle_id = vi.vehicle_id
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1
|
||||
) vl ON true;
|
||||
|
||||
-- 车辆实时状态统计视图
|
||||
-- 车辆实时状态统计视图 - 规范化版本
|
||||
CREATE OR REPLACE VIEW vehicle_status_summary AS
|
||||
SELECT
|
||||
vehicle_type,
|
||||
COUNT(*) as total_vehicles,
|
||||
COUNT(CASE WHEN timestamp > NOW() - INTERVAL '5 minutes' THEN 1 END) as active_vehicles,
|
||||
COUNT(CASE WHEN timestamp <= NOW() - INTERVAL '5 minutes' THEN 1 END) as inactive_vehicles,
|
||||
COUNT(CASE WHEN last_location_time > NOW() - INTERVAL '5 minutes' THEN 1 END) as active_vehicles,
|
||||
COUNT(CASE WHEN last_location_time > NOW() - INTERVAL '30 minutes' AND last_location_time <= NOW() - INTERVAL '5 minutes' THEN 1 END) as inactive_vehicles,
|
||||
COUNT(CASE WHEN last_location_time IS NULL OR last_location_time <= NOW() - INTERVAL '30 minutes' THEN 1 END) as offline_vehicles,
|
||||
AVG(speed) as avg_speed,
|
||||
MAX(speed) as max_speed
|
||||
FROM vehicle_locations vl
|
||||
WHERE timestamp > NOW() - INTERVAL '24 hours'
|
||||
GROUP BY vehicle_type;
|
||||
FROM vehicle_complete_info;
|
||||
|
||||
-- ============================================
|
||||
-- 第六步:创建触发器
|
||||
@ -319,12 +320,24 @@ CREATE TRIGGER trigger_spatial_rules_updated_at
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ============================================
|
||||
-- 第七步:添加外键约束(可选)
|
||||
-- 第七步:添加表注释
|
||||
-- ============================================
|
||||
|
||||
-- 在CollisionAvoidanceSystem的vehicle_locations表和QAUP的sys_vehicle_info表之间建立关联
|
||||
-- 注意:由于vehicle_id类型不同(一个是varchar,一个是bigint),需要类型转换或字段调整
|
||||
-- 这里提供灵活的关联方式,不强制外键约束
|
||||
-- 添加表和视图注释
|
||||
COMMENT ON TABLE vehicle_locations IS '车辆位置信息表(规范化版本)- 只存储位置相关数据,车辆基础信息通过关联sys_vehicle_info表获取';
|
||||
COMMENT ON COLUMN vehicle_locations.vehicle_id IS '车辆ID,关联sys_vehicle_info.vehicle_id(数字主键)';
|
||||
COMMENT ON VIEW vehicle_complete_info IS '车辆完整信息视图 - 包含基础信息和最新位置,通过vehicle_id关联';
|
||||
|
||||
-- ============================================
|
||||
-- 第八步:添加外键约束(可选)
|
||||
-- ============================================
|
||||
|
||||
-- 外键约束:确保vehicle_id在sys_vehicle_info表中存在
|
||||
-- 注意:只有在确保数据完整性的情况下才启用
|
||||
-- ALTER TABLE vehicle_locations
|
||||
-- ADD CONSTRAINT fk_vehicle_locations_vehicle_id
|
||||
-- FOREIGN KEY (vehicle_id) REFERENCES sys_vehicle_info(vehicle_id)
|
||||
-- ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- ============================================
|
||||
-- 第八步:插入示例配置数据
|
||||
|
||||
@ -57,8 +57,12 @@ TRAFFIC_LIGHT_SWITCH_INTERVAL = 10.0 # 红绿灯切换间隔
|
||||
# 根据 route.md 定义的坐标点
|
||||
|
||||
# 飞机 CA1234 路径
|
||||
AIRCRAFT_START = {"longitude": 120.086263, "latitude": 36.370484} # 飞机起点
|
||||
AIRCRAFT_END = {"longitude": 120.080996, "latitude": 36.369105} # 飞机终点
|
||||
CA_START = {"longitude": 120.086263, "latitude": 36.370484} # 飞机起点
|
||||
CA_END = {"longitude": 120.080996, "latitude": 36.369105} # 飞机终点
|
||||
|
||||
# 飞机 MU5123 路径
|
||||
MU_START = {"longitude": 120.088076, "latitude": 36.374179} # 飞机起点
|
||||
MU_END = {"longitude": 120.077971, "latitude": 36.371503} # 飞机终点
|
||||
|
||||
# 特勤车 鲁B123 路径
|
||||
SPECIAL_VEHICLE_START = {"longitude": 120.080801, "latitude": 36.366626} # 特勤车起点
|
||||
@ -80,18 +84,30 @@ UNMANNED_B_END = {"longitude": 120.086263, "latitude": 36.370484} # 无
|
||||
AIRCRAFT_SIZE_M = 30.0
|
||||
VEHICLE_SIZE_M = 10.0
|
||||
|
||||
# 飞机数据 - CA1234,根据 route.md 配置
|
||||
# 飞机数据 - 根据 route.md 配置
|
||||
aircraft_data = [
|
||||
{
|
||||
"flightNo": "CA1234", # 根据route.md更新航班号
|
||||
"longitude": AIRCRAFT_START["longitude"],
|
||||
"latitude": AIRCRAFT_START["latitude"],
|
||||
"longitude": CA_START["longitude"],
|
||||
"latitude": CA_START["latitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"altitude": 0.0,
|
||||
"trackNumber": 1001,
|
||||
"speed": 50.0, # 根据route.md设置为50km/h
|
||||
"start_point": AIRCRAFT_START, # 起点
|
||||
"end_point": AIRCRAFT_END, # 终点
|
||||
"start_point": CA_START, # 起点
|
||||
"end_point": CA_END, # 终点
|
||||
"moving_to_end": True # 当前是否向终点移动
|
||||
},
|
||||
{
|
||||
"flightNo": "MU5123", # 根据route.md更新航班号
|
||||
"longitude": MU_START["longitude"],
|
||||
"latitude": MU_START["latitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"altitude": 0.0,
|
||||
"trackNumber": 1002,
|
||||
"speed": 50.0, # 根据route.md设置为50km/h
|
||||
"start_point": MU_START, # 起点
|
||||
"end_point": MU_END, # 终点
|
||||
"moving_to_end": True # 当前是否向终点移动
|
||||
}
|
||||
]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user