feat: migrate backend to Spring Boot 4
This commit is contained in:
parent
521e3e4e24
commit
bdb6640ad8
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -45,12 +45,3 @@ nbdist/
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
||||
|
||||
bin/
|
||||
ry.bat
|
||||
ry.sh
|
||||
run.sh
|
||||
package.bat
|
||||
sql/quartz.sql
|
||||
|
||||
**/application-local.yml
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<trees>
|
||||
<tree path="/cmvr-iot-system" title="系统模块"/>
|
||||
<tree path="/cmvr-iot-admin" title="后台服务"/>
|
||||
<tree path="/cmvr-iot-common" title="工具类"/>
|
||||
<tree path="/cmvr-iot-framework" title="框架核心组件"/>
|
||||
<tree path="/cmvr-iot-generator" title="代码生成"/>
|
||||
<tree path="/cmvr-iot-quartz" title="定时任务"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/annotation" title="自定义注解"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/config" title="全局配置"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/constant" title="通用常量"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/core" title="核心控制"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/enums" title="通用枚举"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/exception" title="通用异常"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/filter" title="过滤器处理"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/utils" title="通用类处理"/>
|
||||
<tree path="/cmvr-iot-common/src/main/java/com/cmvr/common/xss" title="自定义xss校验注解"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/aspectj" title="注解实现"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/config" title="系统配置"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/datasource" title="数据权限"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/interceptor" title="拦截器"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/manager" title="异步处理"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/security" title="权限控制"/>
|
||||
<tree path="/cmvr-iot-framework/src/main/java/com/cmvr/framework/web" title="前端控制"/>
|
||||
<tree path="/cmvr-iot-device" title="设备管理"/>
|
||||
</trees>
|
||||
50
Dockerfile
50
Dockerfile
@ -1,50 +0,0 @@
|
||||
# ====================== 构建阶段 ======================
|
||||
FROM maven:3.9.9-eclipse-temurin-8 AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 缓存pom
|
||||
COPY pom.xml .
|
||||
COPY cmvr-iot-admin/pom.xml cmvr-iot-admin/
|
||||
COPY cmvr-iot-framework/pom.xml cmvr-iot-framework/
|
||||
COPY cmvr-iot-system/pom.xml cmvr-iot-system/
|
||||
COPY cmvr-iot-quartz/pom.xml cmvr-iot-quartz/
|
||||
COPY cmvr-iot-generator/pom.xml cmvr-iot-generator/
|
||||
COPY cmvr-iot-common/pom.xml cmvr-iot-common/
|
||||
COPY cmvr-iot-device/pom.xml cmvr-iot-device/
|
||||
COPY cmvr-iot-test/pom.xml cmvr-iot-test/
|
||||
COPY cmvr-iot-api/pom.xml cmvr-iot-api/
|
||||
COPY cmvr-iot-vi/pom.xml cmvr-iot-vi/
|
||||
COPY cmvr-iot-ti/pom.xml cmvr-iot-ti/
|
||||
COPY cmvr-iot-evaluation/pom.xml cmvr-iot-evaluation/
|
||||
COPY cmvr-iot-api/cmvr-iot-llm/pom.xml cmvr-iot-api/cmvr-iot-llm/
|
||||
COPY cmvr-iot-api/cmvr-iot-edge/pom.xml cmvr-iot-api/cmvr-iot-edge/
|
||||
COPY cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/pom.xml cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/
|
||||
COPY cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib/pom.xml cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib/
|
||||
|
||||
RUN mvn dependency:go-offline -B --no-transfer-progress
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN mvn clean package -DskipTests -pl cmvr-iot-admin -am --no-transfer-progress
|
||||
|
||||
# ====================== 运行阶段 ======================
|
||||
FROM openjdk:8-jdk-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# -------------------------- 【唯一正确写法】 --------------------------
|
||||
# 1. 先把所有jar复制进来
|
||||
COPY --from=builder /app/cmvr-iot-admin/target/*.jar ./
|
||||
|
||||
# 2. 自动删除不能运行的小jar(只保留可执行jar)
|
||||
RUN find . -name "*.jar" -type f -size -20000k -delete
|
||||
|
||||
# 3. 重命名为app.jar
|
||||
RUN mv *.jar app.jar
|
||||
|
||||
ENTRYPOINT ["java", "-jar", \
|
||||
"-Dspring.profiles.active=dev", \
|
||||
"-Djava.security.egd=file:/dev/./urandom", \
|
||||
"-XX:+UseG1GC", \
|
||||
"-Xms512m", "-Xmx1024m", \
|
||||
"app.jar"]
|
||||
43
Jenkinsfile
vendored
43
Jenkinsfile
vendored
@ -1,43 +0,0 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'mvn clean install -Dmaven.test.skip=true'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy') {
|
||||
parallel {
|
||||
stage('Deploy to DEV') {
|
||||
steps {
|
||||
sh './ry.sh start dev'
|
||||
}
|
||||
}
|
||||
stage('Deploy to TEST') {
|
||||
steps {
|
||||
sh './ry.sh start test'
|
||||
}
|
||||
}
|
||||
stage('Deploy to PROD') {
|
||||
steps {
|
||||
sh './ry.sh start prod'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts artifacts: '**/*.jar', allowEmptyArchive: true
|
||||
}
|
||||
success {
|
||||
echo 'Pipeline completed successfully.'
|
||||
}
|
||||
failure {
|
||||
echo 'Pipeline failed.'
|
||||
}
|
||||
}
|
||||
}
|
||||
332
README.md
332
README.md
@ -1,249 +1,153 @@
|
||||
# cmvr-iot
|
||||
# CMVR IoT 物联网平台
|
||||
|
||||
cmvr-iot 是一个基于 Java 8 和 Spring Boot 2.5 的多模块后端工程,提供设备管理、边缘机器人控制、测试编排、巡检、TTS、AIMA、VI/TI 等业务能力。工程入口在 `cmvr-iot-admin`,其余模块按业务和基础能力拆分。
|
||||
CMVR IoT 是招商车研机器人及智能设备管理平台。本项目以 RuoYi-Vue 3.9.2 为基础升级到 Spring Boot 4,保留原有 REST、前端和 gRPC 对外接口,并将 QUIC Gateway 集成到主应用进程中。
|
||||
|
||||
Java 包名、Maven 坐标统一使用 `com.cmvr`。旧项目 `cmvr-iot` 仅作为迁移来源,不参与本项目构建。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 系统管理:用户、角色、部门、菜单、字典、参数、日志和通知。
|
||||
- 设备管理:设备注册、终端配置、设备档案和机器人配置。
|
||||
- 测试业务:任务配置、任务编排、任务实例、检测项、语音唤醒、Flowise 和 AI 评估。
|
||||
- 智能巡检:机器人、地图、点位、任务、执行实例、巡检结果和告警。
|
||||
- 智能座舱:语音交互、触控交互、TTS、语料库和车机操作配置。
|
||||
- 专项业务:爱玛电动车测试、评价指标库和大模型服务。
|
||||
- 边缘控制:通过 gRPC 调用 AGV、相机、机械臂、灵巧手、机器人头部、麦克风和扬声器。
|
||||
- QUIC 接入:节点注册、心跳、设备快照、媒体 DATAGRAM 和 gRPC 地址同步。
|
||||
- 平台能力:Quartz 定时任务、代码生成、WebSocket、MinIO 文件服务和 Knife4j 接口文档。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Java 8
|
||||
- Spring Boot 2.5.15
|
||||
- Spring Security
|
||||
- MyBatis Plus / MyBatis Plus Join
|
||||
- MySQL / Redis
|
||||
- Druid
|
||||
- Quartz
|
||||
- Knife4j / Swagger
|
||||
- gRPC Java 1.58.0
|
||||
- Protobuf / protoc-maven-plugin
|
||||
- MinIO
|
||||
| 组件 | 版本或实现 |
|
||||
| --- | --- |
|
||||
| Java | 17 |
|
||||
| Spring Boot | 4.0.6 |
|
||||
| Spring Security | JWT 认证 |
|
||||
| MyBatis | MyBatis-Plus 3.5.17、MPJ 1.5.9 |
|
||||
| 数据库 | MySQL |
|
||||
| 缓存 | Redis |
|
||||
| RPC | gRPC 1.58.0、Protobuf 3.25.5 |
|
||||
| QUIC | Netty QUIC 4.2.16.Final |
|
||||
| API 文档 | Springdoc 3.0.3、Knife4j 4.5.0 |
|
||||
| 前端 | Vue 3.4、Element Plus 2.9、Vite 5.3 |
|
||||
|
||||
## 模块说明
|
||||
## 工程结构
|
||||
|
||||
| 模块 | 说明 |
|
||||
| --- | --- |
|
||||
| `cmvr-iot-admin` | Web 启动模块,包含 REST Controller、启动类和运行配置 |
|
||||
| `cmvr-iot-framework` | Web、安全、配置、通用框架能力 |
|
||||
| `cmvr-iot-common` | 公共工具、通用响应、基础常量和通用能力 |
|
||||
| `cmvr-iot-system` | 系统管理、用户、角色、菜单、字典等基础业务 |
|
||||
| `cmvr-iot-quartz` | 定时任务模块 |
|
||||
| `cmvr-iot-generator` | 代码生成模块 |
|
||||
| `cmvr-iot-device` | 设备领域模型与设备相关能力 |
|
||||
| `cmvr-iot-test` | 测试任务、编排、执行实例等业务 |
|
||||
| `cmvr-iot-vi` | VI 相关业务 |
|
||||
| `cmvr-iot-ti` | TI 相关业务 |
|
||||
| `cmvr-iot-evaluation` | 评测相关业务 |
|
||||
| `cmvr-iot-inspection` | 巡检机器人、地图、任务、告警等业务 |
|
||||
| `cmvr-iot-aima` | AIMA 相关业务 |
|
||||
| `cmvr-iot-tts` | TTS 语音合成相关业务 |
|
||||
| `cmvr-iot-api` | API 聚合模块 |
|
||||
| `cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib` | gRPC proto 与生成代码模块 |
|
||||
| `cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client` | 边缘设备 gRPC 客户端、设备控制服务和请求 VO |
|
||||
| `cmvr-iot-admin` | Spring Boot 启动入口、REST 控制器和运行配置 |
|
||||
| `cmvr-iot-framework` | 安全、Web、MyBatis、WebSocket 等框架配置 |
|
||||
| `cmvr-iot-common` | 公共模型、注解、异常和工具类 |
|
||||
| `cmvr-iot-system` | 用户、角色、菜单、字典、参数和通用分组 |
|
||||
| `cmvr-iot-quartz` | Quartz 任务调度和任务日志 |
|
||||
| `cmvr-iot-generator` | 代码生成 |
|
||||
| `cmvr-iot-device` | 设备、终端和机器人配置 |
|
||||
| `cmvr-iot-test` | 测试任务、流程、检测项和执行实例 |
|
||||
| `cmvr-iot-vi` | 语音交互业务 |
|
||||
| `cmvr-iot-ti` | 触控交互业务 |
|
||||
| `cmvr-iot-evaluation` | 评价指标业务 |
|
||||
| `cmvr-iot-inspection` | 智能巡检业务 |
|
||||
| `cmvr-iot-aima` | 爱玛电动车专项业务 |
|
||||
| `cmvr-iot-tts` | TTS 任务和车载语料 |
|
||||
| `cmvr-iot-api` | 边缘服务和大模型 API 聚合模块 |
|
||||
| `cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib` | 原有 gRPC 协议及生成代码 |
|
||||
| `cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client` | 原有机器人 gRPC 客户端 |
|
||||
| `cmvr-iot-quic-contract` | QUIC 控制、节点事件和媒体数据协议 |
|
||||
| `cmvr-iot-quic` | 与主应用同进程运行的 QUIC Gateway |
|
||||
| `ruoyi-ui` | Vue 3 管理控制台 |
|
||||
|
||||
## 目录结构
|
||||
## 环境准备
|
||||
|
||||
```text
|
||||
cmvr-iot
|
||||
├── cmvr-iot-admin # Spring Boot 启动模块
|
||||
├── cmvr-iot-common # 公共模块
|
||||
├── cmvr-iot-framework # 框架模块
|
||||
├── cmvr-iot-system # 系统管理模块
|
||||
├── cmvr-iot-device # 设备模块
|
||||
├── cmvr-iot-test # 测试业务模块
|
||||
├── cmvr-iot-api
|
||||
│ └── cmvr-iot-edge
|
||||
│ ├── cmvr-iot-grpc-lib # proto 与 gRPC 生成代码
|
||||
│ └── cmvr-iot-grpc-client # gRPC 客户端与边缘控制服务
|
||||
├── sql # 增量 SQL
|
||||
├── Dockerfile
|
||||
├── Jenkinsfile
|
||||
└── pom.xml # Maven 父工程
|
||||
```
|
||||
|
||||
## 环境要求
|
||||
|
||||
- JDK 1.8
|
||||
- Maven 3.6+
|
||||
- MySQL 5.7+/8.x
|
||||
- JDK 17
|
||||
- Maven 3.6.3 或更高版本
|
||||
- MySQL 5.7/8.0
|
||||
- Redis
|
||||
- 可访问的 gRPC 机器人/边缘终端服务
|
||||
- 可选:MinIO、外部大模型/TTS/评测服务
|
||||
- Node.js 18/20 LTS 和 pnpm 9
|
||||
- MinIO,仅使用文件、语料或媒体存储功能时需要
|
||||
|
||||
注意:`application-*.yml` 中包含环境相关地址、账号和密钥配置。实际部署或本地开发时,应使用本机配置、环境变量或配置中心覆盖,不要直接复用生产/测试环境敏感配置。
|
||||
默认激活 `test` profile,后端端口为 `13080`。运行前检查以下配置:
|
||||
|
||||
## 配置文件
|
||||
- `cmvr-iot-admin/src/main/resources/application-test.yml`:MySQL、Redis、MinIO、gRPC 和第三方服务。
|
||||
- `cmvr-iot-admin/src/main/resources/application.yml`:上传目录、JWT、MyBatis、Knife4j 和 QUIC。
|
||||
- 不要把生产密码、访问密钥或 TLS 私钥提交到仓库。
|
||||
|
||||
主配置文件位于:
|
||||
## 数据库初始化
|
||||
|
||||
```text
|
||||
cmvr-iot-admin/src/main/resources/application.yml
|
||||
```
|
||||
新数据库以 `sql/ry_20260417.sql` 为基础脚本。按实际启用模块检查并执行以下补充脚本:
|
||||
|
||||
环境配置文件位于:
|
||||
- `sql/quartz.sql`:Quartz 表结构。
|
||||
- `sql/aima_module.sql`:爱玛业务。
|
||||
- `sql/inspection_module.sql`、`inspection_result.sql`、`inspection_detection_alert.sql`:巡检业务。
|
||||
- `sql/tts_corpus_module.sql`:TTS 和语料业务。
|
||||
- `sql/quic_gateway_terminal_migration.sql`:QUIC 节点绑定及运行状态字段。
|
||||
|
||||
```text
|
||||
cmvr-iot-admin/src/main/resources/application-dev.yml
|
||||
cmvr-iot-admin/src/main/resources/application-test.yml
|
||||
cmvr-iot-admin/src/main/resources/application-prod.yml
|
||||
```
|
||||
补充脚本可能面向不同历史库版本。已有数据库升级前应先备份,并确认字段或索引尚未存在。
|
||||
|
||||
当前 `application.yml` 默认激活:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
profiles:
|
||||
active: test
|
||||
```
|
||||
|
||||
如需切换环境,可以修改 `spring.profiles.active`,或启动时指定:
|
||||
|
||||
```bash
|
||||
java -jar cmvr-iot-admin-dev.jar --spring.profiles.active=dev
|
||||
```
|
||||
|
||||
## 构建
|
||||
## 后端运行
|
||||
|
||||
在项目根目录执行:
|
||||
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
java -jar cmvr-iot-admin/target/cmvr-iot-admin.jar
|
||||
```
|
||||
|
||||
只编译启动模块及其依赖:
|
||||
也可以在 IDEA 中直接运行:
|
||||
|
||||
```text
|
||||
com.cmvr.CmvrIotApplication
|
||||
```
|
||||
|
||||
DevTools 自动重启已关闭。修改代码后需要完整停止并重新启动应用,避免 MPJ 保留已经关闭的 Spring 上下文。
|
||||
|
||||
常用地址:
|
||||
|
||||
- 后端 API:`http://localhost:13080`
|
||||
- Knife4j 文档:`http://localhost:13080/doc.html`
|
||||
- OpenAPI JSON:`http://localhost:13080/v3/api-docs/00-all`
|
||||
|
||||
Knife4j 只展示带原有 `@ApiOperation` 注解的正式接口,并沿用中文 `@Api(tags=...)` 业务分类。
|
||||
|
||||
## 前端运行
|
||||
|
||||
```bash
|
||||
mvn -pl cmvr-iot-admin -am compile -DskipTests
|
||||
cd ruoyi-ui
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
只重新生成并编译 gRPC 代码:
|
||||
默认开发端口为 `80`。`.env.development` 中的 `VITE_API_URL` 必须与后端地址一致;本项目后端默认是 `http://localhost:13080`。更多说明见 [ruoyi-ui/README.md](ruoyi-ui/README.md)。
|
||||
|
||||
## gRPC 与 QUIC
|
||||
|
||||
原有机器人 gRPC 客户端与 proto 均保留。终端 gRPC 地址优先读取 `de_device_terminal_config` 的 `host` 和 `port`,没有终端配置时使用当前 profile 中的 `grpc.client.grpc-server.address`。
|
||||
|
||||
QUIC 不是独立服务,`cmvr-iot-quic` 已作为 `cmvr-iot-admin` 的运行时依赖。启用时主应用同时启动:
|
||||
|
||||
- QUIC:默认 `0.0.0.0:4433/udp`
|
||||
- 节点和媒体事件:通过 Spring 单例 `NodeEventBroker` 在进程内直接分发,不占用内部端口
|
||||
|
||||
设置 `cmvr.quic.enabled=true` 前必须配置有效的 TLS 证书和私钥,否则应用会拒绝启动。详细配置见 [cmvr-iot-quic/README.md](cmvr-iot-quic/README.md)。
|
||||
|
||||
## 构建与验证
|
||||
|
||||
```bash
|
||||
mvn -pl cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib -am compile -DskipTests
|
||||
```
|
||||
# 运行测试
|
||||
mvn test
|
||||
|
||||
## 启动
|
||||
|
||||
启动类:
|
||||
|
||||
```text
|
||||
cmvr-iot-admin/src/main/java/com/cmvr/CmvrIotApplication.java
|
||||
```
|
||||
|
||||
IDE 中直接运行 `CmvrIotApplication` 即可。
|
||||
|
||||
打包后运行:
|
||||
|
||||
```bash
|
||||
java -jar cmvr-iot-admin/target/cmvr-iot-admin-dev.jar --spring.profiles.active=dev
|
||||
```
|
||||
|
||||
默认开发端口以当前 profile 配置为准,例如 `application-dev.yml` 中为:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 13080
|
||||
```
|
||||
|
||||
## 接口文档
|
||||
|
||||
项目集成 Knife4j / Swagger。服务启动后可访问:
|
||||
|
||||
```text
|
||||
http://localhost:13080/doc.html
|
||||
```
|
||||
|
||||
如果端口或上下文路径被 profile 覆盖,请以实际配置为准。
|
||||
|
||||
## 数据库脚本
|
||||
|
||||
增量脚本位于:
|
||||
|
||||
```text
|
||||
sql/
|
||||
```
|
||||
|
||||
当前包含:
|
||||
|
||||
- `aima_module.sql`
|
||||
- `inspection_module.sql`
|
||||
- `tts_corpus_module.sql`
|
||||
|
||||
初始化数据库时,需要结合目标环境的基础库结构和增量脚本执行。
|
||||
|
||||
## gRPC 与边缘设备
|
||||
|
||||
边缘设备相关代码集中在:
|
||||
|
||||
```text
|
||||
cmvr-iot-api/cmvr-iot-edge/
|
||||
```
|
||||
|
||||
proto 文件位于:
|
||||
|
||||
```text
|
||||
cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib/src/main/proto/
|
||||
```
|
||||
|
||||
gRPC 客户端服务位于:
|
||||
|
||||
```text
|
||||
cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/
|
||||
```
|
||||
|
||||
边缘控制器位于:
|
||||
|
||||
```text
|
||||
cmvr-iot-admin/src/main/java/com/cmvr/web/controller/api/
|
||||
```
|
||||
|
||||
新增或修改 proto 后,执行 Maven compile 会触发 `protoc-maven-plugin` 生成 Java 和 gRPC Stub 代码。业务代码应复用现有 gRPC 客户端管理工具获取 Stub,避免在业务层重复创建底层 `ManagedChannel`。
|
||||
|
||||
## 实时语音对讲
|
||||
|
||||
当前实时语音对讲链路为:
|
||||
|
||||
```text
|
||||
浏览器 WebSocket Binary PCM <-> Java 后端 <-> gRPC 双向流 <-> 机器人终端
|
||||
```
|
||||
|
||||
约定音频格式:
|
||||
|
||||
- PCM_S16LE
|
||||
- 48000 Hz
|
||||
- 单声道
|
||||
- 16 bit
|
||||
- 10 ms 一帧
|
||||
- 960 bytes/帧
|
||||
|
||||
相关文件:
|
||||
|
||||
- WebSocket 控制器:`cmvr-iot-admin/src/main/java/com/cmvr/web/controller/api/WebRtcSignalingController.java`
|
||||
- 业务会话管理:`cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/service/AudioService.java`
|
||||
- gRPC 音频适配:`cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-client/src/main/java/com/cmvr/edge/client/adapter/RobotAudioGrpcAdapter.java`
|
||||
- proto:`cmvr-iot-api/cmvr-iot-edge/cmvr-iot-grpc-lib/src/main/proto/cmvr/api/robot_audio.proto`
|
||||
|
||||
说明:历史 WebRTC 相关类可能仍保留在源码中,但当前浏览器音频传输链路使用 WebSocket 二进制 PCM。
|
||||
|
||||
## 开发约定
|
||||
|
||||
- Controller 层只做参数接收、基础校验和响应封装。
|
||||
- POST 控制类接口优先使用单个 `@RequestBody VO` 承载完整请求参数。
|
||||
- GET 查询类接口可以使用 query 参数绑定 VO。
|
||||
- 不建议在同一个接口中同时声明通用 VO 和零散业务参数。
|
||||
- gRPC 调用统一通过现有客户端管理类获取 Stub。
|
||||
- proto 修改后必须重新编译并确认生成代码已更新。
|
||||
- 不要提交本地日志、崩溃 dump、临时音频文件和包含真实密钥的配置。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# 编译全部模块
|
||||
mvn clean compile -DskipTests
|
||||
|
||||
# 编译启动模块及依赖
|
||||
mvn -pl cmvr-iot-admin -am compile -DskipTests
|
||||
|
||||
# 打包
|
||||
# 构建全部后端模块
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# 运行指定 profile
|
||||
java -jar cmvr-iot-admin/target/cmvr-iot-admin-dev.jar --spring.profiles.active=dev
|
||||
# 构建前端生产包
|
||||
cd ruoyi-ui
|
||||
pnpm build:prod
|
||||
```
|
||||
|
||||
Windows 下也可以使用 `bin/package.bat`、`bin/run.bat` 和 `bin/clean.bat`。
|
||||
|
||||
## 排查提示
|
||||
|
||||
- `ApplicationContext ... has been closed already`:彻底停止旧 Java 进程后重新启动,不要使用 DevTools 热重启。
|
||||
- `cannot find a NameResolver for static://...`:这是 gRPC target scheme 解析问题,不代表网络不通;检查 `GrpcServiceManager` 的通道创建方式和终端地址格式。
|
||||
- QUIC 启动失败:检查证书、私钥、ALPN、UDP 端口占用和 native library 对应的操作系统架构。
|
||||
- 数据库提示缺少字段:确认基础脚本版本及对应模块迁移脚本已经执行。
|
||||
|
||||
12
bin/clean.bat
Normal file
12
bin/clean.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [信息] 清理工程target生成路径。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
call mvn clean
|
||||
|
||||
pause
|
||||
12
bin/package.bat
Normal file
12
bin/package.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [信息] 打包Web工程,生成war/jar包文件。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
call mvn clean package -Dmaven.test.skip=true
|
||||
|
||||
pause
|
||||
14
bin/run.bat
Normal file
14
bin/run.bat
Normal file
@ -0,0 +1,14 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [<5B><>Ϣ] ʹ<><CAB9>Jar<61><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̡<EFBFBD>
|
||||
echo.
|
||||
|
||||
cd %~dp0
|
||||
cd ../cmvr-iot-admin/target
|
||||
|
||||
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
java -jar %JAVA_OPTS% cmvr-iot-admin.jar
|
||||
|
||||
cd bin
|
||||
pause
|
||||
25
certs/cmvr-quic-ca.crt
Normal file
25
certs/cmvr-quic-ca.crt
Normal file
@ -0,0 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEKzCCApOgAwIBAgIUTtZCyKM8INYxZKkfwbKdpHKLjn4wDQYJKoZIhvcNAQEL
|
||||
BQAwHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENBMB4XDTI2MDcyNDA2NDQy
|
||||
MFoXDTM2MDcyMTA2NDQyMFowHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENB
|
||||
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmq2rHldOobaemqNfWggS
|
||||
OVj3inKy6AYjfgtcXUfKs48DDbpZ9gyEd/YPJXA8C2jGPXpxzgnc7a4UCUVQZ8ah
|
||||
ddoJtFcC+Q6BgjeMVqUdUubu5Y9HpkfU3lvnp4KhzvOeFnkKtrCzYIPa2nK3zLc7
|
||||
uCiuLlB+91KQSRXPFbc6N7H/EAfGmUHIwlZGysAkRN7b2TAoR4C7E96JLVtuUQsS
|
||||
VtlEGpunSfuefFzeeZCMS6avLbB+a8Q6yUzLt6pqnheNsDB+jCCXodlJs5XS1AOB
|
||||
W2GOpGFMj7dLoTD+eBIlAlrhWFcwKjzFmtp6LGl/Jy0O+E99X4TL72oNSOdTrO2O
|
||||
iavXABz9IvWR2BrAyo5AKlTJqO6tmZw77iVti8jYi+HsXIVGQKMYwvv5k0jdHeIZ
|
||||
FaDToUbFPP/zj0m8ZraMy+8eNAhScnx6Zs56fcncBuDti6pT+zKisjV1rH/sFvZY
|
||||
wO1UvJlOEbvXrfYLPp58Aqe/toG2nV45a2Q4+x6AETV1AgMBAAGjYzBhMB0GA1Ud
|
||||
DgQWBBRdHz+g2vVmqghDZi2kEHtZfoyHZzAfBgNVHSMEGDAWgBRdHz+g2vVmqghD
|
||||
Zi2kEHtZfoyHZzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkq
|
||||
hkiG9w0BAQsFAAOCAYEASuEoMFiVcg4iTMxO2kshFTJ6LIrqqGXBn+1j+yQ1ennG
|
||||
mqPMo5fBOe/Kp3YWCnREQWu0+EEPEC9qWgIDOIm3v7ch4mZiW31GUOqae6bjprBe
|
||||
er7ySElKGZ5GefKAq++we19A6WHnNxtNAT9BE1VSKUmkxEsnIkuwd+QMmQ9eaIRm
|
||||
8RvzshWdUcyiBJg07sI3rPzPD/YpnfcYlAa2a0+oXJ3o3zlhdbs2S+9fIl+Y/zFN
|
||||
wBaUV6ZNk0RFzOCA+jUq2jU5Y1pODcot3Mp2jCHzp4uTFyVqsoqc5USRB2TUILCP
|
||||
acSbEjD9GnKaNU4miTcftnC85fnBAiZ5btZkXIs72ij+J3VW7QnbcFQG8bElngoR
|
||||
mWizh6ByPQrsvyT0evJjdjpuOzILsVTAn64m0cYpnlR4g5orcEkIJHv4DzMhpXc2
|
||||
CvpcWwk2vJrUuws/LGxDATTAcqtIUIxAkxffqUsdB/6j8fdykNCvXFwNh/uPcawd
|
||||
4kzh6dr+RFzHSMQ6oN16
|
||||
-----END CERTIFICATE-----
|
||||
40
certs/cmvr-quic-ca.key
Normal file
40
certs/cmvr-quic-ca.key
Normal file
@ -0,0 +1,40 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIG/wIBADANBgkqhkiG9w0BAQEFAASCBukwggblAgEAAoIBgQCaraseV06htp6a
|
||||
o19aCBI5WPeKcrLoBiN+C1xdR8qzjwMNuln2DIR39g8lcDwLaMY9enHOCdztrhQJ
|
||||
RVBnxqF12gm0VwL5DoGCN4xWpR1S5u7lj0emR9TeW+engqHO854WeQq2sLNgg9ra
|
||||
crfMtzu4KK4uUH73UpBJFc8Vtzo3sf8QB8aZQcjCVkbKwCRE3tvZMChHgLsT3okt
|
||||
W25RCxJW2UQam6dJ+558XN55kIxLpq8tsH5rxDrJTMu3qmqeF42wMH6MIJeh2Umz
|
||||
ldLUA4FbYY6kYUyPt0uhMP54EiUCWuFYVzAqPMWa2nosaX8nLQ74T31fhMvvag1I
|
||||
51Os7Y6Jq9cAHP0i9ZHYGsDKjkAqVMmo7q2ZnDvuJW2LyNiL4exchUZAoxjC+/mT
|
||||
SN0d4hkVoNOhRsU8//OPSbxmtozL7x40CFJyfHpmznp9ydwG4O2LqlP7MqKyNXWs
|
||||
f+wW9ljA7VS8mU4Ru9et9gs+nnwCp7+2gbadXjlrZDj7HoARNXUCAwEAAQKCAYAi
|
||||
e49/pNqWghoTItM9xLldWAhdcMsSH1Y3wgweDoRxqbL2U0I9cFZy0OPZBo+YQowZ
|
||||
RgwLcRbz1MBKPc3aSMWTep95uQEkaVe1YjFS2p3yLqH5AstoFjDuPmJjLWPpuVVX
|
||||
sLXS+wsOO+7lDriLdpjlagpEsHTRqbIZXPeM4YtkwbV5SyZ64ZfCPU4sYo/jW6R6
|
||||
43nDUP9Dw3Nk7XJnNlbpDigY33T4sRPIqUJ+qtsf/WGlx6gzWax6Vni+8gqxQlIe
|
||||
80XG6dHCM3UGFEdGF64PB2FuprsPWBSSau7L7I/gagiIuQbhs9VYXOanz1MQizMU
|
||||
ZdK8cqmG/bpP+uY4VQLJ5Lxu87boEllGFHBOYTCjWq0zatzGMt1/DeggLnjgfC90
|
||||
5kOzlxl403j+4K8nqiHoKpAbOatwTxFz1up6XSecsarE1RKsCq8sIH+FjjCfO1Db
|
||||
hBfv2a7/XemdcxHtl9Q/EOSDaAFOUQfFMDAc3rCH7/vonTzaaZq5ucWmjlBsv2EC
|
||||
gcEAz+cH+3x09pXVOnM7zlWrGAQk69Fj/0BGs7g3GFf8ZvI/kkbez+9J1QGItxj+
|
||||
7Dm4Ay0kgxYi3DxFWbPLHTbFpVg2CyL08IQZSXJzokwz2V/HTd1a+OgzgB/dBNDc
|
||||
V+XnxJI89+SI3VhOQZJG625l//lhHWCy2u0ULE7ZlK3ao95L86bSXQHI4dZ2nWXl
|
||||
0f4ISJmm4qkEJAY+DkQeF3UZLumd1NRxy0LBdbYVM/wZIN+sGsRdQqQ9f+ik6+aX
|
||||
YrjhAoHBAL52dJzIPIN1Z/Y9qxUrVLPE7K8Mm8KLaFIl/ztl3TFqaUvErHi66nx3
|
||||
MTwEnrQIBI31OKxB6Or2JLxYMvDTSVRV1K2tY53+K6Q8giWU4x3zFc/svyl35gkd
|
||||
U1bLdfxXqKMLrvlmVWj6/RMoH7vd/aTcidYVvwOZgtL+6RahS4LfZ9qXAyREFAJa
|
||||
VSI0HW5LLiOBLTegztT1CBVe7xb78bWFMiUkxre4XsLZYwFPzXzVPh6HloDcACDL
|
||||
FPwLrfJrFQKBwQCvGDl90UzEnF4v0vsshLQLDvp1bS1VvTGOjPhB1WBq510o+e0P
|
||||
nM1Gyvr0keWo19elPTDCAjOr3kreCHFpEkcVQRyK9o7pvad6Vx0SNDF6wpKdfm7u
|
||||
sMknAC7prmnU0XkH8c3NTTkDiiqmSObXw2u+UK48ysL3ZLIXuvS+pkk8t6yp8Pa8
|
||||
hBNGOJQ/baFH4TXixx1pScWF/YfoBfB9+w4Rl4loxN9tu7QpSgfDd29GY3qUNIsC
|
||||
5EYzYqD7WIJpD6ECgcEAkdIfdenYas14yw5r7ck/EGO00lDU8B3LwRlWUCOtNihC
|
||||
dcAeTFDPNnwLNehTmYKJ+iXFPh04Nqw9c/YTCk651dfg/RfDLTNsNlIdUqirOkLi
|
||||
cE7SDO2/MTtCkzEzI//5HNvVGx0+RyHioMgXg75yc8ZlwYLku9zMTL7dtnXHWmux
|
||||
F6qGvT1iFGsUwxsjbU4iBQzhkbWMpX70sWf9pZs/c7qGqel+OyrtYkENi/ONYAXj
|
||||
iXxFvmKxtmnFpzNJ+lABAoHBAJjs8jfEXJnJP80t93Bxg6NmfKhxUEm4ovcIeR8U
|
||||
lEfb8Ind1ICrBWGVGOAkirTFZgFAAlEjne+lT1FYgf19oC+XbxFdl/e02M8KfflS
|
||||
01+0/t6lEpefiU0dfW0dWtXOPaAVvpNgMkeMgBzB2Yz7ZNUrMypfCGk289qveWxB
|
||||
8UbO+QLjTI+cFvPsmR95Eti/nAjd2fXGeqbR+z+DdQnz4BvXLSCQCw4on2+T+mpw
|
||||
BprrEQmcdeZA8LE1zgcKtkZR1w==
|
||||
-----END PRIVATE KEY-----
|
||||
1
certs/cmvr-quic-ca.srl
Normal file
1
certs/cmvr-quic-ca.srl
Normal file
@ -0,0 +1 @@
|
||||
2C945D70B02014891B6E09D57E377CEFB6D18498
|
||||
22
certs/quic-gateway.crt
Normal file
22
certs/quic-gateway.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDuzCCAiOgAwIBAgIULJRdcLAgFIkbbgnVfjd877bRhJgwDQYJKoZIhvcNAQEL
|
||||
BQAwHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENBMB4XDTI2MDcyNDA2NDQz
|
||||
NloXDTI4MTAyNjA2NDQzNlowGDEWMBQGA1UEAwwNMTkyLjE2OC4wLjIyMjCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdM3i1FYFKqNWJzhfhsD9nRUAuK
|
||||
pzilz5uqCKAt8lKYYC9WnLHOYdiEjcHGnGr02yd6sWFH/LBbxNhzx8M7h4S4izuO
|
||||
bhSlG1EIhkMiojzVD1e3P7YzXdEoVxTCfmMgBZQJG63GNOfzRawFYtEeGv7ndFVw
|
||||
kitCYlyTza5KlBNlWpiNOPmmx4dLTdGLUk8a5TUm0zJ+b/LyzVsWUtr9sxKmKeG8
|
||||
0/77AeiL0hQE3xUt5QROTZjRTVhNHowv410dFMJyIfY4sab8ndc4SIwE9PCKg068
|
||||
4705vbFInBS3eTvQur5VLSZLPatnXGKzCXjci1lIQX2p/QIqADMGFWIN9OECAwEA
|
||||
AaN4MHYwDwYDVR0RBAgwBocEwKgA3jAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAww
|
||||
CgYIKwYBBQUHAwEwHQYDVR0OBBYEFLmoEtsglxm3mXh4l8OF31bt2ZhnMB8GA1Ud
|
||||
IwQYMBaAFF0fP6Da9WaqCENmLaQQe1l+jIdnMA0GCSqGSIb3DQEBCwUAA4IBgQAR
|
||||
eK1mD9rJkzHe4OusimQfcuDQW+0J32e4T/34RHlW+lIj7botFaElXIzO9S80tDwq
|
||||
4d4ozNPKysqgJN9hv/BBMzJpZLwP2XozPaGLTNl1jRTCc9UhFUPrUeu0LbpQGBfC
|
||||
6Ghq42V94zPAw4lnMujnkq8botk21hclbJORQ9kblXP31IdWCgiKSFLy1NTBmQmc
|
||||
IxmR+SldMWYrWGWv/0I85AeMu6HR3+NKHmzDblm1HUHFekyC1f7sypNG+D1r8ab2
|
||||
GSijoCMKHSEOm81Vl/j5bgWQygnnIOhsLOUf2DZO6jC+VZmTKpMEJNHgwk23WBlu
|
||||
FYz/X9p5z9ZucL8aBxegj7G1fI5Ik2O05+LLeqJMfspAm6ZcnRDxCVZWQ+K/dAle
|
||||
fz+gTASzhHTsjEBeiX46LP0L2PVyBiPvNtD66e52LoM5ZigechZq91niLO+ipWdh
|
||||
xq7ryJzmaAEQKPuYaoswriWzJam07ywX9yupGHzBA6vVgGy0jPAsnGCuZXTfDyA=
|
||||
-----END CERTIFICATE-----
|
||||
17
certs/quic-gateway.csr
Normal file
17
certs/quic-gateway.csr
Normal file
@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpDCCAYwCAQAwGDEWMBQGA1UEAwwNMTkyLjE2OC4wLjIyMjCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAKdM3i1FYFKqNWJzhfhsD9nRUAuKpzilz5uq
|
||||
CKAt8lKYYC9WnLHOYdiEjcHGnGr02yd6sWFH/LBbxNhzx8M7h4S4izuObhSlG1EI
|
||||
hkMiojzVD1e3P7YzXdEoVxTCfmMgBZQJG63GNOfzRawFYtEeGv7ndFVwkitCYlyT
|
||||
za5KlBNlWpiNOPmmx4dLTdGLUk8a5TUm0zJ+b/LyzVsWUtr9sxKmKeG80/77AeiL
|
||||
0hQE3xUt5QROTZjRTVhNHowv410dFMJyIfY4sab8ndc4SIwE9PCKg0684705vbFI
|
||||
nBS3eTvQur5VLSZLPatnXGKzCXjci1lIQX2p/QIqADMGFWIN9OECAwEAAaBHMEUG
|
||||
CSqGSIb3DQEJDjE4MDYwDwYDVR0RBAgwBocEwKgA3jAOBgNVHQ8BAf8EBAMCBaAw
|
||||
EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGIcLeCE344z
|
||||
PENI1/oONVHBzMMt5VN0P8jbkJOFgZ3a6AUhfqAmDNBr+8SBym+cX2Y9Q2BsaWAu
|
||||
TKOBN+fs+fh5/NqF0hTNvmXzp89NFK5SlsTjoC21HJvK1HTNuNW8drOxNfWgFW3/
|
||||
gSCutcsWS9hVtYrV2FHQzOvVXvXKfmuTkZE8g92P3BCkvRm+ORxI0QfVS81/Ibnv
|
||||
Yl+t/o2QCphdP+1OWzU6+Pccqi2xCyIc6jqCmh0b01zg265PTTOfICO6WQNtYcOn
|
||||
308avDDpTneGl5vWRVdmmiXPqkc+dCesoguIEDUlerFpDKKY81P2GTpfjki93K7Z
|
||||
2DJPs4P1cU0=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
28
certs/quic-gateway.key
Normal file
28
certs/quic-gateway.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnTN4tRWBSqjVi
|
||||
c4X4bA/Z0VALiqc4pc+bqgigLfJSmGAvVpyxzmHYhI3Bxpxq9NsnerFhR/ywW8TY
|
||||
c8fDO4eEuIs7jm4UpRtRCIZDIqI81Q9Xtz+2M13RKFcUwn5jIAWUCRutxjTn80Ws
|
||||
BWLRHhr+53RVcJIrQmJck82uSpQTZVqYjTj5pseHS03Ri1JPGuU1JtMyfm/y8s1b
|
||||
FlLa/bMSpinhvNP++wHoi9IUBN8VLeUETk2Y0U1YTR6ML+NdHRTCciH2OLGm/J3X
|
||||
OEiMBPTwioNOvOO9Ob2xSJwUt3k70Lq+VS0mSz2rZ1xiswl43ItZSEF9qf0CKgAz
|
||||
BhViDfThAgMBAAECggEAC3+johwrdIcNFAMRZUuFE+EYZbCuEV1JHX90aBD8X5uo
|
||||
Obyz2J8xF6z1osjEMbA6W1ZqGF/2oMbv6robc+JUrEU0UZ+2lm3qKJnDw5u+55wz
|
||||
uaFHTF4vr6tcf9llzymRZJUzLTCLLJmKc3orymhtrdEp7GDoNPezM753Y6gt0P7Z
|
||||
AzePK3d7YmEAcRkIDVtqQXyma4CPziItJrcF4Lumww6PxRzqYNb6jB8wsmf0B91U
|
||||
iTtnJQoJfkOGTl5OyPp2s7A+qXqToIqAbIlzcF1Q7i0kohmhZHHIB8Qdk9O8IGb6
|
||||
kZuLbTIdIhTG2vyi8nqz+oMh6D+AJBy+GTLlK0LWCQKBgQDVIrGu9J76R7D3npRy
|
||||
3okc6mVHo2InZaOeai4ZAe/CFVPcHecq7+bx06MxTqge0XYJd5UbO8qfRL5mRc8g
|
||||
JwjUrnCzGvJHFavc3Xs221JgWAaNQ0+PtBKCLHQwbmu8V3NZHXcjyonMuQ2g2oOm
|
||||
TYY7Adedci0SgSFlUy9TjifH6QKBgQDI8lcoojzmYkj0O8oO3dr01XbAEl5pxjyB
|
||||
IsEpal6J8gdbbj8vzdWaGoHs/IoLPtwWbQGx7gUJfYtKp/DORw0WDFJmqrm/PGfh
|
||||
RrLKasjp3GC06BU3GmnoKZxQM8PzY0ELWZsQM0fa+ROC1vMrNcHx5nZvcBwrkKXu
|
||||
CEw2G4miOQKBgDMcUsfF2anjL0jyOl8hhnfsrcT0JUGVWhtPEybkE/7S5n1OnnTp
|
||||
etRbEDNuzVo2mLvEdXlGaVywO4iDDRyEv9Vgz53jxF3PkJsyca3CAosgENInZhjr
|
||||
8dRNnL9Qe3166ypfqoZmt4zkg6QjQpbK2HmmA3K6QpzBYqTNwfeji16hAoGBAKO3
|
||||
pkFp5TpC63NpXxe7s4/0dxrIHHoLU/JWcZAfpMS5Fztr2DlHd3dlj2OY4ZOF9GSl
|
||||
DVN9PEdeLOV3bJ9zjfrfsScKg+mF+pC1PCfk1qqKrZPW3qoVcCIB/OPvOQAMZssr
|
||||
h0Wssid69WWNYXNcChbfELF8Yscvw7Dqu8T9rKYpAoGAByn1Vfu95lAxQ36fxXnk
|
||||
3CC9FkGcdfKGTqkFZHJWiJqaLL6eAbCwO38kywYZLANoSz03Qu3MmD0Gt0wyeP6I
|
||||
a3o0goisV8s5W9q1P1i1K73xlbAPf54QsqoPNuXR8n67I9IzzL7EkPJiUsHuqNHW
|
||||
Ffn4lzHEAxaTksNIaC6oIlo=
|
||||
-----END PRIVATE KEY-----
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>cmvr-iot</artifactId>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
@ -24,16 +24,20 @@
|
||||
<optional>true</optional> <!-- 表示依赖不会传递 -->
|
||||
</dependency>
|
||||
|
||||
<!-- Mysql驱动包 -->
|
||||
<!-- spring-doc -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试管理-->
|
||||
<!-- Mysql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-test</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块-->
|
||||
@ -54,73 +58,72 @@
|
||||
<artifactId>cmvr-iot-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 设备管理-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-device</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 语音交互-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-vi</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 触控交互-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-ti</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AI评估-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-evaluation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 智能巡检-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-inspection</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 爱玛电动车测试-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-aima</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 智能座舱语音语料采集-->
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-tts</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-quic</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<properties>
|
||||
<env>dev</env>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Spring Boot Maven 插件 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.5.15</version>
|
||||
<configuration>
|
||||
<mainClass>com.cmvr.CmvrIotApplication</mainClass>
|
||||
<fork>true</fork> <!-- 如果没有该配置, devtools 不会生效 -->
|
||||
<addResources>true</addResources>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>${project.artifactId}-${spring.profiles.active}</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<warName>${project.artifactId}</warName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
package com.cmvr;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
@ -12,13 +11,13 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
* @author cmvr-iot
|
||||
*/
|
||||
@EnableScheduling
|
||||
@MapperScan("com.cmvr.**.mapper")
|
||||
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
|
||||
public class CmvrIotApplication
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// System.setProperty("spring.devtools.restart.enabled", "false");
|
||||
// MPJ keeps its Spring context in a static holder that is not refreshed by DevTools.
|
||||
System.setProperty("spring.devtools.restart.enabled", "false");
|
||||
SpringApplication.run(CmvrIotApplication.class, args);
|
||||
System.out.println("招商车研物联网平台启动成功");
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.aima;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package com.cmvr.web.controller.api;
|
||||
|
||||
import com.cmvr.edge.client.service.AudioService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@ -3,9 +3,9 @@ package com.cmvr.web.controller.common;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -23,7 +23,7 @@ import com.cmvr.system.service.ISysConfigService;
|
||||
/**
|
||||
* 验证码操作处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
public class CaptchaController
|
||||
|
||||
@ -1,38 +1,40 @@
|
||||
package com.cmvr.web.controller.common;
|
||||
|
||||
import com.cmvr.common.config.CmvrIotConfig;
|
||||
import com.cmvr.common.constant.Constants;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.common.utils.file.FileUploadUtils;
|
||||
import com.cmvr.common.utils.file.FileUtils;
|
||||
import com.cmvr.framework.config.ServerConfig;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.cmvr.common.config.CmvrIotConfig;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.common.utils.file.FileUploadUtils;
|
||||
import com.cmvr.common.utils.file.FileUtils;
|
||||
import com.cmvr.framework.config.ServerConfig;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "通用请求处理")
|
||||
/**
|
||||
* 通用请求处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/common")
|
||||
@RequiredArgsConstructor
|
||||
public class CommonController {
|
||||
public class CommonController
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
@Autowired
|
||||
private ServerConfig serverConfig;
|
||||
|
||||
private static final String FILE_DELIMETER = ",";
|
||||
private static final String FILE_DELIMITER = ",";
|
||||
|
||||
/**
|
||||
* 通用下载请求
|
||||
@ -41,9 +43,12 @@ public class CommonController {
|
||||
* @param delete 是否删除
|
||||
*/
|
||||
@GetMapping("/download")
|
||||
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) {
|
||||
try {
|
||||
if (!FileUtils.checkAllowDownload(fileName)) {
|
||||
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FileUtils.checkAllowDownload(fileName))
|
||||
{
|
||||
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
|
||||
}
|
||||
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
|
||||
@ -52,10 +57,13 @@ public class CommonController {
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
FileUtils.setAttachmentResponseHeader(response, realFileName);
|
||||
FileUtils.writeBytes(filePath, response.getOutputStream());
|
||||
if (delete) {
|
||||
if (delete)
|
||||
{
|
||||
FileUtils.deleteFile(filePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("下载文件失败", e);
|
||||
}
|
||||
}
|
||||
@ -64,8 +72,10 @@ public class CommonController {
|
||||
* 通用上传请求(单个)
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
public AjaxResult uploadFile(MultipartFile file) throws Exception {
|
||||
try {
|
||||
public AjaxResult uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// 上传文件路径
|
||||
String filePath = CmvrIotConfig.getUploadPath();
|
||||
// 上传并返回新文件名称
|
||||
@ -77,7 +87,9 @@ public class CommonController {
|
||||
ajax.put("newFileName", FileUtils.getName(fileName));
|
||||
ajax.put("originalFilename", file.getOriginalFilename());
|
||||
return ajax;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -86,15 +98,18 @@ public class CommonController {
|
||||
* 通用上传请求(多个)
|
||||
*/
|
||||
@PostMapping("/uploads")
|
||||
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception {
|
||||
try {
|
||||
public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
// 上传文件路径
|
||||
String filePath = CmvrIotConfig.getUploadPath();
|
||||
List<String> urls = new ArrayList<String>();
|
||||
List<String> fileNames = new ArrayList<String>();
|
||||
List<String> newFileNames = new ArrayList<String>();
|
||||
List<String> originalFilenames = new ArrayList<String>();
|
||||
for (MultipartFile file : files) {
|
||||
for (MultipartFile file : files)
|
||||
{
|
||||
// 上传并返回新文件名称
|
||||
String fileName = FileUploadUtils.upload(filePath, file);
|
||||
String url = serverConfig.getUrl() + fileName;
|
||||
@ -104,12 +119,14 @@ public class CommonController {
|
||||
originalFilenames.add(file.getOriginalFilename());
|
||||
}
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
|
||||
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
|
||||
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
|
||||
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
|
||||
ajax.put("urls", StringUtils.join(urls, FILE_DELIMITER));
|
||||
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMITER));
|
||||
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMITER));
|
||||
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMITER));
|
||||
return ajax;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -119,21 +136,26 @@ public class CommonController {
|
||||
*/
|
||||
@GetMapping("/download/resource")
|
||||
public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
try {
|
||||
if (!FileUtils.checkAllowDownload(resource)) {
|
||||
throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FileUtils.checkAllowDownload(resource))
|
||||
{
|
||||
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
|
||||
}
|
||||
// 本地资源路径
|
||||
String localPath = CmvrIotConfig.getProfile();
|
||||
// 数据库资源地址
|
||||
String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
|
||||
String downloadPath = localPath + FileUtils.stripPrefix(resource);
|
||||
// 下载名称
|
||||
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
FileUtils.setAttachmentResponseHeader(response, downloadName);
|
||||
FileUtils.writeBytes(downloadPath, response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("下载文件失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.device;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.cmvr.edge.client.service.EdgeSystemService;
|
||||
import io.swagger.annotations.Api;
|
||||
|
||||
@ -15,7 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.device;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.cmvr.common.annotation.Log;
|
||||
import com.cmvr.common.core.controller.BaseController;
|
||||
|
||||
@ -11,7 +11,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Api(tags = "评价管理--指标库")
|
||||
@RestController
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -5,7 +5,7 @@ import com.cmvr.inspection.domain.dto.alert.AlertEnvelope;
|
||||
import com.cmvr.inspection.exception.DetectionAlertBadRequestException;
|
||||
import com.cmvr.inspection.service.IInspectionDetectionAlertService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -18,8 +18,8 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -28,7 +28,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import cmvr.msgs.Agv;
|
||||
import com.cmvr.inspection.domain.vo.InspectionRobotVo;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.cmvr.inspection.domain.dto.InspectionTaskInstanceQuery;
|
||||
import io.swagger.annotations.Api;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.inspection;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@ -25,7 +25,7 @@ import com.cmvr.system.domain.SysCache;
|
||||
/**
|
||||
* 缓存监控
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/cache")
|
||||
@ -45,6 +45,7 @@ public class CacheController
|
||||
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
|
||||
@GetMapping()
|
||||
public AjaxResult getInfo() throws Exception
|
||||
|
||||
@ -10,7 +10,7 @@ import com.cmvr.framework.web.domain.Server;
|
||||
/**
|
||||
* 服务器监控
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/server")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.monitor;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@ -23,7 +23,7 @@ import com.cmvr.system.service.ISysLogininforService;
|
||||
/**
|
||||
* 系统访问记录
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/logininfor")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.monitor;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@ -22,7 +22,7 @@ import com.cmvr.system.service.ISysOperLogService;
|
||||
/**
|
||||
* 操作日志记录
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/operlog")
|
||||
|
||||
@ -26,7 +26,7 @@ import com.cmvr.system.service.ISysUserOnlineService;
|
||||
/**
|
||||
* 在线用户监控
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/online")
|
||||
|
||||
@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
|
||||
@Api(tags = "演示--智能座舱")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -25,7 +25,7 @@ import com.cmvr.system.service.ISysConfigService;
|
||||
/**
|
||||
* 参数配置 信息操作处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/config")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@ -25,7 +26,7 @@ import com.cmvr.system.service.ISysDeptService;
|
||||
/**
|
||||
* 部门信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/dept")
|
||||
@ -110,6 +111,20 @@ public class SysDeptController extends BaseController
|
||||
return toAjax(deptService.updateDept(dept));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存部门排序
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
|
||||
@Log(title = "保存部门排序", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updateSort")
|
||||
public AjaxResult updateSort(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String[] deptIds = params.get("deptIds").split(",");
|
||||
String[] orderNums = params.get("orderNums").split(",");
|
||||
deptService.updateDeptSort(deptIds, orderNums);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*/
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -28,7 +28,7 @@ import com.cmvr.system.service.ISysDictTypeService;
|
||||
/**
|
||||
* 数据字典信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/dict/data")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -25,7 +25,7 @@ import com.cmvr.system.service.ISysDictTypeService;
|
||||
/**
|
||||
* 数据字典信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/dict/type")
|
||||
|
||||
@ -1,22 +1,32 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.cmvr.common.config.CmvrIotConfig;
|
||||
import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.domain.entity.SysUser;
|
||||
import com.cmvr.common.utils.SecurityUtils;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
* 首页
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
public class SysIndexController
|
||||
{
|
||||
/** 系统基础配置 */
|
||||
@Autowired
|
||||
private CmvrIotConfig cmvrIotConfig;
|
||||
private CmvrIotConfig ruoyiConfig;
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
@ -24,6 +34,31 @@ public class SysIndexController
|
||||
@RequestMapping("/")
|
||||
public String index()
|
||||
{
|
||||
return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", cmvrIotConfig.getName(), cmvrIotConfig.getVersion());
|
||||
return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁屏幕
|
||||
*/
|
||||
@PostMapping("/unlockscreen")
|
||||
public AjaxResult unlockScreen(@RequestBody Map<String, String> body)
|
||||
{
|
||||
String password = body.get("password");
|
||||
if (StringUtils.isEmpty(password))
|
||||
{
|
||||
return AjaxResult.error("密码不能为空");
|
||||
}
|
||||
String username = SecurityUtils.getUsername();
|
||||
SysUser user = userService.selectUserByUserName(username);
|
||||
if (user == null)
|
||||
{
|
||||
return AjaxResult.error("服务器超时,请重新登录");
|
||||
}
|
||||
if (!SecurityUtils.matchesPassword(password, user.getPassword()))
|
||||
{
|
||||
return AjaxResult.error("密码错误,请重新输入");
|
||||
}
|
||||
|
||||
return AjaxResult.success("解锁成功");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -16,18 +14,21 @@ import com.cmvr.common.core.domain.entity.SysMenu;
|
||||
import com.cmvr.common.core.domain.entity.SysUser;
|
||||
import com.cmvr.common.core.domain.model.LoginBody;
|
||||
import com.cmvr.common.core.domain.model.LoginUser;
|
||||
import com.cmvr.common.core.text.Convert;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
import com.cmvr.common.utils.SecurityUtils;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.framework.web.service.SysLoginService;
|
||||
import com.cmvr.framework.web.service.SysPermissionService;
|
||||
import com.cmvr.framework.web.service.TokenService;
|
||||
import com.cmvr.system.service.ISysConfigService;
|
||||
import com.cmvr.system.service.ISysMenuService;
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Api(tags = "通用--登录验证")
|
||||
@RestController
|
||||
public class SysLoginController
|
||||
{
|
||||
@ -43,13 +44,15 @@ public class SysLoginController
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
*
|
||||
* @param loginBody 登录信息
|
||||
* @return 结果
|
||||
*/
|
||||
@ApiOperation("登录")
|
||||
@PostMapping("/login")
|
||||
public AjaxResult login(@RequestBody LoginBody loginBody)
|
||||
{
|
||||
@ -84,6 +87,9 @@ public class SysLoginController
|
||||
ajax.put("user", user);
|
||||
ajax.put("roles", roles);
|
||||
ajax.put("permissions", permissions);
|
||||
ajax.put("pwdChrtype", getSysAccountChrtype());
|
||||
ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
|
||||
ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
|
||||
return ajax;
|
||||
}
|
||||
|
||||
@ -99,4 +105,34 @@ public class SysLoginController
|
||||
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
|
||||
return AjaxResult.success(menuService.buildMenus(menus));
|
||||
}
|
||||
|
||||
// 获取用户密码自定义配置规则
|
||||
public String getSysAccountChrtype()
|
||||
{
|
||||
return Convert.toStr(configService.selectConfigByKey("sys.account.chrtype"), "0");
|
||||
}
|
||||
|
||||
// 检查初始密码是否提醒修改
|
||||
public boolean initPasswordIsModify(Date pwdUpdateDate)
|
||||
{
|
||||
Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
|
||||
return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
|
||||
}
|
||||
|
||||
// 检查密码是否过期
|
||||
public boolean passwordIsExpiration(Date pwdUpdateDate)
|
||||
{
|
||||
Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
|
||||
if (passwordValidateDays != null && passwordValidateDays > 0)
|
||||
{
|
||||
if (StringUtils.isNull(pwdUpdateDate))
|
||||
{
|
||||
// 如果从未修改过初始密码,直接提醒过期
|
||||
return true;
|
||||
}
|
||||
Date nowDate = DateUtils.getNowDate();
|
||||
return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -24,7 +25,7 @@ import com.cmvr.system.service.ISysMenuService;
|
||||
/**
|
||||
* 菜单信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/menu")
|
||||
@ -93,6 +94,10 @@ public class SysMenuController extends BaseController
|
||||
{
|
||||
return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
}
|
||||
else if (!menuService.checkRouteConfigUnique(menu))
|
||||
{
|
||||
return error("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
|
||||
}
|
||||
menu.setCreateBy(getUsername());
|
||||
return toAjax(menuService.insertMenu(menu));
|
||||
}
|
||||
@ -117,10 +122,28 @@ public class SysMenuController extends BaseController
|
||||
{
|
||||
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
|
||||
}
|
||||
else if (!menuService.checkRouteConfigUnique(menu))
|
||||
{
|
||||
return error("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
|
||||
}
|
||||
menu.setUpdateBy(getUsername());
|
||||
return toAjax(menuService.updateMenu(menu));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存菜单排序
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
|
||||
@Log(title = "保存菜单排序", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updateSort")
|
||||
public AjaxResult updateSort(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String[] menuIds = params.get("menuIds").split(",");
|
||||
String[] orderNums = params.get("orderNums").split(",");
|
||||
menuService.updateMenuSort(menuIds, orderNums);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*/
|
||||
|
||||
@ -11,6 +11,7 @@ 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 com.cmvr.common.annotation.Log;
|
||||
import com.cmvr.common.core.controller.BaseController;
|
||||
@ -18,12 +19,11 @@ import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.page.TableDataInfo;
|
||||
import com.cmvr.common.enums.BusinessType;
|
||||
import com.cmvr.system.domain.SysNotice;
|
||||
import com.cmvr.system.domain.SysNoticeRead;
|
||||
import com.cmvr.system.service.ISysNoticeService;
|
||||
|
||||
/**
|
||||
* 公告 信息操作处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* 公告信息操作处理。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/notice")
|
||||
@ -32,9 +32,6 @@ public class SysNoticeController extends BaseController
|
||||
@Autowired
|
||||
private ISysNoticeService noticeService;
|
||||
|
||||
/**
|
||||
* 获取通知公告列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(SysNotice notice)
|
||||
@ -44,9 +41,37 @@ public class SysNoticeController extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据通知公告编号获取详细信息
|
||||
*/
|
||||
@GetMapping("/listTop")
|
||||
public AjaxResult listTop()
|
||||
{
|
||||
Long userId = getUserId();
|
||||
AjaxResult result = success(noticeService.selectNoticeTop(userId));
|
||||
result.put("unreadCount", noticeService.countUnreadNotice(userId));
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/markRead")
|
||||
public AjaxResult markRead(@RequestParam Long noticeId)
|
||||
{
|
||||
noticeService.markNoticeRead(noticeId, getUserId());
|
||||
return success();
|
||||
}
|
||||
|
||||
@PostMapping("/markReadAll")
|
||||
public AjaxResult markReadAll(@RequestParam Long[] ids)
|
||||
{
|
||||
noticeService.markNoticeReadAll(ids, getUserId());
|
||||
return success();
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:list')")
|
||||
@GetMapping("/readUsers/list")
|
||||
public TableDataInfo readUsers(SysNoticeRead noticeRead)
|
||||
{
|
||||
startPage();
|
||||
return getDataTable(noticeService.selectNoticeReadUsers(noticeRead));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:query')")
|
||||
@GetMapping(value = "/{noticeId}")
|
||||
public AjaxResult getInfo(@PathVariable Long noticeId)
|
||||
@ -54,9 +79,6 @@ public class SysNoticeController extends BaseController
|
||||
return success(noticeService.selectNoticeById(noticeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增通知公告
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:add')")
|
||||
@Log(title = "通知公告", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@ -66,9 +88,6 @@ public class SysNoticeController extends BaseController
|
||||
return toAjax(noticeService.insertNotice(notice));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改通知公告
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
|
||||
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@ -78,9 +97,6 @@ public class SysNoticeController extends BaseController
|
||||
return toAjax(noticeService.updateNotice(notice));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知公告
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:notice:remove')")
|
||||
@Log(title = "通知公告", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{noticeIds}")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -25,7 +25,7 @@ import com.cmvr.system.service.ISysPostService;
|
||||
/**
|
||||
* 岗位信息操作处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/post")
|
||||
|
||||
@ -17,9 +17,11 @@ import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.domain.entity.SysUser;
|
||||
import com.cmvr.common.core.domain.model.LoginUser;
|
||||
import com.cmvr.common.enums.BusinessType;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
import com.cmvr.common.utils.SecurityUtils;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.common.utils.file.FileUploadUtils;
|
||||
import com.cmvr.common.utils.file.FileUtils;
|
||||
import com.cmvr.common.utils.file.MimeTypeUtils;
|
||||
import com.cmvr.framework.web.service.TokenService;
|
||||
import com.cmvr.system.service.ISysUserService;
|
||||
@ -27,7 +29,7 @@ import com.cmvr.system.service.ISysUserService;
|
||||
/**
|
||||
* 个人信息 业务处理
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/user/profile")
|
||||
@ -93,8 +95,9 @@ public class SysProfileController extends BaseController
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String userName = loginUser.getUsername();
|
||||
String password = loginUser.getPassword();
|
||||
Long userId = loginUser.getUserId();
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
String password = user.getPassword();
|
||||
if (!SecurityUtils.matchesPassword(oldPassword, password))
|
||||
{
|
||||
return error("修改密码失败,旧密码错误");
|
||||
@ -104,9 +107,10 @@ public class SysProfileController extends BaseController
|
||||
return error("新密码不能与旧密码相同");
|
||||
}
|
||||
newPassword = SecurityUtils.encryptPassword(newPassword);
|
||||
if (userService.resetUserPwd(userName, newPassword) > 0)
|
||||
if (userService.resetUserPwd(userId, newPassword) > 0)
|
||||
{
|
||||
// 更新缓存用户密码
|
||||
// 更新缓存用户密码&密码最后更新时间
|
||||
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
|
||||
loginUser.getUser().setPassword(newPassword);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
@ -124,9 +128,14 @@ public class SysProfileController extends BaseController
|
||||
if (!file.isEmpty())
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
String avatar = FileUploadUtils.upload(CmvrIotConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
|
||||
String avatar = FileUploadUtils.upload(CmvrIotConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
|
||||
if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
|
||||
{
|
||||
String oldAvatar = loginUser.getUser().getAvatar();
|
||||
if (StringUtils.isNotEmpty(oldAvatar))
|
||||
{
|
||||
FileUtils.deleteFile(CmvrIotConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
|
||||
}
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("imgUrl", avatar);
|
||||
// 更新缓存用户头像
|
||||
|
||||
@ -14,7 +14,7 @@ import com.cmvr.system.service.ISysConfigService;
|
||||
/**
|
||||
* 注册验证
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
public class SysRegisterController extends BaseController
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@ -19,10 +19,8 @@ import com.cmvr.common.core.domain.AjaxResult;
|
||||
import com.cmvr.common.core.domain.entity.SysDept;
|
||||
import com.cmvr.common.core.domain.entity.SysRole;
|
||||
import com.cmvr.common.core.domain.entity.SysUser;
|
||||
import com.cmvr.common.core.domain.model.LoginUser;
|
||||
import com.cmvr.common.core.page.TableDataInfo;
|
||||
import com.cmvr.common.enums.BusinessType;
|
||||
import com.cmvr.common.utils.StringUtils;
|
||||
import com.cmvr.common.utils.poi.ExcelUtil;
|
||||
import com.cmvr.framework.web.service.SysPermissionService;
|
||||
import com.cmvr.framework.web.service.TokenService;
|
||||
@ -34,7 +32,7 @@ import com.cmvr.system.service.ISysUserService;
|
||||
/**
|
||||
* 角色信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/role")
|
||||
@ -128,14 +126,8 @@ public class SysRoleController extends BaseController
|
||||
|
||||
if (roleService.updateRole(role) > 0)
|
||||
{
|
||||
// 更新缓存用户权限
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
|
||||
{
|
||||
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
|
||||
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
|
||||
tokenService.setLoginUser(loginUser);
|
||||
}
|
||||
// 刷新所有持有该角色的在线用户权限
|
||||
tokenService.refreshPermissionByRoleId(role.getRoleId(), permissionService);
|
||||
return success();
|
||||
}
|
||||
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@ -35,7 +35,7 @@ import com.cmvr.system.service.ISysUserService;
|
||||
/**
|
||||
* 用户信息
|
||||
*
|
||||
* @author cmvr-iot
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/user")
|
||||
@ -111,7 +111,7 @@ public class SysUserController extends BaseController
|
||||
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
|
||||
}
|
||||
List<SysRole> roles = roleService.selectRoleAll();
|
||||
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
ajax.put("posts", postService.selectPostAll());
|
||||
return ajax;
|
||||
}
|
||||
@ -226,7 +226,7 @@ public class SysUserController extends BaseController
|
||||
SysUser user = userService.selectUserById(userId);
|
||||
List<SysRole> roles = roleService.selectRolesByUserId(userId);
|
||||
ajax.put("user", user);
|
||||
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
ajax.put("roles", SecurityUtils.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
return ajax;
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "测试--检测项配置")
|
||||
|
||||
@ -30,8 +30,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
|
||||
@ -14,8 +14,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "测试--任务编排")
|
||||
|
||||
@ -15,7 +15,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
||||
@ -18,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,207 @@
|
||||
package com.cmvr.web.core.config;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.core.converter.AnnotatedType;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springdoc.core.customizers.OpenApiCustomizer;
|
||||
import org.springdoc.core.customizers.OperationCustomizer;
|
||||
import org.springdoc.core.customizers.PropertyCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
/** OpenAPI metadata, authorization and business-domain groups. */
|
||||
@Configuration
|
||||
public class OpenApiConfig
|
||||
{
|
||||
private static final String AUTHORIZATION = "Authorization";
|
||||
|
||||
@Bean
|
||||
public OpenAPI cmvrOpenApi()
|
||||
{
|
||||
SecurityScheme bearerAuth = new SecurityScheme()
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT");
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("CMVR IoT API")
|
||||
.description("招商车研物联网平台接口文档")
|
||||
.version("1.0.0"))
|
||||
.components(new Components().addSecuritySchemes(AUTHORIZATION, bearerAuth))
|
||||
.addSecurityItem(new SecurityRequirement().addList(AUTHORIZATION));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi allApi()
|
||||
{
|
||||
return group("00-all", "全部接口", "/**");
|
||||
}
|
||||
|
||||
private GroupedOpenApi group(String id, String displayName, String... paths)
|
||||
{
|
||||
return GroupedOpenApi.builder()
|
||||
.group(id)
|
||||
.displayName(displayName)
|
||||
.pathsToMatch(paths)
|
||||
// Keep the same visibility rule as the legacy Swagger Docket.
|
||||
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(ApiOperation.class))
|
||||
.addOperationCustomizer(legacySwaggerOperationCustomizer())
|
||||
.addOpenApiCustomizer(legacyTagOrderCustomizer())
|
||||
.build();
|
||||
}
|
||||
|
||||
private OpenApiCustomizer legacyTagOrderCustomizer()
|
||||
{
|
||||
return openApi -> {
|
||||
Set<String> tagNames = new TreeSet<>();
|
||||
if (openApi.getPaths() != null)
|
||||
{
|
||||
openApi.getPaths().values().forEach(pathItem -> pathItem.readOperations().forEach(operation -> {
|
||||
if (operation.getTags() != null)
|
||||
{
|
||||
tagNames.addAll(operation.getTags());
|
||||
}
|
||||
}));
|
||||
}
|
||||
openApi.setTags(tagNames.stream()
|
||||
.map(name -> new io.swagger.v3.oas.models.tags.Tag().name(name))
|
||||
.collect(Collectors.toList()));
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OperationCustomizer legacySwaggerOperationCustomizer()
|
||||
{
|
||||
return this::customizeLegacyOperation;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertyCustomizer legacySwaggerPropertyCustomizer()
|
||||
{
|
||||
return (schema, annotatedType) -> {
|
||||
ApiModelProperty property = findAnnotation(annotatedType, ApiModelProperty.class);
|
||||
if (property != null)
|
||||
{
|
||||
if (StringUtils.hasText(property.value()))
|
||||
{
|
||||
schema.setDescription(property.value());
|
||||
}
|
||||
if (StringUtils.hasText(property.example()))
|
||||
{
|
||||
schema.setExample(property.example());
|
||||
}
|
||||
}
|
||||
return schema;
|
||||
};
|
||||
}
|
||||
|
||||
private Operation customizeLegacyOperation(Operation operation, HandlerMethod handlerMethod)
|
||||
{
|
||||
Api api = handlerMethod.getBeanType().getAnnotation(Api.class);
|
||||
if (api != null)
|
||||
{
|
||||
List<String> tags = Arrays.stream(api.tags())
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toList());
|
||||
if (tags.isEmpty() && StringUtils.hasText(api.value()))
|
||||
{
|
||||
tags.add(api.value());
|
||||
}
|
||||
if (!tags.isEmpty())
|
||||
{
|
||||
operation.setTags(tags);
|
||||
}
|
||||
}
|
||||
|
||||
ApiOperation apiOperation = handlerMethod.getMethodAnnotation(ApiOperation.class);
|
||||
if (apiOperation != null)
|
||||
{
|
||||
if (StringUtils.hasText(apiOperation.value()))
|
||||
{
|
||||
operation.setSummary(apiOperation.value());
|
||||
}
|
||||
if (StringUtils.hasText(apiOperation.notes()))
|
||||
{
|
||||
operation.setDescription(apiOperation.notes());
|
||||
}
|
||||
}
|
||||
applyLegacyParameterDescriptions(operation, handlerMethod);
|
||||
return operation;
|
||||
}
|
||||
|
||||
private void applyLegacyParameterDescriptions(Operation operation, HandlerMethod handlerMethod)
|
||||
{
|
||||
if (operation.getParameters() == null || operation.getParameters().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<Parameter> parameters = operation.getParameters();
|
||||
int fallbackIndex = 0;
|
||||
for (MethodParameter methodParameter : handlerMethod.getMethodParameters())
|
||||
{
|
||||
ApiParam apiParam = methodParameter.getParameterAnnotation(ApiParam.class);
|
||||
if (apiParam == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String parameterName = StringUtils.hasText(apiParam.name())
|
||||
? apiParam.name() : methodParameter.getParameterName();
|
||||
Parameter parameter = parameters.stream()
|
||||
.filter(item -> parameterName != null && parameterName.equals(item.getName()))
|
||||
.findFirst()
|
||||
.orElse(fallbackIndex < parameters.size() ? parameters.get(fallbackIndex++) : null);
|
||||
if (parameter != null)
|
||||
{
|
||||
if (StringUtils.hasText(apiParam.value()))
|
||||
{
|
||||
parameter.setDescription(apiParam.value());
|
||||
}
|
||||
if (apiParam.required())
|
||||
{
|
||||
parameter.setRequired(true);
|
||||
}
|
||||
if (StringUtils.hasText(apiParam.example()))
|
||||
{
|
||||
parameter.setExample(apiParam.example());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <A extends Annotation> A findAnnotation(AnnotatedType annotatedType, Class<A> annotationType)
|
||||
{
|
||||
Annotation[] annotations = annotatedType.getCtxAnnotations();
|
||||
if (annotations == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
for (Annotation annotation : annotations)
|
||||
{
|
||||
if (annotationType.isInstance(annotation))
|
||||
{
|
||||
return annotationType.cast(annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,140 +0,0 @@
|
||||
package com.cmvr.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.cmvr.common.config.CmvrIotConfig;
|
||||
import com.cmvr.test.model.vo.inspection.InspectionAlarmRuleVO;
|
||||
import com.cmvr.test.model.vo.inspection.InspectionManualReviewConfigVO;
|
||||
import com.cmvr.test.model.vo.inspection.InspectionMediaVO;
|
||||
import com.cmvr.test.model.vo.inspection.InspectionMeterRecognizeConfigVO;
|
||||
import com.fasterxml.classmate.TypeResolver;
|
||||
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 cmvr-iot
|
||||
*/
|
||||
@Configuration
|
||||
public class SwaggerConfig
|
||||
{
|
||||
/** 系统基础配置 */
|
||||
@Autowired
|
||||
private CmvrIotConfig cmvrIotConfig;
|
||||
|
||||
/** 用于将动态工作流payload对应的参数模型显式加入Swagger文档。 */
|
||||
@Autowired
|
||||
private TypeResolver typeResolver;
|
||||
|
||||
/** 是否开启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.cmvr.project.tool.swagger"))
|
||||
// 扫描所有 .apis(RequestHandlerSelectors.any())
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
// 工作流单节点接口使用JSONObject作为payload,需显式注册巡检节点参数模型。
|
||||
.additionalModels(
|
||||
typeResolver.resolve(InspectionMeterRecognizeConfigVO.class),
|
||||
typeResolver.resolve(InspectionAlarmRuleVO.class),
|
||||
typeResolver.resolve(InspectionManualReviewConfigVO.class),
|
||||
typeResolver.resolve(InspectionMediaVO.class))
|
||||
/* 设置安全模式,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(cmvrIotConfig.getName(), null, null))
|
||||
// 版本
|
||||
.version("版本号:" + cmvrIotConfig.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ spring:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
data.redis:
|
||||
# 地址
|
||||
host: 192.168.28.10
|
||||
# 端口,默认为6379
|
||||
|
||||
@ -68,7 +68,7 @@ spring:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
data.redis:
|
||||
# 地址
|
||||
host: 192.168.0.100
|
||||
# 端口,默认为6379
|
||||
|
||||
61
cmvr-iot-admin/src/main/resources/application-druid.yml
Normal file
61
cmvr-iot-admin/src/main/resources/application-druid.yml
Normal file
@ -0,0 +1,61 @@
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: password
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: ruoyi
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
@ -68,7 +68,7 @@ spring:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
data.redis:
|
||||
# 地址
|
||||
host: 192.168.28.10
|
||||
# 端口,默认为6379
|
||||
|
||||
@ -68,7 +68,7 @@ spring:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
data.redis:
|
||||
# 地址
|
||||
host: redis
|
||||
# 端口,默认为6379
|
||||
|
||||
@ -68,7 +68,7 @@ spring:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
data.redis:
|
||||
# 地址
|
||||
host: 192.168.28.10
|
||||
# 端口,默认为6379
|
||||
|
||||
@ -48,6 +48,10 @@ user:
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
sql:
|
||||
init:
|
||||
mode: always
|
||||
schema-locations: classpath:db/notice-schema.sql
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
@ -99,13 +103,15 @@ swagger:
|
||||
# 请求前缀
|
||||
pathMapping: /
|
||||
knife4j:
|
||||
# 开启屏蔽文档资源
|
||||
# 生产环境可开启屏蔽文档资源
|
||||
production: false
|
||||
# 是否开启Knife4j(swagger 增强版)
|
||||
# 是否开启Knife4j增强界面
|
||||
enable: true
|
||||
setting:
|
||||
language: zh-CN
|
||||
enableRequestCache: true
|
||||
enableSwaggerModels: true
|
||||
swaggerModelName: 数据模型
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
@ -116,4 +122,34 @@ xss:
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*,/device/*,/test/*,/api/*
|
||||
|
||||
# Integrated QUIC gateway and in-process node synchronization.
|
||||
cmvr:
|
||||
quic:
|
||||
enabled: true
|
||||
bind-host: 0.0.0.0
|
||||
port: 4433
|
||||
grpc-host-source: observed-source
|
||||
alpn: cmvr-quic-edge/1
|
||||
allowed-node-ids: ""
|
||||
tls:
|
||||
certificate: "certs/quic-gateway.crt"
|
||||
private-key: "certs/quic-gateway.key"
|
||||
require-client-certificate: false
|
||||
client-ca: ""
|
||||
heartbeat-interval-ms: 5000
|
||||
heartbeat-timeout-ms: 15000
|
||||
idle-timeout-ms: 30000
|
||||
maximum-control-frame-bytes: 1048576
|
||||
datagram-queue-length: 1024
|
||||
maximum-frame-bytes: 8388608
|
||||
maximum-reassembly-bytes: 67108864
|
||||
maximum-reassembly-frames: 256
|
||||
reassembly-timeout-ms: 2000
|
||||
media-subscriber-queue-capacity: 64
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
path: /swagger-ui.html
|
||||
|
||||
9
cmvr-iot-admin/src/main/resources/db/notice-schema.sql
Normal file
9
cmvr-iot-admin/src/main/resources/db/notice-schema.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS sys_notice_read (
|
||||
read_id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
notice_id INT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
read_time DATETIME NOT NULL,
|
||||
PRIMARY KEY (read_id),
|
||||
UNIQUE KEY uk_user_notice (user_id, notice_id),
|
||||
KEY idx_notice_id (notice_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Notice read records';
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot</artifactId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cmvr-iot-aima</artifactId>
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.spring.service.IService;
|
||||
import com.cmvr.aima.domain.AimaTaskInstance;
|
||||
import com.cmvr.aima.domain.dto.AimaTaskInstanceQuery;
|
||||
import com.cmvr.aima.domain.vo.AimaTaskInstanceVo;
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
|
||||
@ -3,7 +3,7 @@ package com.cmvr.aima.service.impl;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import com.cmvr.aima.domain.dto.AimaTaskInstanceQuery;
|
||||
import com.cmvr.aima.domain.vo.AimaTaskInstanceVo;
|
||||
import com.cmvr.common.exception.ServiceException;
|
||||
|
||||
@ -7,7 +7,7 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import com.cmvr.aima.domain.AimaTaskTestCase;
|
||||
import com.cmvr.aima.domain.AimaTestCase;
|
||||
import com.cmvr.aima.domain.vo.AimaTaskVo;
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import com.cmvr.aima.domain.vo.AimaTestCaseVo;
|
||||
import com.cmvr.test.model.domain.TeDetectionItem;
|
||||
import com.cmvr.test.service.ITeDetectionItemService;
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
|
||||
@ -2,7 +2,7 @@ package com.cmvr.aima.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.cmvr.common.utils.DateUtils;
|
||||
|
||||
@ -6,27 +6,30 @@
|
||||
<parent>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-edge</artifactId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cmvr-iot-grpc-client</artifactId>
|
||||
<description>cmvr-iot gRPC客户端</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-quic-contract</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-grpc-lib</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.devh</groupId>
|
||||
<artifactId>grpc-client-spring-boot-starter</artifactId>
|
||||
<version>${grpc-client.version}</version>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.25.5</version>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -17,7 +17,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -49,6 +49,17 @@ public class GrpcChannelCache {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 终端主动上报的gRPC地址发生变化时立即关闭旧通道。
|
||||
* 下一次业务调用仍通过原有supplier创建ManagedChannel。
|
||||
*/
|
||||
public void invalidate(String terminalId) {
|
||||
ChannelWrapper wrapper = channelMap.remove(terminalId);
|
||||
if (wrapper != null) {
|
||||
wrapper.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通道包装器(管理通道生命周期和访问时间)
|
||||
*/
|
||||
|
||||
@ -12,7 +12,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
@ -110,6 +110,11 @@ public class GrpcServiceManager {
|
||||
grpcChannelCache.cleanExpiredChannels();
|
||||
}
|
||||
|
||||
/** QUIC发现终端地址变化后,使指定终端的旧gRPC通道立即失效。 */
|
||||
public void invalidateTerminal(String terminalId) {
|
||||
grpcChannelCache.invalidate(terminalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* gRPC客户端工厂类
|
||||
*/
|
||||
|
||||
@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
@Data
|
||||
@ApiModel("边缘系统通用VO")
|
||||
|
||||
@ -6,7 +6,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* AGV 地图名称请求参数。
|
||||
|
||||
@ -6,7 +6,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* AGV 导航到站点的请求参数。
|
||||
|
||||
@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* AGV 上传地图请求参数。
|
||||
|
||||
@ -6,7 +6,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 机械臂标定零点参数。
|
||||
|
||||
@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 扬声器播放音频请求参数。
|
||||
|
||||
@ -0,0 +1,224 @@
|
||||
package com.cmvr.edge.client.quic;
|
||||
|
||||
import com.cmvr.device.domain.DeDeviceTerminalConfig;
|
||||
import com.cmvr.device.service.IDeDeviceTerminalConfigService;
|
||||
import com.cmvr.edge.client.manage.GrpcServiceManager;
|
||||
import com.cmvr.quic.contract.QuicEventBus;
|
||||
import com.cmvr.quic.gateway.v1.NodeEvent;
|
||||
import com.cmvr.quic.gateway.v1.NodeEventType;
|
||||
import com.cmvr.quic.gateway.v1.NodeSnapshot;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** 直接订阅进程内 QUIC 节点事件,并同步已绑定终端的运行状态和 gRPC 地址。 */
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class QuicGatewayNodeSyncService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QuicGatewayNodeSyncService.class);
|
||||
private static final long HEARTBEAT_PERSIST_INTERVAL_MS = 15000L;
|
||||
|
||||
private final IDeDeviceTerminalConfigService terminalConfigService;
|
||||
private final GrpcServiceManager grpcServiceManager;
|
||||
private final QuicEventBus quicEventBus;
|
||||
|
||||
@Value("${cmvr.quic.enabled:false}")
|
||||
private boolean enabled;
|
||||
|
||||
/** observed-source 使用 QUIC 来源 IP;advertised 使用节点主动上报的 gRPC host。 */
|
||||
@Value("${cmvr.quic.grpc-host-source:observed-source}")
|
||||
private String grpcHostSource;
|
||||
|
||||
private final AtomicBoolean stopping = new AtomicBoolean(false);
|
||||
private final AtomicBoolean grpcHostSourceWarningLogged = new AtomicBoolean(false);
|
||||
private final Set<String> unknownNodes = ConcurrentHashMap.newKeySet();
|
||||
private final Set<String> grpcAddressMismatchNodes = ConcurrentHashMap.newKeySet();
|
||||
private final ConcurrentHashMap<String, Long> lastPersistedAt = new ConcurrentHashMap<String, Long>();
|
||||
private final ConcurrentHashMap<String, String> synchronizedTerminals =
|
||||
new ConcurrentHashMap<String, String>();
|
||||
private ExecutorService eventExecutor;
|
||||
private QuicEventBus.Subscription subscription;
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
if (!enabled) {
|
||||
log.info("QUIC 节点状态同步未启用");
|
||||
return;
|
||||
}
|
||||
eventExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
Thread thread = new Thread(runnable, "quic-node-sync");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
}
|
||||
});
|
||||
subscription = quicEventBus.watchNodes(true, this::enqueueEvent);
|
||||
log.info("已通过进程内事件总线订阅 QUIC 节点事件");
|
||||
}
|
||||
|
||||
private void enqueueEvent(final NodeEvent event) {
|
||||
ExecutorService executor = eventExecutor;
|
||||
if (stopping.get() || executor == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleEventSafely(event);
|
||||
}
|
||||
});
|
||||
} catch (RejectedExecutionException ignored) {
|
||||
if (!stopping.get()) {
|
||||
log.warn("QUIC 节点事件线程已停止,忽略事件,nodeId={}", event.getNode().getNodeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEventSafely(NodeEvent event) {
|
||||
try {
|
||||
syncNode(event);
|
||||
} catch (RuntimeException error) {
|
||||
log.error("同步 QUIC 终端状态失败,nodeId={},eventType={}",
|
||||
event.getNode().getNodeId(), event.getType(), error);
|
||||
}
|
||||
}
|
||||
|
||||
private void syncNode(NodeEvent event) {
|
||||
NodeSnapshot node = event.getNode();
|
||||
if (node.getNodeId().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
DeDeviceTerminalConfig terminal = terminalConfigService.selectByQuicNodeId(node.getNodeId());
|
||||
if (terminal == null) {
|
||||
if (unknownNodes.add(node.getNodeId())) {
|
||||
log.warn("QUIC 节点尚未绑定平台终端,忽略运行状态同步,nodeId={},remoteIp={}",
|
||||
node.getNodeId(), node.getObservedSourceIp());
|
||||
}
|
||||
return;
|
||||
}
|
||||
unknownNodes.remove(node.getNodeId());
|
||||
|
||||
boolean offline = event.getType() == NodeEventType.NODE_EVENT_TYPE_OFFLINE || !node.getOnline();
|
||||
if (offline) {
|
||||
synchronizedTerminals.remove(node.getNodeId());
|
||||
} else {
|
||||
synchronizedTerminals.put(node.getNodeId(), terminal.getId());
|
||||
}
|
||||
String grpcHost = offline ? null : resolveGrpcHost(node);
|
||||
Long grpcPort = offline || node.getGrpcEndpoint().getPort() == 0
|
||||
? null : Long.valueOf(node.getGrpcEndpoint().getPort());
|
||||
boolean endpointChanged = !offline && grpcHost != null && grpcPort != null
|
||||
&& (!grpcHost.equals(terminal.getHost()) || !grpcPort.equals(terminal.getPort()));
|
||||
long now = System.currentTimeMillis();
|
||||
Long previousPersisted = lastPersistedAt.get(node.getNodeId());
|
||||
boolean immediate = event.getType() == NodeEventType.NODE_EVENT_TYPE_REGISTERED
|
||||
|| event.getType() == NodeEventType.NODE_EVENT_TYPE_OFFLINE || endpointChanged;
|
||||
if (!immediate && previousPersisted != null
|
||||
&& now - previousPersisted.longValue() < HEARTBEAT_PERSIST_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminalConfigService.updateQuicRuntimeState(terminal.getId(), grpcHost, grpcPort,
|
||||
node.getObservedSourceIp(), node.getSessionId(), node.getBootId(),
|
||||
node.getSoftwareVersion(), offline ? "0" : "1",
|
||||
new Date(event.getEventTimeUnixMs() == 0 ? now : event.getEventTimeUnixMs()));
|
||||
lastPersistedAt.put(node.getNodeId(), now);
|
||||
if (offline) {
|
||||
lastPersistedAt.remove(node.getNodeId());
|
||||
}
|
||||
if (endpointChanged || offline) {
|
||||
grpcServiceManager.invalidateTerminal(terminal.getId());
|
||||
}
|
||||
log.info("QUIC 终端状态已同步,terminalId={},nodeId={},online={},grpc={}:{}",
|
||||
terminal.getId(), node.getNodeId(), !offline,
|
||||
grpcHost == null ? terminal.getHost() : grpcHost,
|
||||
grpcPort == null ? terminal.getPort() : grpcPort);
|
||||
}
|
||||
|
||||
private String resolveGrpcHost(NodeSnapshot node) {
|
||||
String advertisedHost = emptyToNull(node.getGrpcEndpoint().getHost());
|
||||
if ("advertised".equalsIgnoreCase(grpcHostSource)) {
|
||||
return advertisedHost;
|
||||
}
|
||||
if (!"observed-source".equalsIgnoreCase(grpcHostSource)
|
||||
&& grpcHostSourceWarningLogged.compareAndSet(false, true)) {
|
||||
log.warn("未知的 QUIC gRPC 地址来源配置,按 observed-source 处理,value={}", grpcHostSource);
|
||||
}
|
||||
String observedHost = emptyToNull(node.getObservedSourceIp());
|
||||
if (observedHost == null) {
|
||||
log.warn("QUIC 节点缺少来源 IP,回退到节点上报地址,nodeId={},advertisedHost={}",
|
||||
node.getNodeId(), advertisedHost);
|
||||
return advertisedHost;
|
||||
}
|
||||
if (advertisedHost != null && !observedHost.equals(advertisedHost)
|
||||
&& grpcAddressMismatchNodes.add(node.getNodeId())) {
|
||||
log.info("QUIC 节点上报的 gRPC 地址与来源 IP 不同,使用来源 IP,nodeId={},"
|
||||
+ "observedHost={},advertisedHost={}",
|
||||
node.getNodeId(), observedHost, advertisedHost);
|
||||
} else if (observedHost.equals(advertisedHost)) {
|
||||
grpcAddressMismatchNodes.remove(node.getNodeId());
|
||||
}
|
||||
return observedHost;
|
||||
}
|
||||
|
||||
private void markSynchronizedTerminalsOffline() {
|
||||
for (Map.Entry<String, String> entry : synchronizedTerminals.entrySet()) {
|
||||
try {
|
||||
terminalConfigService.updateQuicRuntimeState(entry.getValue(), null, null,
|
||||
null, null, null, null, "0", new Date());
|
||||
grpcServiceManager.invalidateTerminal(entry.getValue());
|
||||
} catch (RuntimeException error) {
|
||||
log.error("标记 QUIC 终端离线失败,nodeId={},terminalId={}",
|
||||
entry.getKey(), entry.getValue(), error);
|
||||
}
|
||||
}
|
||||
synchronizedTerminals.clear();
|
||||
lastPersistedAt.clear();
|
||||
}
|
||||
|
||||
private static String emptyToNull(String value) {
|
||||
return value == null || value.trim().isEmpty() ? null : value.trim();
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void stop() {
|
||||
if (!stopping.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
QuicEventBus.Subscription currentSubscription = subscription;
|
||||
if (currentSubscription != null) {
|
||||
currentSubscription.close();
|
||||
}
|
||||
ExecutorService executor = eventExecutor;
|
||||
if (executor != null) {
|
||||
executor.shutdown();
|
||||
try {
|
||||
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException interrupted) {
|
||||
executor.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
markSynchronizedTerminalsOffline();
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,9 @@ import com.cmvr.common.utils.uuid.IdUtils;
|
||||
import com.cmvr.edge.client.adapter.RobotAudioGrpcAdapter;
|
||||
import com.cmvr.edge.client.adapter.RobotAudioGrpcAdapter.RobotAudioStream;
|
||||
import com.cmvr.edge.client.adapter.RobotAudioGrpcAdapter.RobotAudioStreamListener;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -3,7 +3,7 @@ package com.cmvr.edge.client.webrtc;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-edge</artifactId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cmvr-iot-grpc-lib</artifactId>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-api</artifactId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cmvr-iot-edge</artifactId>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.cmvr</groupId>
|
||||
<artifactId>cmvr-iot-api</artifactId>
|
||||
<version>3.8.9</version>
|
||||
<version>3.9.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cmvr-iot-llm</artifactId>
|
||||
|
||||
@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
@Data
|
||||
@ApiModel("智能体调用VO")
|
||||
|
||||
@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
@Data
|
||||
@ApiModel("智能体调用VO")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user