mirror of
https://gitee.com/NexIoT/Universal-IoT-Java.git
synced 2026-08-29 09:00:54 +08:00
feat:合并2.0全部商业版全部代码,保持与演示环境100%一致
This commit is contained in:
+2
-1
@@ -120,7 +120,8 @@
|
||||
- **文档地址
|
||||
**:[https://nexiotplatform.github.io/universal-iot-docs/](https://nexiotplatform.github.io/universal-iot-docs/)
|
||||
- **在线演示**:[http://iot.192886.xyz:81/](http://iot.192886.xyz:81/)
|
||||
- **调试IDE**:[http://iot.192886.xyz:81/magic/debug/index.html](http://iot.192886.xyz:81/magic/debug/index.html)
|
||||
- **调试IDE
|
||||
**:[http://iot.192886.xyz:81/magic/debug/index.html](http://iot.192886.xyz:81/magic/debug/index.html)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
一键导入,无需修改平台代码,无需重新编译部署。设备驱动在线热更新,新增设备类型零停机。稳定运行几年无需重启维护。
|
||||
|
||||
### 🎯 核心价值
|
||||
|
||||
- **🚀 零代码侵入**:设备驱动完全外置,一键导出源码直接使用,无任何代码侵入
|
||||
- **🔧 解耦架构**:驱动与平台完全分离,热插拔式设备接入,生态共享
|
||||
- **🏗️ 全协议支持**:TCP、MQTT、Modbus、天翼物联、HTTP等主流协议
|
||||
@@ -65,7 +66,6 @@
|
||||
- ✅ **全协议支持**:Modbus RTU/TCP、MQTT、HTTP等工业协议和物联网协议
|
||||
- ✅ **云平台对接**:天翼物联、移动OneNet等云平台集成
|
||||
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 🎯 演示地址
|
||||
@@ -82,34 +82,37 @@
|
||||
### 🛠️ 技术栈
|
||||
|
||||
#### 🚀 核心框架(极简轻量)
|
||||
|
||||
- **后端框架**:`Java 21` `SpringBoot 3.5` `Tk.Mybatis 5.0.1`
|
||||
- **前端技术**:基于`RuoYi-Antdv`构建,感谢开源社区!
|
||||
|
||||
#### 💾 数据存储
|
||||
- **关系数据库**:MySQL 5.8+
|
||||
- **缓存系统**:Redis 6.0+
|
||||
|
||||
- **关系数据库**:MySQL 5.8+
|
||||
- **缓存系统**:Redis 6.0+
|
||||
- **时序数据库**:ClickHouse / IoTDB(配置开启)
|
||||
|
||||
## 📈 正在推进
|
||||
|
||||
### 🚀 近期规划
|
||||
|
||||
- **🤖 AI能力增强**:智能故障诊断
|
||||
- **📱 移动端应用**:付费图鸟定小程序,具备指令控制、属性、告警查看,轻量化、多管理员
|
||||
|
||||
### 🌟 长期愿景
|
||||
|
||||
- **🧠 深度学习**:设备行为学习、预测性维护、智能优化
|
||||
- **🌐 生态建设**:插件市场、第三方集成、开发者社区
|
||||
- **🏭 行业方案**:智能制造、智慧城市、智慧农业等垂直解决方案
|
||||
|
||||
|
||||
## 📸 实战案例与视频教程
|
||||
|
||||
### 📊 支持设备类型
|
||||
|
||||
| | |
|
||||
|-------------------------------------------------|-----------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
| | |
|
||||
|-------------------------------------------------|--------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
### 📡 天翼物联AI接入教程
|
||||
@@ -117,15 +120,18 @@
|
||||
#### 🌊 NB水浸传感器
|
||||
|
||||
1. **【手把手实战:水浸传感器开箱:初见与硬件解析】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1kLY5z5EMH/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1kLY5z5EMH/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:设备开箱、硬件介绍、AI智能识别功能测试
|
||||
|
||||
2. **【手把手实战:NexIoT接入Ctwing参数配置】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1jVY7zVEjM/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1jVY7zVEjM/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:天翼物联平台参数配置、智能设备注册流程
|
||||
|
||||
3. **【手把手实战:NB水浸传感器CTWing接入全流程】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1sJaZzBEfr/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1sJaZzBEfr/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:外置驱动的完整设备接入流程,从智能配置到数据上报
|
||||
|
||||
### 🔌 TCP直连AI接入教程
|
||||
@@ -133,15 +139,18 @@
|
||||
#### ⚡ DL/T645电表
|
||||
|
||||
1. **【NexIoT开源】手把手实战:DL/T645电表TCP直连接入】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1x4pBzZEVN/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1x4pBzZEVN/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:TCP协议解析、设备直连配置、智能数据解析
|
||||
|
||||
2. **【NexIoT开源】手把手实战:产品创建与SNI解读】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1zSWNzoETF/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1zSWNzoETF/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:创建流程、智能SNI设备标识解析、AI协议调试
|
||||
|
||||
3. **【NexIoT开源】手把手实战:如何写TCP接入的DL/T645电表解析脚本】**
|
||||
- 📺 [B站观看](https://www.bilibili.com/video/BV1SAWVzwEZE/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
-
|
||||
📺 [B站观看](https://www.bilibili.com/video/BV1SAWVzwEZE/?share_source=copy_web&vd_source=c9e1500efcc8aa0763f711fadaa68dff)
|
||||
- 📝 内容:数据流介绍、智能调试IDE的编写和调试、645电表的AI解析
|
||||
|
||||
#### ⚡ 接入成果展示
|
||||
@@ -180,15 +189,14 @@
|
||||
|
||||
### 🎯 功能深度展示
|
||||
|
||||
| | |
|
||||
|------------------------------------------|---------------------------------------|
|
||||
| | |
|
||||
|-----------------------------------------|--------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
|  |  |
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
@@ -228,25 +236,27 @@
|
||||
```
|
||||
|
||||
4. **创建 Pull Request**
|
||||
- 在 Gitee 上创建 Pull Request
|
||||
- 详细描述您的修改内容和增强功能
|
||||
- 等待代码审查和功能验证
|
||||
- 在 Gitee 上创建 Pull Request
|
||||
- 详细描述您的修改内容和增强功能
|
||||
- 等待代码审查和功能验证
|
||||
|
||||
#### 🌟 社区价值观
|
||||
|
||||
- **🤝 相互尊重**:欢迎所有开发者加入,有问题我们第一时间回复
|
||||
- **⭐ 真诚支持**:喜欢请点star,不喜欢也请尊重,不要恶意行为
|
||||
- **🚀 共同成长**:分享技术经验,推动物联网智能化发展
|
||||
- **💡 开放创新**:鼓励功能创新和零代码技术改进
|
||||
|
||||
#### 📢 重要声明
|
||||
|
||||
- **自媒体转发**:欢迎转发项目信息,请遵循AGPL3.0开源协议
|
||||
- **企业授权**:商业使用需获得授权,保留企业授权信息
|
||||
- **法律追责**:如单位误用引起法律后果,保留追责责任
|
||||
|
||||
#### 🌐 社区联系方式
|
||||
|
||||
| QQ群 | 微信 | B站 | 抖音 |
|
||||
|-------------------------------|-------------------------------|----------------------------------|----------------------------------|
|
||||
| QQ群 | 微信 | B站 | 抖音 |
|
||||
|-------------------------------|-------------------------------|---------------------------------|----------------------------------|
|
||||
|  |  |  |  |
|
||||
|
||||
## 🛠️ 技术支持与服务
|
||||
@@ -255,14 +265,14 @@
|
||||
|
||||
### 📋 服务类型对比
|
||||
|
||||
| 服务类型 | 服务内容 | 价格 | 适用场景 | 联系方式 |
|
||||
|---------|---------|--------------|----------|----------|
|
||||
| **🆓 社区技术答疑** | • 基础问题答疑<br>• 使用指导<br>• 社区交流 | **免费** | 学习、使用 | QQ群、微信群 |
|
||||
| **📚 技术文档** | • 技术文档<br>• 实践指南<br>• 视频教程 | **免费** | 自学、参考 | [文档地址](https://nexiotplatform.github.io/universal-iot-docs/) |
|
||||
| **☁️ 线上部署服务** | • 客户提供硬件<br>• 完成系统部署<br>• 云服务部署 | **¥169** | 快速上线、云部署 | 联系客服 |
|
||||
| **🔧 专属技术支持** | • 各类对接支持<br>• 使用指导<br>• 部署方案<br>• 解决方案 | **¥99/天** | 项目对接、技术咨询 | 联系客服 |
|
||||
| **⚡ 设备接入服务** | • IDE解析源码<br>• 产品配置导出<br>• 定制化接入 | **¥1000/日** | 复杂设备接入、定制开发 | 联系客服 |
|
||||
| **🏢 企业省心服务** | • 商业版本地部署<br>• 代运维服务<br>• 5×8技术支持 | **¥5,999/年** | 仅剩5名,企业级应用、长期运维 | 联系客服 |
|
||||
| 服务类型 | 服务内容 | 价格 | 适用场景 | 联系方式 |
|
||||
|---------------|----------------------------------------|--------------|-----------------|--------------------------------------------------------------|
|
||||
| **🆓 社区技术答疑** | • 基础问题答疑<br>• 使用指导<br>• 社区交流 | **免费** | 学习、使用 | QQ群、微信群 |
|
||||
| **📚 技术文档** | • 技术文档<br>• 实践指南<br>• 视频教程 | **免费** | 自学、参考 | [文档地址](https://nexiotplatform.github.io/universal-iot-docs/) |
|
||||
| **☁️ 线上部署服务** | • 客户提供硬件<br>• 完成系统部署<br>• 云服务部署 | **¥169** | 快速上线、云部署 | 联系客服 |
|
||||
| **🔧 专属技术支持** | • 各类对接支持<br>• 使用指导<br>• 部署方案<br>• 解决方案 | **¥99/天** | 项目对接、技术咨询 | 联系客服 |
|
||||
| **⚡ 设备接入服务** | • IDE解析源码<br>• 产品配置导出<br>• 定制化接入 | **¥1000/日** | 复杂设备接入、定制开发 | 联系客服 |
|
||||
| **🏢 企业省心服务** | • 商业版本地部署<br>• 代运维服务<br>• 5×8技术支持 | **¥5,999/年** | 仅剩5名,企业级应用、长期运维 | 联系客服 |
|
||||
|
||||
---
|
||||
|
||||
@@ -271,18 +281,20 @@
|
||||
### 📜 详细授权条款
|
||||
|
||||
#### ✅ 授权用户权益
|
||||
|
||||
1. **内部使用**:授权用户可无限期在其内部使用
|
||||
- 企业授权:不含分公司、子公司及第三方公司
|
||||
- 个人授权:仅限个人使用,不可用于在职公司或第三方
|
||||
- 企业授权:不含分公司、子公司及第三方公司
|
||||
- 个人授权:仅限个人使用,不可用于在职公司或第三方
|
||||
|
||||
2. **开发权限**:授权用户可通过项目形式进行源码二次开发
|
||||
- 定制化软件必须加密打包后交付客户
|
||||
- 如需交付源码,必须为客户购买对应商业授权
|
||||
- 定制化软件必须加密打包后交付客户
|
||||
- 如需交付源码,必须为客户购买对应商业授权
|
||||
|
||||
#### ❌ 授权限制
|
||||
|
||||
1. **禁止转让**:不可向第三方发行或转让授权源码
|
||||
- 无论有偿或无偿转让
|
||||
- 不可申请包含本项目的软件著作权
|
||||
- 无论有偿或无偿转让
|
||||
- 不可申请包含本项目的软件著作权
|
||||
|
||||
### 🔒 开源和商业
|
||||
|
||||
|
||||
+133
-133
@@ -1,143 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>cn-universal</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>cn-universal</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cn-universal-admin</artifactId>
|
||||
<dependencies>
|
||||
<artifactId>cn-universal-admin</artifactId>
|
||||
<dependencies>
|
||||
|
||||
<!--velocity代码生成使用模板 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-persistence</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-rule</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-dm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!--velocity代码生成使用模板 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-persistence</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-rule</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-dm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-oss</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-oss</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<!--排除这个slf4j-log4j12-->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<!--排除这个slf4j-log4j12-->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Validation依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!-- OAuth2 Resource Server (JWT support) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.vaadin.external.google</groupId>
|
||||
<artifactId>android-json</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-mqtt-protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-tcp-protocol</artifactId>
|
||||
<scope>compile</scope>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-udp-protocol</artifactId>
|
||||
<scope>compile</scope>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-notice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Validation依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!-- OAuth2 Resource Server (JWT support) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.vaadin.external.google</groupId>
|
||||
<artifactId>android-json</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-mqtt-protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-tcp-protocol</artifactId>
|
||||
<scope>compile</scope>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-udp-protocol</artifactId>
|
||||
<scope>compile</scope>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-notice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
-3
@@ -809,7 +809,6 @@ public class NetworkServiceImpl implements INetworkService {
|
||||
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
|
||||
}
|
||||
|
||||
|
||||
String productKey = network.getProductKey();
|
||||
if (StrUtil.isBlank(productKey)) {
|
||||
log.error("TCP网络组件缺少productKey: {}", network.getName());
|
||||
@@ -843,7 +842,6 @@ public class NetworkServiceImpl implements INetworkService {
|
||||
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
|
||||
}
|
||||
|
||||
|
||||
String productKey = network.getProductKey();
|
||||
if (StrUtil.isBlank(productKey)) {
|
||||
log.error("TCP网络组件缺少productKey: {}", network.getName());
|
||||
@@ -876,7 +874,6 @@ public class NetworkServiceImpl implements INetworkService {
|
||||
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
|
||||
}
|
||||
|
||||
|
||||
String productKey = network.getProductKey();
|
||||
if (StrUtil.isBlank(productKey)) {
|
||||
log.error("TCP网络组件缺少productKey: {}", network.getName());
|
||||
|
||||
+25
-27
@@ -26,58 +26,50 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 网关轮询定时任务调度器
|
||||
*
|
||||
*
|
||||
* <p>使用固定轮询间隔(30s, 60s, 120s, 300s, 600s),通过分布式锁保证集群环境下只有一个节点执行
|
||||
*
|
||||
*
|
||||
* @author Aleo
|
||||
* @date 2025-10-26
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(prefix = "gateway.polling", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
prefix = "gateway.polling",
|
||||
name = "enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public class GatewayPollingScheduler {
|
||||
|
||||
@Autowired
|
||||
private IGatewayPollingService gatewayPollingService;
|
||||
@Autowired private IGatewayPollingService gatewayPollingService;
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired private RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 30秒轮询任务
|
||||
*/
|
||||
/** 30秒轮询任务 */
|
||||
@Scheduled(cron = "0/30 * * * * ?")
|
||||
public void polling30s() {
|
||||
executePollingWithLock(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* 60秒轮询任务
|
||||
*/
|
||||
/** 60秒轮询任务 */
|
||||
@Scheduled(cron = "0 0/1 * * * ?")
|
||||
public void polling60s() {
|
||||
executePollingWithLock(60);
|
||||
}
|
||||
|
||||
/**
|
||||
* 120秒轮询任务
|
||||
*/
|
||||
/** 120秒轮询任务 */
|
||||
@Scheduled(cron = "0 0/2 * * * ?")
|
||||
public void polling120s() {
|
||||
executePollingWithLock(120);
|
||||
}
|
||||
|
||||
/**
|
||||
* 300秒轮询任务 (5分钟)
|
||||
*/
|
||||
/** 300秒轮询任务 (5分钟) */
|
||||
@Scheduled(cron = "0 0/5 * * * ?")
|
||||
public void polling300s() {
|
||||
executePollingWithLock(300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 600秒轮询任务 (10分钟)
|
||||
*/
|
||||
/** 600秒轮询任务 (10分钟) */
|
||||
@Scheduled(cron = "0 0/10 * * * ?")
|
||||
public void polling600s() {
|
||||
executePollingWithLock(600);
|
||||
@@ -85,7 +77,7 @@ public class GatewayPollingScheduler {
|
||||
|
||||
/**
|
||||
* 使用分布式锁执行轮询任务
|
||||
*
|
||||
*
|
||||
* @param intervalSeconds 轮询间隔(秒)
|
||||
*/
|
||||
private void executePollingWithLock(int intervalSeconds) {
|
||||
@@ -118,14 +110,15 @@ public class GatewayPollingScheduler {
|
||||
|
||||
/**
|
||||
* 执行轮询任务
|
||||
*
|
||||
*
|
||||
* @param intervalSeconds 轮询间隔(秒)
|
||||
*/
|
||||
private void executePollingTask(int intervalSeconds) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 1. 查询待轮询的网关设备
|
||||
List<GatewayPollingConfig> configs = gatewayPollingService.getDuePollingDevices(intervalSeconds);
|
||||
List<GatewayPollingConfig> configs =
|
||||
gatewayPollingService.getDuePollingDevices(intervalSeconds);
|
||||
|
||||
if (configs == null || configs.isEmpty()) {
|
||||
log.debug("本次{}s轮询无待执行设备", intervalSeconds);
|
||||
@@ -149,7 +142,12 @@ public class GatewayPollingScheduler {
|
||||
}
|
||||
|
||||
long costTime = System.currentTimeMillis() - startTime;
|
||||
log.info("{}s轮询任务完成: 总数={}, 成功={}, 失败={}, 耗时={}ms",
|
||||
intervalSeconds, configs.size(), successCount, failCount, costTime);
|
||||
log.info(
|
||||
"{}s轮询任务完成: 总数={}, 成功={}, 失败={}, 耗时={}ms",
|
||||
intervalSeconds,
|
||||
configs.size(),
|
||||
successCount,
|
||||
failCount,
|
||||
costTime);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -19,7 +19,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 网关轮询服务接口
|
||||
*
|
||||
*
|
||||
* @author Aleo
|
||||
* @date 2025-10-26
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ public interface IGatewayPollingService {
|
||||
|
||||
/**
|
||||
* 保存或更新网关轮询配置
|
||||
*
|
||||
*
|
||||
* @param dto 配置DTO
|
||||
* @return 操作结果
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ public interface IGatewayPollingService {
|
||||
|
||||
/**
|
||||
* 获取网关轮询配置
|
||||
*
|
||||
*
|
||||
* @param productKey 产品Key
|
||||
* @param deviceId 设备ID
|
||||
* @return 配置信息
|
||||
@@ -44,7 +44,7 @@ public interface IGatewayPollingService {
|
||||
|
||||
/**
|
||||
* 删除网关轮询配置
|
||||
*
|
||||
*
|
||||
* @param productKey 产品Key
|
||||
* @param deviceId 设备ID
|
||||
* @return 操作结果
|
||||
@@ -53,14 +53,14 @@ public interface IGatewayPollingService {
|
||||
|
||||
/**
|
||||
* 执行单个网关的轮询任务
|
||||
*
|
||||
*
|
||||
* @param config 轮询配置
|
||||
*/
|
||||
void pollGatewayDevice(GatewayPollingConfig config);
|
||||
|
||||
/**
|
||||
* 查询待轮询的网关设备列表
|
||||
*
|
||||
*
|
||||
* @param intervalSeconds 轮询间隔
|
||||
* @return 待轮询设备列表
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public interface IGatewayPollingService {
|
||||
|
||||
/**
|
||||
* 测试轮询 - 立即执行一次轮询
|
||||
*
|
||||
*
|
||||
* @param productKey 产品Key
|
||||
* @param deviceId 设备ID
|
||||
* @return 操作结果
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ public class ConnectionInfoServiceImpl implements ConnectionInfoService {
|
||||
.passthroughTopics(passthroughTopics)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
/** 构建订阅主题信息 */
|
||||
private ConnectionInfoDTO.TopicInfo buildSubscribeTopics(JSONObject config, String productKey) {
|
||||
String subscribe = config.getStr("subscribeTopics");
|
||||
|
||||
+18
-23
@@ -15,9 +15,6 @@ package cn.universal.admin.platform.service.impl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.admin.platform.service.DeviceCommandDispatcher;
|
||||
import cn.universal.admin.platform.service.IGatewayPollingService;
|
||||
import cn.universal.common.constant.IoTConstant.DeviceNode;
|
||||
@@ -32,8 +29,6 @@ import cn.universal.persistence.mapper.GatewayPollingConfigMapper;
|
||||
import cn.universal.persistence.mapper.IoTDeviceMapper;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import cn.universal.persistence.query.IoTAPIQuery;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -193,11 +188,12 @@ public class GatewayPollingServiceImpl implements IGatewayPollingService {
|
||||
IoTDevice gateway =
|
||||
deviceMapper.selectIoTDevice(config.getProductKey(), config.getDeviceId());
|
||||
|
||||
// if (gateway == null || !Boolean.TRUE.equals(gateway.getState())) {
|
||||
// log.warn(
|
||||
// "网关离线,跳过轮询: productKey={}, deviceId={}", config.getProductKey(), config.getDeviceId());
|
||||
// return;
|
||||
// }
|
||||
// if (gateway == null || !Boolean.TRUE.equals(gateway.getState())) {
|
||||
// log.warn(
|
||||
// "网关离线,跳过轮询: productKey={}, deviceId={}", config.getProductKey(),
|
||||
// config.getDeviceId());
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 2. 查询轮询指令 (使用 productKey + deviceId)
|
||||
List<GatewayPollingCommand> commands =
|
||||
@@ -219,8 +215,9 @@ public class GatewayPollingServiceImpl implements IGatewayPollingService {
|
||||
boolean allSuccess = true;
|
||||
int executedCount = 0;
|
||||
// 获取配置的指令间隔,默认300ms
|
||||
int commandIntervalMs = config.getCommandIntervalMs() != null ? config.getCommandIntervalMs() : 300;
|
||||
|
||||
int commandIntervalMs =
|
||||
config.getCommandIntervalMs() != null ? config.getCommandIntervalMs() : 300;
|
||||
|
||||
for (GatewayPollingCommand command : commands) {
|
||||
if (Boolean.TRUE.equals(command.getEnabled())) {
|
||||
// 非第一条指令先等待,给设备响应时间
|
||||
@@ -228,15 +225,13 @@ public class GatewayPollingServiceImpl implements IGatewayPollingService {
|
||||
try {
|
||||
Thread.sleep(commandIntervalMs);
|
||||
log.debug(
|
||||
"[指令间隔] 等待{}ms,防止设备缓冲区溢出: deviceId={}",
|
||||
commandIntervalMs,
|
||||
config.getDeviceId());
|
||||
"[指令间隔] 等待{}ms,防止设备缓冲区溢出: deviceId={}", commandIntervalMs, config.getDeviceId());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("指令间隔等待被中断", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean success = executeCommand(gateway, command);
|
||||
if (!success) {
|
||||
allSuccess = false;
|
||||
@@ -285,10 +280,7 @@ public class GatewayPollingServiceImpl implements IGatewayPollingService {
|
||||
IoTDeviceDTO device =
|
||||
deviceMapper.selectIoTDeviceBO(
|
||||
BeanUtil.beanToMap(
|
||||
IoTDevice.builder()
|
||||
.productKey(productKey)
|
||||
.deviceId(deviceId)
|
||||
.build()));
|
||||
IoTDevice.builder().productKey(productKey).deviceId(deviceId).build()));
|
||||
|
||||
if (device == null) {
|
||||
return R.error("设备不存在");
|
||||
@@ -306,13 +298,16 @@ public class GatewayPollingServiceImpl implements IGatewayPollingService {
|
||||
return R.error("未配置轮询指令,请先添加轮询指令");
|
||||
}
|
||||
|
||||
log.info("开始测试轮询: productKey={}, deviceId={}, commands={}",
|
||||
productKey, deviceId, commands.size());
|
||||
log.info(
|
||||
"开始测试轮询: productKey={}, deviceId={}, commands={}", productKey, deviceId, commands.size());
|
||||
|
||||
// 4. 立即执行轮询
|
||||
pollGatewayDevice(config);
|
||||
|
||||
return R.ok("测试轮询已执行,共发送 " + commands.stream().filter(cmd -> Boolean.TRUE.equals(cmd.getEnabled())).count() + " 条指令");
|
||||
return R.ok(
|
||||
"测试轮询已执行,共发送 "
|
||||
+ commands.stream().filter(cmd -> Boolean.TRUE.equals(cmd.getEnabled())).count()
|
||||
+ " 条指令");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("测试轮询失败: productKey={}, deviceId={}", productKey, deviceId, e);
|
||||
|
||||
+2
-1
@@ -546,7 +546,8 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
|
||||
|
||||
IoTProduct ioTProduct = ioTProductMapper.getProductByProductKey(productKey);
|
||||
// 转换为UnifiedDownlinkCommand(保持所有参数)
|
||||
UnifiedDownlinkCommand command = UnifiedDownlinkCommand.fromJson(JSONUtil.parseObj(downRequest));
|
||||
UnifiedDownlinkCommand command =
|
||||
UnifiedDownlinkCommand.fromJson(JSONUtil.parseObj(downRequest));
|
||||
R result = IoTDownlFactory.getIDown(ioTProduct.getThirdPlatform()).doAction(command);
|
||||
return result;
|
||||
}
|
||||
|
||||
+12
-27
@@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 网关轮询配置控制器
|
||||
*
|
||||
*
|
||||
* @author Aleo
|
||||
* @date 2025-10-26
|
||||
*/
|
||||
@@ -34,12 +34,9 @@ import org.springframework.web.bind.annotation.*;
|
||||
@Tag(name = "网关轮询管理", description = "网关云端轮询配置管理接口")
|
||||
public class GatewayPollingController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IGatewayPollingService gatewayPollingService;
|
||||
@Autowired private IGatewayPollingService gatewayPollingService;
|
||||
|
||||
/**
|
||||
* 保存网关轮询配置
|
||||
*/
|
||||
/** 保存网关轮询配置 */
|
||||
@Operation(summary = "保存轮询配置", description = "保存或更新网关轮询配置及指令")
|
||||
@PostMapping("/save")
|
||||
@PreAuthorize("@ss.hasPermi('platform:device:edit')")
|
||||
@@ -47,45 +44,33 @@ public class GatewayPollingController extends BaseController {
|
||||
return gatewayPollingService.savePollingConfig(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网关轮询配置
|
||||
*/
|
||||
/** 获取网关轮询配置 */
|
||||
@Operation(summary = "获取轮询配置", description = "根据产品Key和设备ID获取轮询配置")
|
||||
@GetMapping("/get/{productKey}/{deviceId}")
|
||||
@PreAuthorize("@ss.hasPermi('platform:device:query')")
|
||||
public R getConfig(
|
||||
@Parameter(description = "产品Key", required = true)
|
||||
@PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true)
|
||||
@PathVariable String deviceId) {
|
||||
@Parameter(description = "产品Key", required = true) @PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true) @PathVariable String deviceId) {
|
||||
return gatewayPollingService.getPollingConfig(productKey, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除网关轮询配置
|
||||
*/
|
||||
/** 删除网关轮询配置 */
|
||||
@Operation(summary = "删除轮询配置", description = "删除指定设备的轮询配置")
|
||||
@DeleteMapping("/delete/{productKey}/{deviceId}")
|
||||
@PreAuthorize("@ss.hasPermi('platform:device:remove')")
|
||||
public R deleteConfig(
|
||||
@Parameter(description = "产品Key", required = true)
|
||||
@PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true)
|
||||
@PathVariable String deviceId) {
|
||||
@Parameter(description = "产品Key", required = true) @PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true) @PathVariable String deviceId) {
|
||||
return gatewayPollingService.deletePollingConfig(productKey, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试轮询指令
|
||||
*/
|
||||
/** 测试轮询指令 */
|
||||
@Operation(summary = "测试轮询指令", description = "立即执行一次轮询测试")
|
||||
@PostMapping("/test/{productKey}/{deviceId}")
|
||||
@PreAuthorize("@ss.hasPermi('platform:device:edit')")
|
||||
public R testPolling(
|
||||
@Parameter(description = "产品Key", required = true)
|
||||
@PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true)
|
||||
@PathVariable String deviceId) {
|
||||
@Parameter(description = "产品Key", required = true) @PathVariable String productKey,
|
||||
@Parameter(description = "设备ID", required = true) @PathVariable String deviceId) {
|
||||
return gatewayPollingService.testPolling(productKey, deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-30
@@ -57,16 +57,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/admin/v1/protocol")
|
||||
public class IoTDeviceProtocolController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IoTDeviceProtocolService devProtocolService;
|
||||
@Resource
|
||||
private IoTDeviceMessageCodecService ioTDeviceMessageCodecService;
|
||||
@Resource
|
||||
private ISysOssService ossService;
|
||||
@Autowired private IoTDeviceProtocolService devProtocolService;
|
||||
@Resource private IoTDeviceMessageCodecService ioTDeviceMessageCodecService;
|
||||
@Resource private ISysOssService ossService;
|
||||
|
||||
/**
|
||||
* 查询设备协议列表
|
||||
*/
|
||||
/** 查询设备协议列表 */
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(IoTDeviceProtocol ioTDeviceProtocol) {
|
||||
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
|
||||
@@ -76,9 +71,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备协议列表
|
||||
*/
|
||||
/** 查询设备协议列表 */
|
||||
@GetMapping("/list/ids")
|
||||
public AjaxResult<List<String>> idList(IoTDeviceProtocol ioTDeviceProtocol) {
|
||||
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
|
||||
@@ -88,9 +81,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return AjaxResult.success(list1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出设备协议列表
|
||||
*/
|
||||
/** 导出设备协议列表 */
|
||||
@PostMapping("/export")
|
||||
@Log(title = "导出设备协议列表", businessType = BusinessType.EXPORT)
|
||||
public void export(HttpServletResponse response, IoTDeviceProtocol ioTDeviceProtocol) {
|
||||
@@ -101,9 +92,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
util.exportExcel(response, list, "设备协议数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备协议详细信息
|
||||
*/
|
||||
/** 获取设备协议详细信息 */
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
@@ -126,9 +115,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return AjaxResult.success(devProductVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备协议
|
||||
*/
|
||||
/** 新增设备协议 */
|
||||
@PostMapping
|
||||
@Log(title = "新增设备协议", businessType = BusinessType.INSERT)
|
||||
public AjaxResult add(@RequestBody IoTDeviceProtocolBO ioTDeviceProtocolBO) {
|
||||
@@ -157,9 +144,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return toAjax(devProtocolService.insertDevProtocol(ioTDeviceProtocol));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备协议
|
||||
*/
|
||||
/** 修改设备协议 */
|
||||
@PutMapping
|
||||
@Log(title = "修改设备协议", businessType = BusinessType.UPDATE)
|
||||
public AjaxResult edit(@RequestBody IoTDeviceProtocolBO ioTDeviceProtocolBO) {
|
||||
@@ -167,9 +152,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return toAjax(devProtocolService.updateDevProtocol(ioTDeviceProtocolBO, iotUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除设备协议
|
||||
*/
|
||||
/** 删除设备协议 */
|
||||
@DeleteMapping("/{ids}")
|
||||
@Log(title = "删除设备协议", businessType = BusinessType.DELETE)
|
||||
public AjaxResult remove(@PathVariable String[] ids) {
|
||||
@@ -182,9 +165,7 @@ public class IoTDeviceProtocolController extends BaseController {
|
||||
return toAjax(devProtocolService.deleteDevProtocolByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 编解码测试
|
||||
*/
|
||||
/** 编解码测试 */
|
||||
@PostMapping("/codec")
|
||||
public AjaxResult codec(@RequestBody IoTDeviceProtocolBO ioTDeviceProtocolBO) {
|
||||
|
||||
|
||||
+11
-9
@@ -45,7 +45,6 @@ import cn.universal.persistence.entity.admin.SysDictData;
|
||||
import cn.universal.persistence.entity.bo.IoTProductBO;
|
||||
import cn.universal.persistence.entity.bo.IoTProductImportBO;
|
||||
import cn.universal.persistence.entity.vo.IoTDeviceModelVO;
|
||||
import cn.universal.persistence.entity.vo.IoTProductExportVO;
|
||||
import cn.universal.persistence.entity.vo.IoTProductVO;
|
||||
import cn.universal.persistence.mapper.IoTProductMapper;
|
||||
import cn.universal.persistence.page.TableDataInfo;
|
||||
@@ -573,8 +572,9 @@ public class IoTProductController extends BaseController {
|
||||
|
||||
/**
|
||||
* 导出设备产品列表
|
||||
* @deprecated 请使用 ProductImportExportController.exportProductPackages() 代替
|
||||
* 该接口仅保留用于向后兼容,建议使用 /admin/v1/product/transfer/export
|
||||
*
|
||||
* @deprecated 请使用 ProductImportExportController.exportProductPackages() 代替 该接口仅保留用于向后兼容,建议使用
|
||||
* /admin/v1/product/transfer/export
|
||||
*/
|
||||
@Deprecated
|
||||
@PostMapping("/export")
|
||||
@@ -590,17 +590,17 @@ public class IoTProductController extends BaseController {
|
||||
}
|
||||
|
||||
// 使用新的导出包方法
|
||||
List<cn.universal.persistence.entity.dto.ProductExportPackageDTO> packageList =
|
||||
List<cn.universal.persistence.entity.dto.ProductExportPackageDTO> packageList =
|
||||
devProductService.exportProductPackages(query);
|
||||
|
||||
// 设置响应头为文件下载格式
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
|
||||
|
||||
// 生成文件名:产品导出_时间戳.json
|
||||
String fileName = String.format("products_export_%d.json", System.currentTimeMillis());
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
|
||||
|
||||
|
||||
// 添加缓存控制头,防止浏览器缓存
|
||||
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
@@ -629,8 +629,9 @@ public class IoTProductController extends BaseController {
|
||||
|
||||
/**
|
||||
* 批量导入产品
|
||||
* @deprecated 请使用 ProductImportExportController.importProductPackages() 代替
|
||||
* 该接口仅保留用于向后兼容,建议使用 /admin/v1/product/transfer/import
|
||||
*
|
||||
* @deprecated 请使用 ProductImportExportController.importProductPackages() 代替 该接口仅保留用于向后兼容,建议使用
|
||||
* /admin/v1/product/transfer/import
|
||||
*/
|
||||
@Deprecated
|
||||
@PostMapping("/import")
|
||||
@@ -645,7 +646,8 @@ public class IoTProductController extends BaseController {
|
||||
// 解析JSON数据 - 尝试解析为新格式(完整包)
|
||||
try {
|
||||
List<cn.universal.persistence.entity.dto.ProductExportPackageDTO> packages =
|
||||
JSONUtil.toList(jsonContent, cn.universal.persistence.entity.dto.ProductExportPackageDTO.class);
|
||||
JSONUtil.toList(
|
||||
jsonContent, cn.universal.persistence.entity.dto.ProductExportPackageDTO.class);
|
||||
|
||||
if (packages != null && !packages.isEmpty()) {
|
||||
// 使用新的导入方法
|
||||
|
||||
+2
-2
@@ -220,9 +220,9 @@ public class IoTUserAppController extends BaseController {
|
||||
@Log(title = "重置密钥", businessType = BusinessType.UPDATE)
|
||||
public AjaxResult<Void> resetSecret(@RequestBody IoTUserApplication iotUserApplication) {
|
||||
IoTUser user = loginIoTUnionUser(SecurityUtils.getUnionId());
|
||||
|
||||
|
||||
// 权限检查
|
||||
IoTUserApplication oldApplication =
|
||||
IoTUserApplication oldApplication =
|
||||
iotUserApplicationService.selectIotUserApplicationById(iotUserApplication.getAppUniqueId());
|
||||
if (!user.isAdmin()
|
||||
&& ObjectUtil.isNotEmpty(oldApplication)
|
||||
|
||||
+10
-19
@@ -36,9 +36,8 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 产品导入导出控制器
|
||||
* 专门处理产品的完整导出导入功能
|
||||
*
|
||||
* 产品导入导出控制器 专门处理产品的完整导出导入功能
|
||||
*
|
||||
* @author NexIoT
|
||||
* @since 2025-10-27
|
||||
*/
|
||||
@@ -50,11 +49,7 @@ public class ProductImportExportController extends BaseController {
|
||||
|
||||
@Autowired private IIoTProductService devProductService;
|
||||
|
||||
/**
|
||||
* 导出产品完整包
|
||||
* 包含产品基本信息、设备协议、网络配置、物模型等所有配置
|
||||
* 导出后可直接在其他环境导入使用,无需额外配置
|
||||
*/
|
||||
/** 导出产品完整包 包含产品基本信息、设备协议、网络配置、物模型等所有配置 导出后可直接在其他环境导入使用,无需额外配置 */
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出产品完整包", description = "导出产品及其关联的协议、网络配置、物模型等完整信息")
|
||||
@Log(title = "产品完整包导出", businessType = BusinessType.EXPORT)
|
||||
@@ -62,7 +57,7 @@ public class ProductImportExportController extends BaseController {
|
||||
try {
|
||||
// 获取当前用户信息
|
||||
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
|
||||
|
||||
|
||||
// 权限控制:非管理员只能导出自己创建的产品
|
||||
query.setSelf(!iotUser.isAdmin());
|
||||
if (query.isSelf()) {
|
||||
@@ -78,11 +73,11 @@ public class ProductImportExportController extends BaseController {
|
||||
// 设置响应头
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
|
||||
|
||||
// 生成文件名:产品导出包_时间戳.json
|
||||
String fileName = String.format("product_packages_%d.json", System.currentTimeMillis());
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
|
||||
|
||||
|
||||
// 缓存控制
|
||||
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
@@ -100,7 +95,7 @@ public class ProductImportExportController extends BaseController {
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
||||
log.info("产品导出成功: 用户={}, 导出数量={}", iotUser.getUnionId(), packageList.size());
|
||||
} catch (Exception e) {
|
||||
log.error("产品导出失败", e);
|
||||
@@ -108,11 +103,7 @@ public class ProductImportExportController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入产品完整包
|
||||
* 支持导入完整的产品配置,包括协议、网络、物模型等
|
||||
* 自动处理ProductKey冲突、协议依赖等问题
|
||||
*/
|
||||
/** 导入产品完整包 支持导入完整的产品配置,包括协议、网络、物模型等 自动处理ProductKey冲突、协议依赖等问题 */
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入产品完整包", description = "导入产品完整配置包,自动处理所有依赖关系")
|
||||
@Log(title = "产品完整包导入", businessType = BusinessType.IMPORT)
|
||||
@@ -121,7 +112,7 @@ public class ProductImportExportController extends BaseController {
|
||||
try {
|
||||
// 获取当前用户ID
|
||||
String unionId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
|
||||
|
||||
|
||||
// 读取上传的JSON文件
|
||||
String jsonContent = new String(file.getBytes(), "UTF-8");
|
||||
|
||||
@@ -135,7 +126,7 @@ public class ProductImportExportController extends BaseController {
|
||||
|
||||
// 执行导入
|
||||
String result = devProductService.importProductPackages(packages, unionId);
|
||||
|
||||
|
||||
log.info("产品导入完成: 用户={}, 导入数量={}", unionId, packages.size());
|
||||
return AjaxResult.success(result);
|
||||
} catch (Exception e) {
|
||||
|
||||
+30
-41
@@ -17,56 +17,45 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 系统通知Controller
|
||||
*
|
||||
*
|
||||
* @author universal
|
||||
* @date 2023-01-01
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/notice")
|
||||
public class NoticeController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private ISysNoticeService noticeService;
|
||||
|
||||
/**
|
||||
* 查询系统通知列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysNotice> list(SysNotice notice) {
|
||||
startPage();
|
||||
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
||||
return getDataTable(list);
|
||||
}
|
||||
@Autowired private ISysNoticeService noticeService;
|
||||
|
||||
/**
|
||||
* 获取系统通知详细信息
|
||||
*/
|
||||
@GetMapping(value = "/{noticeId}")
|
||||
public SysNotice getInfo(@PathVariable("noticeId") Long noticeId) {
|
||||
return noticeService.selectNoticeById(noticeId);
|
||||
}
|
||||
/** 查询系统通知列表 */
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysNotice> list(SysNotice notice) {
|
||||
startPage();
|
||||
List<SysNotice> list = noticeService.selectNoticeList(notice);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增系统通知
|
||||
*/
|
||||
@PostMapping
|
||||
public void add(@RequestBody SysNotice notice) {
|
||||
noticeService.insertNotice(notice);
|
||||
}
|
||||
/** 获取系统通知详细信息 */
|
||||
@GetMapping(value = "/{noticeId}")
|
||||
public SysNotice getInfo(@PathVariable("noticeId") Long noticeId) {
|
||||
return noticeService.selectNoticeById(noticeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改系统通知
|
||||
*/
|
||||
@PutMapping
|
||||
public void edit(@RequestBody SysNotice notice) {
|
||||
noticeService.updateNotice(notice);
|
||||
}
|
||||
/** 新增系统通知 */
|
||||
@PostMapping
|
||||
public void add(@RequestBody SysNotice notice) {
|
||||
noticeService.insertNotice(notice);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除系统通知
|
||||
*/
|
||||
@DeleteMapping("/{noticeIds}")
|
||||
public void remove(@PathVariable Long[] noticeIds) {
|
||||
noticeService.deleteNoticeByIds(noticeIds);
|
||||
}
|
||||
/** 修改系统通知 */
|
||||
@PutMapping
|
||||
public void edit(@RequestBody SysNotice notice) {
|
||||
noticeService.updateNotice(notice);
|
||||
}
|
||||
|
||||
/** 删除系统通知 */
|
||||
@DeleteMapping("/{noticeIds}")
|
||||
public void remove(@PathVariable Long[] noticeIds) {
|
||||
noticeService.deleteNoticeByIds(noticeIds);
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -80,7 +80,6 @@ public interface ISysDictDataService {
|
||||
*/
|
||||
Map<String, Object> selectDictDataByTypes(String[] dictType);
|
||||
|
||||
|
||||
/**
|
||||
* 根据label标签,查找白名单
|
||||
*
|
||||
|
||||
+43
-43
@@ -5,56 +5,56 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统通知Service接口
|
||||
*
|
||||
*
|
||||
* @author universal
|
||||
* @date 2023-01-01
|
||||
*/
|
||||
public interface ISysNoticeService {
|
||||
/**
|
||||
* 查询系统通知
|
||||
*
|
||||
* @param noticeId 系统通知主键
|
||||
* @return 系统通知
|
||||
*/
|
||||
public SysNotice selectNoticeById(Long noticeId);
|
||||
/**
|
||||
* 查询系统通知
|
||||
*
|
||||
* @param noticeId 系统通知主键
|
||||
* @return 系统通知
|
||||
*/
|
||||
public SysNotice selectNoticeById(Long noticeId);
|
||||
|
||||
/**
|
||||
* 查询系统通知列表
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 系统通知集合
|
||||
*/
|
||||
public List<SysNotice> selectNoticeList(SysNotice notice);
|
||||
/**
|
||||
* 查询系统通知列表
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 系统通知集合
|
||||
*/
|
||||
public List<SysNotice> selectNoticeList(SysNotice notice);
|
||||
|
||||
/**
|
||||
* 新增系统通知
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertNotice(SysNotice notice);
|
||||
/**
|
||||
* 新增系统通知
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertNotice(SysNotice notice);
|
||||
|
||||
/**
|
||||
* 修改系统通知
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateNotice(SysNotice notice);
|
||||
/**
|
||||
* 修改系统通知
|
||||
*
|
||||
* @param notice 系统通知
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateNotice(SysNotice notice);
|
||||
|
||||
/**
|
||||
* 批量删除系统通知
|
||||
*
|
||||
* @param noticeIds 需要删除的系统通知主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteNoticeByIds(Long[] noticeIds);
|
||||
/**
|
||||
* 批量删除系统通知
|
||||
*
|
||||
* @param noticeIds 需要删除的系统通知主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteNoticeByIds(Long[] noticeIds);
|
||||
|
||||
/**
|
||||
* 删除系统通知信息
|
||||
*
|
||||
* @param noticeId 系统通知主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteNoticeById(Long noticeId);
|
||||
/**
|
||||
* 删除系统通知信息
|
||||
*
|
||||
* @param noticeId 系统通知主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteNoticeById(Long noticeId);
|
||||
}
|
||||
|
||||
-1
@@ -187,7 +187,6 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
|
||||
}
|
||||
|
||||
/** 新增字典类型(进销人员) */
|
||||
|
||||
@Override
|
||||
@Cacheable(
|
||||
cacheNames = "selectWhitelist",
|
||||
|
||||
+2
-1
@@ -133,7 +133,8 @@ public class RulegoChainController extends BaseController {
|
||||
|
||||
@Operation(summary = "获取设计器URL")
|
||||
@GetMapping("/chain/{id}/designer")
|
||||
public AjaxResult<String> getDesignerUrl(@PathVariable @Parameter(description = "规则链ID") Long id) {
|
||||
public AjaxResult<String> getDesignerUrl(
|
||||
@PathVariable @Parameter(description = "规则链ID") Long id) {
|
||||
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
|
||||
RulegoChainVO rulegoChain = rulegoChainService.queryRulegoChainById(id, creatorId);
|
||||
String designerUrl = rulegoChainService.getDesignerUrl(rulegoChain.getRulegoId());
|
||||
|
||||
-1
@@ -87,7 +87,6 @@ public class SysDictDataController extends BaseController {
|
||||
return toAjax(dictDataService.insertDictData(dict));
|
||||
}
|
||||
|
||||
|
||||
/** 修改保存字典类型 */
|
||||
@PutMapping
|
||||
public AjaxResult edit(@Validated @RequestBody SysDictData dict) {
|
||||
|
||||
@@ -1,161 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.universal.admin.generator.mapper.GenTableColumnMapper">
|
||||
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTableColumn" id="GenTableColumnResult">
|
||||
<id property="columnId" column="column_id"/>
|
||||
<result property="tableId" column="table_id"/>
|
||||
<result property="columnName" column="column_name"/>
|
||||
<result property="columnComment" column="column_comment"/>
|
||||
<result property="columnType" column="column_type"/>
|
||||
<result property="javaType" column="java_type"/>
|
||||
<result property="javaField" column="java_field"/>
|
||||
<result property="isPk" column="is_pk"/>
|
||||
<result property="isIncrement" column="is_increment"/>
|
||||
<result property="isRequired" column="is_required"/>
|
||||
<result property="isInsert" column="is_insert"/>
|
||||
<result property="isEdit" column="is_edit"/>
|
||||
<result property="isList" column="is_list"/>
|
||||
<result property="isQuery" column="is_query"/>
|
||||
<result property="queryType" column="query_type"/>
|
||||
<result property="htmlType" column="html_type"/>
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="sort" column="sort"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTableColumn" id="GenTableColumnResult">
|
||||
<id property="columnId" column="column_id"/>
|
||||
<result property="tableId" column="table_id"/>
|
||||
<result property="columnName" column="column_name"/>
|
||||
<result property="columnComment" column="column_comment"/>
|
||||
<result property="columnType" column="column_type"/>
|
||||
<result property="javaType" column="java_type"/>
|
||||
<result property="javaField" column="java_field"/>
|
||||
<result property="isPk" column="is_pk"/>
|
||||
<result property="isIncrement" column="is_increment"/>
|
||||
<result property="isRequired" column="is_required"/>
|
||||
<result property="isInsert" column="is_insert"/>
|
||||
<result property="isEdit" column="is_edit"/>
|
||||
<result property="isList" column="is_list"/>
|
||||
<result property="isQuery" column="is_query"/>
|
||||
<result property="queryType" column="query_type"/>
|
||||
<result property="htmlType" column="html_type"/>
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="sort" column="sort"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectGenTableColumnVo">
|
||||
select column_id,
|
||||
table_id,
|
||||
column_name,
|
||||
column_comment,
|
||||
column_type,
|
||||
java_type,
|
||||
java_field,
|
||||
is_pk,
|
||||
is_increment,
|
||||
is_required,
|
||||
is_insert,
|
||||
is_edit,
|
||||
is_list,
|
||||
is_query,
|
||||
query_type,
|
||||
html_type,
|
||||
dict_type,
|
||||
sort,
|
||||
create_by,
|
||||
create_time,
|
||||
update_by,
|
||||
update_time
|
||||
from gen_table_column
|
||||
</sql>
|
||||
<sql id="selectGenTableColumnVo">
|
||||
select column_id,
|
||||
table_id,
|
||||
column_name,
|
||||
column_comment,
|
||||
column_type,
|
||||
java_type,
|
||||
java_field,
|
||||
is_pk,
|
||||
is_increment,
|
||||
is_required,
|
||||
is_insert,
|
||||
is_edit,
|
||||
is_list,
|
||||
is_query,
|
||||
query_type,
|
||||
html_type,
|
||||
dict_type,
|
||||
sort,
|
||||
create_by,
|
||||
create_time,
|
||||
update_by,
|
||||
update_time
|
||||
from gen_table_column
|
||||
</sql>
|
||||
|
||||
<select id="selectGenTableColumnListByTableId" parameterType="Long"
|
||||
resultMap="GenTableColumnResult">
|
||||
<include refid="selectGenTableColumnVo"/>
|
||||
where table_id = #{tableId}
|
||||
order by sort
|
||||
</select>
|
||||
<select id="selectGenTableColumnListByTableId" parameterType="Long"
|
||||
resultMap="GenTableColumnResult">
|
||||
<include refid="selectGenTableColumnVo"/>
|
||||
where table_id = #{tableId}
|
||||
order by sort
|
||||
</select>
|
||||
|
||||
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
|
||||
select column_name,
|
||||
(case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,
|
||||
(case when column_key = 'PRI' then '1' else '0' end) as is_pk,
|
||||
ordinal_position as sort,
|
||||
column_comment,
|
||||
(case when extra = 'auto_increment' then '1' else '0' end) as is_increment,
|
||||
column_type
|
||||
from information_schema.columns
|
||||
where table_schema = (select database())
|
||||
and table_name = (#{tableName})
|
||||
order by ordinal_position
|
||||
</select>
|
||||
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult">
|
||||
select column_name,
|
||||
(case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required,
|
||||
(case when column_key = 'PRI' then '1' else '0' end) as is_pk,
|
||||
ordinal_position as sort,
|
||||
column_comment,
|
||||
(case when extra = 'auto_increment' then '1' else '0' end) as is_increment,
|
||||
column_type
|
||||
from information_schema.columns
|
||||
where table_schema = (select database())
|
||||
and table_name = (#{tableName})
|
||||
order by ordinal_position
|
||||
</select>
|
||||
|
||||
<insert id="insertGenTableColumn"
|
||||
parameterType="cn.universal.admin.generator.domain.GenTableColumn" useGeneratedKeys="true"
|
||||
keyProperty="columnId">
|
||||
insert into gen_table_column (
|
||||
<if test="tableId != null and tableId != ''">table_id,</if>
|
||||
<if test="columnName != null and columnName != ''">column_name,</if>
|
||||
<if test="columnComment != null and columnComment != ''">column_comment,</if>
|
||||
<if test="columnType != null and columnType != ''">column_type,</if>
|
||||
<if test="javaType != null and javaType != ''">java_type,</if>
|
||||
<if test="javaField != null and javaField != ''">java_field,</if>
|
||||
<if test="isPk != null and isPk != ''">is_pk,</if>
|
||||
<if test="isIncrement != null and isIncrement != ''">is_increment,</if>
|
||||
<if test="isRequired != null and isRequired != ''">is_required,</if>
|
||||
<if test="isInsert != null and isInsert != ''">is_insert,</if>
|
||||
<if test="isEdit != null and isEdit != ''">is_edit,</if>
|
||||
<if test="isList != null and isList != ''">is_list,</if>
|
||||
<if test="isQuery != null and isQuery != ''">is_query,</if>
|
||||
<if test="queryType != null and queryType != ''">query_type,</if>
|
||||
<if test="htmlType != null and htmlType != ''">html_type,</if>
|
||||
<if test="dictType != null and dictType != ''">dict_type,</if>
|
||||
<if test="sort != null">sort,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="tableId != null and tableId != ''">#{tableId},</if>
|
||||
<if test="columnName != null and columnName != ''">#{columnName},</if>
|
||||
<if test="columnComment != null and columnComment != ''">#{columnComment},</if>
|
||||
<if test="columnType != null and columnType != ''">#{columnType},</if>
|
||||
<if test="javaType != null and javaType != ''">#{javaType},</if>
|
||||
<if test="javaField != null and javaField != ''">#{javaField},</if>
|
||||
<if test="isPk != null and isPk != ''">#{isPk},</if>
|
||||
<if test="isIncrement != null and isIncrement != ''">#{isIncrement},</if>
|
||||
<if test="isRequired != null and isRequired != ''">#{isRequired},</if>
|
||||
<if test="isInsert != null and isInsert != ''">#{isInsert},</if>
|
||||
<if test="isEdit != null and isEdit != ''">#{isEdit},</if>
|
||||
<if test="isList != null and isList != ''">#{isList},</if>
|
||||
<if test="isQuery != null and isQuery != ''">#{isQuery},</if>
|
||||
<if test="queryType != null and queryType != ''">#{queryType},</if>
|
||||
<if test="htmlType != null and htmlType != ''">#{htmlType},</if>
|
||||
<if test="dictType != null and dictType != ''">#{dictType},</if>
|
||||
<if test="sort != null">#{sort},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
<insert id="insertGenTableColumn"
|
||||
parameterType="cn.universal.admin.generator.domain.GenTableColumn" useGeneratedKeys="true"
|
||||
keyProperty="columnId">
|
||||
insert into gen_table_column (
|
||||
<if test="tableId != null and tableId != ''">table_id,</if>
|
||||
<if test="columnName != null and columnName != ''">column_name,</if>
|
||||
<if test="columnComment != null and columnComment != ''">column_comment,</if>
|
||||
<if test="columnType != null and columnType != ''">column_type,</if>
|
||||
<if test="javaType != null and javaType != ''">java_type,</if>
|
||||
<if test="javaField != null and javaField != ''">java_field,</if>
|
||||
<if test="isPk != null and isPk != ''">is_pk,</if>
|
||||
<if test="isIncrement != null and isIncrement != ''">is_increment,</if>
|
||||
<if test="isRequired != null and isRequired != ''">is_required,</if>
|
||||
<if test="isInsert != null and isInsert != ''">is_insert,</if>
|
||||
<if test="isEdit != null and isEdit != ''">is_edit,</if>
|
||||
<if test="isList != null and isList != ''">is_list,</if>
|
||||
<if test="isQuery != null and isQuery != ''">is_query,</if>
|
||||
<if test="queryType != null and queryType != ''">query_type,</if>
|
||||
<if test="htmlType != null and htmlType != ''">html_type,</if>
|
||||
<if test="dictType != null and dictType != ''">dict_type,</if>
|
||||
<if test="sort != null">sort,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="tableId != null and tableId != ''">#{tableId},</if>
|
||||
<if test="columnName != null and columnName != ''">#{columnName},</if>
|
||||
<if test="columnComment != null and columnComment != ''">#{columnComment},</if>
|
||||
<if test="columnType != null and columnType != ''">#{columnType},</if>
|
||||
<if test="javaType != null and javaType != ''">#{javaType},</if>
|
||||
<if test="javaField != null and javaField != ''">#{javaField},</if>
|
||||
<if test="isPk != null and isPk != ''">#{isPk},</if>
|
||||
<if test="isIncrement != null and isIncrement != ''">#{isIncrement},</if>
|
||||
<if test="isRequired != null and isRequired != ''">#{isRequired},</if>
|
||||
<if test="isInsert != null and isInsert != ''">#{isInsert},</if>
|
||||
<if test="isEdit != null and isEdit != ''">#{isEdit},</if>
|
||||
<if test="isList != null and isList != ''">#{isList},</if>
|
||||
<if test="isQuery != null and isQuery != ''">#{isQuery},</if>
|
||||
<if test="queryType != null and queryType != ''">#{queryType},</if>
|
||||
<if test="htmlType != null and htmlType != ''">#{htmlType},</if>
|
||||
<if test="dictType != null and dictType != ''">#{dictType},</if>
|
||||
<if test="sort != null">#{sort},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateGenTableColumn"
|
||||
parameterType="cn.universal.admin.generator.domain.GenTableColumn">
|
||||
update gen_table_column
|
||||
<set>
|
||||
column_comment = #{columnComment},
|
||||
java_type = #{javaType},
|
||||
java_field = #{javaField},
|
||||
is_insert = #{isInsert},
|
||||
is_edit = #{isEdit},
|
||||
is_list = #{isList},
|
||||
is_query = #{isQuery},
|
||||
is_required = #{isRequired},
|
||||
query_type = #{queryType},
|
||||
html_type = #{htmlType},
|
||||
dict_type = #{dictType},
|
||||
sort = #{sort},
|
||||
update_by = #{updateBy},
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where column_id = #{columnId}
|
||||
</update>
|
||||
<update id="updateGenTableColumn"
|
||||
parameterType="cn.universal.admin.generator.domain.GenTableColumn">
|
||||
update gen_table_column
|
||||
<set>
|
||||
column_comment = #{columnComment},
|
||||
java_type = #{javaType},
|
||||
java_field = #{javaField},
|
||||
is_insert = #{isInsert},
|
||||
is_edit = #{isEdit},
|
||||
is_list = #{isList},
|
||||
is_query = #{isQuery},
|
||||
is_required = #{isRequired},
|
||||
query_type = #{queryType},
|
||||
html_type = #{htmlType},
|
||||
dict_type = #{dictType},
|
||||
sort = #{sort},
|
||||
update_by = #{updateBy},
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where column_id = #{columnId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteGenTableColumnByIds" parameterType="Long">
|
||||
delete from gen_table_column where table_id in
|
||||
<foreach collection="array" item="tableId" open="(" separator="," close=")">
|
||||
#{tableId}
|
||||
</foreach>
|
||||
</delete>
|
||||
<delete id="deleteGenTableColumnByIds" parameterType="Long">
|
||||
delete from gen_table_column where table_id in
|
||||
<foreach collection="array" item="tableId" open="(" separator="," close=")">
|
||||
#{tableId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteGenTableColumns">
|
||||
delete from gen_table_column where column_id in
|
||||
<foreach collection="list" item="item" open="(" separator="," close=")">
|
||||
#{item.columnId}
|
||||
</foreach>
|
||||
</delete>
|
||||
<delete id="deleteGenTableColumns">
|
||||
delete from gen_table_column where column_id in
|
||||
<foreach collection="list" item="item" open="(" separator="," close=")">
|
||||
#{item.columnId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
@@ -1,323 +1,323 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.universal.admin.generator.mapper.GenTableMapper">
|
||||
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTable" id="GenTableResult">
|
||||
<id property="tableId" column="table_id"/>
|
||||
<result property="tableName" column="table_name"/>
|
||||
<result property="tableComment" column="table_comment"/>
|
||||
<result property="subTableName" column="sub_table_name"/>
|
||||
<result property="subTableFkName" column="sub_table_fk_name"/>
|
||||
<result property="className" column="class_name"/>
|
||||
<result property="tplCategory" column="tpl_category"/>
|
||||
<result property="packageName" column="package_name"/>
|
||||
<result property="moduleName" column="module_name"/>
|
||||
<result property="businessName" column="business_name"/>
|
||||
<result property="functionName" column="function_name"/>
|
||||
<result property="functionAuthor" column="function_Author"/>
|
||||
<result property="genType" column="gen_type"/>
|
||||
<result property="genPath" column="gen_path"/>
|
||||
<result property="options" column="options"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult"/>
|
||||
</resultMap>
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTable" id="GenTableResult">
|
||||
<id property="tableId" column="table_id"/>
|
||||
<result property="tableName" column="table_name"/>
|
||||
<result property="tableComment" column="table_comment"/>
|
||||
<result property="subTableName" column="sub_table_name"/>
|
||||
<result property="subTableFkName" column="sub_table_fk_name"/>
|
||||
<result property="className" column="class_name"/>
|
||||
<result property="tplCategory" column="tpl_category"/>
|
||||
<result property="packageName" column="package_name"/>
|
||||
<result property="moduleName" column="module_name"/>
|
||||
<result property="businessName" column="business_name"/>
|
||||
<result property="functionName" column="function_name"/>
|
||||
<result property="functionAuthor" column="function_Author"/>
|
||||
<result property="genType" column="gen_type"/>
|
||||
<result property="genPath" column="gen_path"/>
|
||||
<result property="options" column="options"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult"/>
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTableColumn" id="GenTableColumnResult">
|
||||
<id property="columnId" column="column_id"/>
|
||||
<result property="tableId" column="table_id"/>
|
||||
<result property="columnName" column="column_name"/>
|
||||
<result property="columnComment" column="column_comment"/>
|
||||
<result property="columnType" column="column_type"/>
|
||||
<result property="javaType" column="java_type"/>
|
||||
<result property="javaField" column="java_field"/>
|
||||
<result property="isPk" column="is_pk"/>
|
||||
<result property="isIncrement" column="is_increment"/>
|
||||
<result property="isRequired" column="is_required"/>
|
||||
<result property="isInsert" column="is_insert"/>
|
||||
<result property="isEdit" column="is_edit"/>
|
||||
<result property="isList" column="is_list"/>
|
||||
<result property="isQuery" column="is_query"/>
|
||||
<result property="queryType" column="query_type"/>
|
||||
<result property="htmlType" column="html_type"/>
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="sort" column="sort"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
<resultMap type="cn.universal.admin.generator.domain.GenTableColumn" id="GenTableColumnResult">
|
||||
<id property="columnId" column="column_id"/>
|
||||
<result property="tableId" column="table_id"/>
|
||||
<result property="columnName" column="column_name"/>
|
||||
<result property="columnComment" column="column_comment"/>
|
||||
<result property="columnType" column="column_type"/>
|
||||
<result property="javaType" column="java_type"/>
|
||||
<result property="javaField" column="java_field"/>
|
||||
<result property="isPk" column="is_pk"/>
|
||||
<result property="isIncrement" column="is_increment"/>
|
||||
<result property="isRequired" column="is_required"/>
|
||||
<result property="isInsert" column="is_insert"/>
|
||||
<result property="isEdit" column="is_edit"/>
|
||||
<result property="isList" column="is_list"/>
|
||||
<result property="isQuery" column="is_query"/>
|
||||
<result property="queryType" column="query_type"/>
|
||||
<result property="htmlType" column="html_type"/>
|
||||
<result property="dictType" column="dict_type"/>
|
||||
<result property="sort" column="sort"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectGenTableVo">
|
||||
select table_id,
|
||||
table_name,
|
||||
table_comment,
|
||||
sub_table_name,
|
||||
sub_table_fk_name,
|
||||
class_name,
|
||||
tpl_category,
|
||||
package_name,
|
||||
module_name,
|
||||
business_name,
|
||||
function_name,
|
||||
function_Author,
|
||||
gen_type,
|
||||
gen_path,
|
||||
options,
|
||||
create_by,
|
||||
create_time,
|
||||
update_by,
|
||||
update_time,
|
||||
remark
|
||||
from gen_table
|
||||
</sql>
|
||||
<sql id="selectGenTableVo">
|
||||
select table_id,
|
||||
table_name,
|
||||
table_comment,
|
||||
sub_table_name,
|
||||
sub_table_fk_name,
|
||||
class_name,
|
||||
tpl_category,
|
||||
package_name,
|
||||
module_name,
|
||||
business_name,
|
||||
function_name,
|
||||
function_Author,
|
||||
gen_type,
|
||||
gen_path,
|
||||
options,
|
||||
create_by,
|
||||
create_time,
|
||||
update_by,
|
||||
update_time,
|
||||
remark
|
||||
from gen_table
|
||||
</sql>
|
||||
|
||||
<select id="selectGenTableList" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
resultMap="GenTableResult">
|
||||
<include refid="selectGenTableVo"/>
|
||||
<where>
|
||||
<if test="tableName != null and tableName != ''">
|
||||
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
|
||||
</if>
|
||||
<if test="tableComment != null and tableComment != ''">
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
<select id="selectGenTableList" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
resultMap="GenTableResult">
|
||||
<include refid="selectGenTableVo"/>
|
||||
<where>
|
||||
<if test="tableName != null and tableName != ''">
|
||||
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
|
||||
</if>
|
||||
<if test="tableComment != null and tableComment != ''">
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectDbTableList" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||
where table_schema = (select database())
|
||||
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
|
||||
AND table_name NOT IN (select table_name from gen_table)
|
||||
<if test="tableName != null and tableName != ''">
|
||||
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
|
||||
</if>
|
||||
<if test="tableComment != null and tableComment != ''">
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
order by create_time desc
|
||||
</select>
|
||||
<select id="selectDbTableList" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||
where table_schema = (select database())
|
||||
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
|
||||
AND table_name NOT IN (select table_name from gen_table)
|
||||
<if test="tableName != null and tableName != ''">
|
||||
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
|
||||
</if>
|
||||
<if test="tableComment != null and tableComment != ''">
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectDbTableListByNames" resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select
|
||||
database())
|
||||
and table_name in
|
||||
<foreach collection="array" item="name" open="(" separator="," close=")">
|
||||
#{name}
|
||||
</foreach>
|
||||
</select>
|
||||
<select id="selectDbTableListByNames" resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select
|
||||
database())
|
||||
and table_name in
|
||||
<foreach collection="array" item="name" open="(" separator="," close=")">
|
||||
#{name}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time
|
||||
from information_schema.tables
|
||||
where table_comment <![CDATA[ <> ]]> ''
|
||||
and table_schema = (select database())
|
||||
and table_name = #{tableName}
|
||||
</select>
|
||||
<select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time
|
||||
from information_schema.tables
|
||||
where table_comment <![CDATA[ <> ]]> ''
|
||||
and table_schema = (select database())
|
||||
and table_name = #{tableName}
|
||||
</select>
|
||||
|
||||
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.gen_type,
|
||||
t.gen_path,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
where t.table_id = #{tableId}
|
||||
order by c.sort
|
||||
</select>
|
||||
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.gen_type,
|
||||
t.gen_path,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
where t.table_id = #{tableId}
|
||||
order by c.sort
|
||||
</select>
|
||||
|
||||
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.gen_type,
|
||||
t.gen_path,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
where t.table_name = #{tableName}
|
||||
order by c.sort
|
||||
</select>
|
||||
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.gen_type,
|
||||
t.gen_path,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
where t.table_name = #{tableName}
|
||||
order by c.sort
|
||||
</select>
|
||||
|
||||
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
order by c.sort
|
||||
</select>
|
||||
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
|
||||
SELECT t.table_id,
|
||||
t.table_name,
|
||||
t.table_comment,
|
||||
t.sub_table_name,
|
||||
t.sub_table_fk_name,
|
||||
t.class_name,
|
||||
t.tpl_category,
|
||||
t.package_name,
|
||||
t.module_name,
|
||||
t.business_name,
|
||||
t.function_name,
|
||||
t.function_Author,
|
||||
t.options,
|
||||
t.remark,
|
||||
c.column_id,
|
||||
c.column_name,
|
||||
c.column_comment,
|
||||
c.column_type,
|
||||
c.java_type,
|
||||
c.java_field,
|
||||
c.is_pk,
|
||||
c.is_increment,
|
||||
c.is_required,
|
||||
c.is_insert,
|
||||
c.is_edit,
|
||||
c.is_list,
|
||||
c.is_query,
|
||||
c.query_type,
|
||||
c.html_type,
|
||||
c.dict_type,
|
||||
c.sort
|
||||
FROM gen_table t
|
||||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
|
||||
order by c.sort
|
||||
</select>
|
||||
|
||||
<insert id="insertGenTable" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
useGeneratedKeys="true" keyProperty="tableId">
|
||||
insert into gen_table (
|
||||
<if test="tableName != null">table_name,</if>
|
||||
<if test="tableComment != null and tableComment != ''">table_comment,</if>
|
||||
<if test="className != null and className != ''">class_name,</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">tpl_category,</if>
|
||||
<if test="packageName != null and packageName != ''">package_name,</if>
|
||||
<if test="moduleName != null and moduleName != ''">module_name,</if>
|
||||
<if test="businessName != null and businessName != ''">business_name,</if>
|
||||
<if test="functionName != null and functionName != ''">function_name,</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">function_Author,</if>
|
||||
<if test="genType != null and genType != ''">gen_type,</if>
|
||||
<if test="genPath != null and genPath != ''">gen_path,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="tableName != null">#{tableName},</if>
|
||||
<if test="tableComment != null and tableComment != ''">#{tableComment},</if>
|
||||
<if test="className != null and className != ''">#{className},</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if>
|
||||
<if test="packageName != null and packageName != ''">#{packageName},</if>
|
||||
<if test="moduleName != null and moduleName != ''">#{moduleName},</if>
|
||||
<if test="businessName != null and businessName != ''">#{businessName},</if>
|
||||
<if test="functionName != null and functionName != ''">#{functionName},</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">#{functionAuthor},</if>
|
||||
<if test="genType != null and genType != ''">#{genType},</if>
|
||||
<if test="genPath != null and genPath != ''">#{genPath},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
<insert id="insertGenTable" parameterType="cn.universal.admin.generator.domain.GenTable"
|
||||
useGeneratedKeys="true" keyProperty="tableId">
|
||||
insert into gen_table (
|
||||
<if test="tableName != null">table_name,</if>
|
||||
<if test="tableComment != null and tableComment != ''">table_comment,</if>
|
||||
<if test="className != null and className != ''">class_name,</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">tpl_category,</if>
|
||||
<if test="packageName != null and packageName != ''">package_name,</if>
|
||||
<if test="moduleName != null and moduleName != ''">module_name,</if>
|
||||
<if test="businessName != null and businessName != ''">business_name,</if>
|
||||
<if test="functionName != null and functionName != ''">function_name,</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">function_Author,</if>
|
||||
<if test="genType != null and genType != ''">gen_type,</if>
|
||||
<if test="genPath != null and genPath != ''">gen_path,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="tableName != null">#{tableName},</if>
|
||||
<if test="tableComment != null and tableComment != ''">#{tableComment},</if>
|
||||
<if test="className != null and className != ''">#{className},</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if>
|
||||
<if test="packageName != null and packageName != ''">#{packageName},</if>
|
||||
<if test="moduleName != null and moduleName != ''">#{moduleName},</if>
|
||||
<if test="businessName != null and businessName != ''">#{businessName},</if>
|
||||
<if test="functionName != null and functionName != ''">#{functionName},</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">#{functionAuthor},</if>
|
||||
<if test="genType != null and genType != ''">#{genType},</if>
|
||||
<if test="genPath != null and genPath != ''">#{genPath},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateGenTable" parameterType="cn.universal.admin.generator.domain.GenTable">
|
||||
update gen_table
|
||||
<set>
|
||||
<if test="tableName != null">table_name = #{tableName},</if>
|
||||
<if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if>
|
||||
<if test="subTableName != null">sub_table_name = #{subTableName},</if>
|
||||
<if test="subTableFkName != null">sub_table_fk_name = #{subTableFkName},</if>
|
||||
<if test="className != null and className != ''">class_name = #{className},</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">function_Author =
|
||||
#{functionAuthor},
|
||||
</if>
|
||||
<if test="genType != null and genType != ''">gen_type = #{genType},</if>
|
||||
<if test="genPath != null and genPath != ''">gen_path = #{genPath},</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if>
|
||||
<if test="packageName != null and packageName != ''">package_name = #{packageName},</if>
|
||||
<if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if>
|
||||
<if test="businessName != null and businessName != ''">business_name = #{businessName},</if>
|
||||
<if test="functionName != null and functionName != ''">function_name = #{functionName},</if>
|
||||
<if test="options != null and options != ''">options = #{options},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where table_id = #{tableId}
|
||||
</update>
|
||||
<update id="updateGenTable" parameterType="cn.universal.admin.generator.domain.GenTable">
|
||||
update gen_table
|
||||
<set>
|
||||
<if test="tableName != null">table_name = #{tableName},</if>
|
||||
<if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if>
|
||||
<if test="subTableName != null">sub_table_name = #{subTableName},</if>
|
||||
<if test="subTableFkName != null">sub_table_fk_name = #{subTableFkName},</if>
|
||||
<if test="className != null and className != ''">class_name = #{className},</if>
|
||||
<if test="functionAuthor != null and functionAuthor != ''">function_Author =
|
||||
#{functionAuthor},
|
||||
</if>
|
||||
<if test="genType != null and genType != ''">gen_type = #{genType},</if>
|
||||
<if test="genPath != null and genPath != ''">gen_path = #{genPath},</if>
|
||||
<if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if>
|
||||
<if test="packageName != null and packageName != ''">package_name = #{packageName},</if>
|
||||
<if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if>
|
||||
<if test="businessName != null and businessName != ''">business_name = #{businessName},</if>
|
||||
<if test="functionName != null and functionName != ''">function_name = #{functionName},</if>
|
||||
<if test="options != null and options != ''">options = #{options},</if>
|
||||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
update_time = sysdate()
|
||||
</set>
|
||||
where table_id = #{tableId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteGenTableByIds" parameterType="Long">
|
||||
delete from gen_table where table_id in
|
||||
<foreach collection="array" item="tableId" open="(" separator="," close=")">
|
||||
#{tableId}
|
||||
</foreach>
|
||||
</delete>
|
||||
<delete id="deleteGenTableByIds" parameterType="Long">
|
||||
delete from gen_table where table_id in
|
||||
<foreach collection="array" item="tableId" open="(" separator="," close=")">
|
||||
#{tableId}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
@@ -29,79 +29,79 @@ import io.swagger.annotations.Api;
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@RestController
|
||||
@Api(value = "${functionName}管理" , tags = {"${functionName}管理" })
|
||||
@RequestMapping("/admin/${moduleName}/${businessName}" )
|
||||
@Api(value = "${functionName}管理", tags = {"${functionName}管理"})
|
||||
@RequestMapping("/admin/${moduleName}/${businessName}")
|
||||
public class ${ClassName}Controller extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private I${ClassName}Service ${className}Service;
|
||||
@Autowired
|
||||
private I${ClassName}Service ${className}Service;
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*/
|
||||
@ApiOperation("查询${functionName}列表" )
|
||||
@GetMapping("/list" )
|
||||
@ApiOperation("查询${functionName}列表")
|
||||
@GetMapping("/list")
|
||||
#if($table.crud || $table.sub)
|
||||
public TableDataInfo list(${ClassName} ${className}) {
|
||||
startPage();
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
return getDataTable(list);
|
||||
startPage();
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
return getDataTable(list);
|
||||
}
|
||||
#elseif($table.tree)
|
||||
public AjaxResult list(${ClassName} ${className}) {
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
public AjaxResult list(${ClassName} ${className}) {
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
return AjaxResult.success(list);
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* 导出${functionName}列表
|
||||
*/
|
||||
@ApiOperation("导出${functionName}列表" )
|
||||
@PostMapping("/export" )
|
||||
public void export(HttpServletResponse response, ${ClassName} ${className}) {
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class);
|
||||
util.exportExcel(response, list, "${functionName}数据" );
|
||||
}
|
||||
/**
|
||||
* 导出${functionName}列表
|
||||
*/
|
||||
@ApiOperation("导出${functionName}列表")
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, ${ClassName} ${className}) {
|
||||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
|
||||
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}. class);
|
||||
util.exportExcel(response, list, "${functionName}数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取${functionName}详细信息
|
||||
*/
|
||||
@ApiOperation("获取${functionName}详细信息" )
|
||||
@GetMapping(value = "/{${pkColumn.javaField}}" )
|
||||
public AjaxResult<${ClassName}> getInfo(
|
||||
@PathVariable("${pkColumn.javaField}" ) ${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return AjaxResult.success(${className}Service.select${ClassName}ById(${pkColumn.javaField}));
|
||||
}
|
||||
/**
|
||||
* 获取${functionName}详细信息
|
||||
*/
|
||||
@ApiOperation("获取${functionName}详细信息")
|
||||
@GetMapping(value = "/{${pkColumn.javaField}}")
|
||||
public AjaxResult<${ClassName}> getInfo(
|
||||
@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return AjaxResult.success(${className}Service.select${ClassName}ById(${pkColumn.javaField}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*/
|
||||
@ApiOperation("新增${functionName}" )
|
||||
@PostMapping
|
||||
public AjaxResult<Void> add(@RequestBody ${ClassName} ${className}) {
|
||||
${className}Service.insert${ClassName}(${className});
|
||||
return AjaxResult.success();
|
||||
}
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*/
|
||||
@ApiOperation("新增${functionName}")
|
||||
@PostMapping
|
||||
public AjaxResult<Void> add(@RequestBody ${ClassName} ${className}) {
|
||||
${className}Service.insert${ClassName}(${className});
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*/
|
||||
@ApiOperation("修改${functionName}" )
|
||||
@PutMapping
|
||||
public AjaxResult<Void> edit(@RequestBody ${ClassName} ${className}) {
|
||||
${className}Service.update${ClassName}(${className});
|
||||
return AjaxResult.success();
|
||||
}
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*/
|
||||
@ApiOperation("修改${functionName}")
|
||||
@PutMapping
|
||||
public AjaxResult<Void> edit(@RequestBody ${ClassName} ${className}) {
|
||||
${className}Service.update${ClassName}(${className});
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*/
|
||||
@ApiOperation("删除${functionName}" )
|
||||
@DeleteMapping("/{${pkColumn.javaField}s}" )
|
||||
public AjaxResult<Void> remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
${className}Service.delete${ClassName}ByIds(${pkColumn.javaField}s);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*/
|
||||
@ApiOperation("删除${functionName}")
|
||||
@DeleteMapping("/{${pkColumn.javaField}s}")
|
||||
public AjaxResult<Void> remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
${className}Service.delete${ClassName}ByIds(${pkColumn.javaField}s);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,38 +26,38 @@ import ${import};
|
||||
#elseif($table.tree)
|
||||
#set($Entity="TreeEntity")
|
||||
#end
|
||||
@Table(name = "${tableName}" )
|
||||
@Data
|
||||
public class ${ClassName} extends
|
||||
@Table(name = "${tableName}")
|
||||
@Data
|
||||
public class ${ClassName} extends
|
||||
|
||||
BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
#foreach ($column in $columns)
|
||||
#if(!$column.superColumn)
|
||||
/** $column.columnComment */
|
||||
#if($column.list)
|
||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||
#if($parentheseIndex != -1)
|
||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
||||
#else
|
||||
#set($comment=$column.columnComment)
|
||||
#end
|
||||
#if($parentheseIndex != -1)
|
||||
@Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()" )
|
||||
#elseif($column.javaType == 'Date')
|
||||
@Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd" )
|
||||
#else
|
||||
@Excel(name = "${comment}" )
|
||||
#end
|
||||
#end
|
||||
#if($column.isPk==1)
|
||||
@Id
|
||||
#foreach ($column in $columns)
|
||||
#if(!$column.superColumn)
|
||||
/** $column.columnComment */
|
||||
#if($column.list)
|
||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||
#if($parentheseIndex != -1)
|
||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
||||
#else
|
||||
@Column(name = "${column.columnName}" )
|
||||
#set($comment=$column.columnComment)
|
||||
#end
|
||||
#if($parentheseIndex != -1)
|
||||
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
|
||||
#elseif($column.javaType == 'Date')
|
||||
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
#else
|
||||
@Excel(name = "${comment}")
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
|
||||
#end
|
||||
#if($column.isPk==1)
|
||||
@Id
|
||||
#else
|
||||
@Column(name = "${column.columnName}")
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
|
||||
#end
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -13,51 +13,51 @@ import cn.universal.persistence.common.BaseMapper;
|
||||
*/
|
||||
public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> {
|
||||
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}集合
|
||||
*/
|
||||
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}集合
|
||||
*/
|
||||
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int insert${ClassName}(${ClassName} ${className});
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int insert${ClassName}(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int update${ClassName}(${ClassName} ${className});
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int update${ClassName}(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
|
||||
/**
|
||||
* 批量删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
|
||||
/**
|
||||
* 批量删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
|
||||
}
|
||||
@@ -14,60 +14,60 @@ import com.zebra.common.core.domain.Ztree;
|
||||
*/
|
||||
public interface I${ClassName}Service {
|
||||
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}集合
|
||||
*/
|
||||
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}集合
|
||||
*/
|
||||
List<${ClassName}> select${ClassName}List(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int insert${ClassName}(${ClassName} ${className});
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int insert${ClassName}(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int update${ClassName}(${ClassName} ${className});
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
int update${ClassName}(${ClassName} ${className});
|
||||
|
||||
/**
|
||||
* 批量删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaType}[] ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
|
||||
/**
|
||||
* 批量删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaType}[] ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s);
|
||||
|
||||
/**
|
||||
* 删除${functionName}信息
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
/**
|
||||
* 删除${functionName}信息
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
#if($table.tree)
|
||||
|
||||
/**
|
||||
* 查询${functionName}树列表
|
||||
*
|
||||
* @return 所有${functionName}信息
|
||||
*/
|
||||
List<Ztree> select${ClassName}Tree();
|
||||
/**
|
||||
* 查询${functionName}树列表
|
||||
*
|
||||
* @return 所有${functionName}信息
|
||||
*/
|
||||
List<Ztree> select${ClassName}Tree();
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -25,118 +25,118 @@ import ${packageName}.service.I${ClassName}Service;
|
||||
@Service
|
||||
public class ${ClassName}ServiceImpl implements I${ClassName}Service {
|
||||
|
||||
@Resource
|
||||
private ${ClassName}Mapper ${className}Mapper;
|
||||
@Resource
|
||||
private ${ClassName}Mapper ${className}Mapper;
|
||||
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
@Override
|
||||
public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return ${className}Mapper.select${ClassName}ById(${pkColumn.javaField});
|
||||
}
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return ${functionName}
|
||||
*/
|
||||
@Override
|
||||
public ${ClassName} select${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return ${className}Mapper.select${ClassName}ById(${pkColumn.javaField});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}
|
||||
*/
|
||||
@Override
|
||||
public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) {
|
||||
return ${className}Mapper.select${ClassName}List(${className});
|
||||
}
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return ${functionName}
|
||||
*/
|
||||
@Override
|
||||
public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) {
|
||||
return ${className}Mapper.select${ClassName}List(${className});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insert${ClassName}(${ClassName} ${className}) {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.javaField == 'createTime')
|
||||
${className}.setCreateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateTime')
|
||||
${className}.setUpdateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateBy')
|
||||
${className}.setUpdateBy(SecurityUtils.getUnionId());
|
||||
#end
|
||||
#end
|
||||
return ${className}Mapper.insert${ClassName}(${className});
|
||||
}
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insert${ClassName}(${ClassName} ${className}) {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.javaField == 'createTime')
|
||||
${className}.setCreateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateTime')
|
||||
${className}.setUpdateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateBy')
|
||||
${className}.setUpdateBy(SecurityUtils.getUnionId());
|
||||
#end
|
||||
#end
|
||||
return ${className}Mapper.insert${ClassName}(${className});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int update${ClassName}(${ClassName} ${className}) {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.javaField == 'updateTime')
|
||||
${className}.setUpdateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateBy')
|
||||
${className}.setUpdateBy(SecurityUtils.getUnionId());
|
||||
#end
|
||||
#end
|
||||
return ${className}Mapper.update${ClassName}(${className});
|
||||
}
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param ${className} ${functionName}
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int update${ClassName}(${ClassName} ${className}) {
|
||||
#foreach ($column in $columns)
|
||||
#if($column.javaField == 'updateTime')
|
||||
${className}.setUpdateTime(new Date());
|
||||
#end
|
||||
#if($column.javaField == 'updateBy')
|
||||
${className}.setUpdateBy(SecurityUtils.getUnionId());
|
||||
#end
|
||||
#end
|
||||
return ${className}Mapper.update${ClassName}(${className});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除${functionName}对象
|
||||
*
|
||||
* @param ${pkColumn.javaType}[] ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaType}[]${pkColumn.javaField}s);
|
||||
}
|
||||
/**
|
||||
* 删除${functionName}对象
|
||||
*
|
||||
* @param ${pkColumn.javaType}[] ${pkColumn.javaField}s 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int delete${ClassName}ByIds(${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
return ${className}Mapper.delete${ClassName}ByIds(${pkColumn.javaType}[]${pkColumn.javaField}s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除${functionName}信息
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField});
|
||||
}
|
||||
/**
|
||||
* 删除${functionName}信息
|
||||
*
|
||||
* @param ${pkColumn.javaField} ${functionName}ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int delete${ClassName}ById(${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return ${className}Mapper.delete${ClassName}ById(${pkColumn.javaField});
|
||||
}
|
||||
#if($table.tree)
|
||||
|
||||
/**
|
||||
* 查询${functionName}树列表
|
||||
*
|
||||
* @return 所有${functionName}信息
|
||||
*/
|
||||
@Override
|
||||
public List<Ztree> select${ClassName}Tree() {
|
||||
List<${ClassName}> ${className}List = ${className}Mapper.select${ClassName}List(
|
||||
new ${ClassName}());
|
||||
List<Ztree> ztrees = new ArrayList<Ztree>();
|
||||
for (${ClassName} ${className} :${className}List)
|
||||
{
|
||||
Ztree ztree = new Ztree();
|
||||
#set($TreeCode=$treeCode.substring(0,1).toUpperCase() + ${treeCode.substring(1)})
|
||||
#set($TreeParentCode=$treeParentCode.substring(0,1).toUpperCase() + ${treeParentCode.substring(1)})
|
||||
#set($TreeName=$treeName.substring(0,1).toUpperCase() + ${treeName.substring(1)})
|
||||
ztree.setId(${className}.get${TreeCode}());
|
||||
ztree.setpId(${className}.get${TreeParentCode}());
|
||||
ztree.setName(${className}.get${TreeName}());
|
||||
ztree.setTitle(${className}.get${TreeName}());
|
||||
ztrees.add(ztree);
|
||||
}
|
||||
return ztrees;
|
||||
/**
|
||||
* 查询${functionName}树列表
|
||||
*
|
||||
* @return 所有${functionName}信息
|
||||
*/
|
||||
@Override
|
||||
public List<Ztree> select${ClassName}Tree() {
|
||||
List<${ClassName}> ${className}List = ${className}Mapper.select${ClassName}List(
|
||||
new ${ClassName}());
|
||||
List<Ztree> ztrees = new ArrayList<Ztree>();
|
||||
for (${ClassName} ${className} :${className}List)
|
||||
{
|
||||
Ztree ztree = new Ztree();
|
||||
#set($TreeCode=$treeCode.substring(0,1).toUpperCase() + ${treeCode.substring(1)})
|
||||
#set($TreeParentCode=$treeParentCode.substring(0,1).toUpperCase() + ${treeParentCode.substring(1)})
|
||||
#set($TreeName=$treeName.substring(0,1).toUpperCase() + ${treeName.substring(1)})
|
||||
ztree.setId(${className}.get${TreeCode}());
|
||||
ztree.setpId(${className}.get${TreeParentCode}());
|
||||
ztree.setName(${className}.get${TreeName}());
|
||||
ztree.setTitle(${className}.get${TreeName}());
|
||||
ztrees.add(ztree);
|
||||
}
|
||||
return ztrees;
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
@@ -15,31 +15,31 @@ import com.ruoyi.common.annotation.Excel;
|
||||
@Data
|
||||
public class ${subClassName} extends
|
||||
|
||||
BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
#foreach ($column in $subTable.columns)
|
||||
#if(!$table.isSuperColumn($column.javaField))
|
||||
/** $column.columnComment */
|
||||
#if($column.list)
|
||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||
#if($parentheseIndex != -1)
|
||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
||||
#else
|
||||
#set($comment=$column.columnComment)
|
||||
#end
|
||||
#if($parentheseIndex != -1)
|
||||
@Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()" )
|
||||
#elseif($column.javaType == 'Date')
|
||||
@JsonFormat(pattern = "yyyy-MM-dd" )
|
||||
@Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd" )
|
||||
#else
|
||||
@Excel(name = "${comment}" )
|
||||
#end
|
||||
#foreach ($column in $subTable.columns)
|
||||
#if(!$table.isSuperColumn($column.javaField))
|
||||
/** $column.columnComment */
|
||||
#if($column.list)
|
||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||
#if($parentheseIndex != -1)
|
||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
||||
#else
|
||||
#set($comment=$column.columnComment)
|
||||
#end
|
||||
#if($parentheseIndex != -1)
|
||||
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
|
||||
#elseif($column.javaType == 'Date')
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
#else
|
||||
@Excel(name = "${comment}")
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
|
||||
#end
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
}
|
||||
|
||||
@@ -2,43 +2,43 @@ import request from '@/utils/request'
|
||||
|
||||
// 查询${functionName}列表
|
||||
export function list${BusinessName}(query) {
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询${functionName}详细
|
||||
export function get${BusinessName}(${pkColumn.javaField}) {
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
})
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增${functionName}
|
||||
export function add${BusinessName}(data) {
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改${functionName}
|
||||
export function update${BusinessName}(data) {
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除${functionName}
|
||||
export function del${BusinessName}(${pkColumn.javaField}) {
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
})
|
||||
return request({
|
||||
url: '/admin/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索
|
||||
</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
@@ -68,9 +68,11 @@
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t('button.search') }}
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t
|
||||
('button.search') }}
|
||||
</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}
|
||||
</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
@@ -68,8 +68,11 @@
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">{{ $t('button.edit') }}</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}</el-button>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">{{ $t
|
||||
('button.edit') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
@@ -68,8 +68,11 @@
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">{{ $t('button.search') }}</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}</el-button>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">{{ $t
|
||||
('button.search') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery">{{ $t('button.reset') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
@@ -1,139 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="${packageName}.mapper.${ClassName}Mapper">
|
||||
|
||||
<resultMap type="${ClassName}" id="${ClassName}Result">
|
||||
#foreach ($column in $columns)
|
||||
<result property="${column.javaField}" column="${column.columnName}"/>
|
||||
#end
|
||||
#if($table.tree)
|
||||
<result property="parentName" column="parent_name"/>
|
||||
#end
|
||||
</resultMap>
|
||||
<resultMap type="${ClassName}" id="${ClassName}Result">
|
||||
#foreach ($column in $columns)
|
||||
<result property="${column.javaField}" column="${column.columnName}"/>
|
||||
#end
|
||||
#if($table.tree)
|
||||
<result property="parentName" column="parent_name"/>
|
||||
#end
|
||||
</resultMap>
|
||||
|
||||
<sql id="select${ClassName}Vo">
|
||||
select#foreach($column in $columns) $column.columnName#if($velocityCount != $columns.size()),#end#end
|
||||
from ${tableName}
|
||||
</sql>
|
||||
<sql id="select${ClassName}Vo">
|
||||
select#foreach($column in $columns) $column.columnName#if($velocityCount != $columns.size()),#end#end
|
||||
from ${tableName}
|
||||
</sql>
|
||||
|
||||
<select id="select${ClassName}List" parameterType="${ClassName}" resultMap="${ClassName}Result">
|
||||
<select id="select${ClassName}List" parameterType="${ClassName}" resultMap="${ClassName}Result">
|
||||
<include refid="select${ClassName}Vo"/>
|
||||
<where>
|
||||
#foreach($column in $columns)
|
||||
#set($queryType=$column.queryType)
|
||||
#set($javaField=$column.javaField)
|
||||
#set($javaType=$column.javaType)
|
||||
#set($columnName=$column.columnName)
|
||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#if($column.query)
|
||||
#if($column.queryType == "EQ")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName = #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "NE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName != #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "GT")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName > #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "GTE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName >= #{$javaField
|
||||
}
|
||||
</if>
|
||||
#elseif($queryType == "LT")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName < #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "LTE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName <= #{$javaField
|
||||
}
|
||||
</if>
|
||||
#elseif($queryType == "LIKE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName like concat('%',
|
||||
#{$javaField}, '%')
|
||||
</if>
|
||||
#elseif($queryType == "BETWEEN")
|
||||
<if
|
||||
test="params.begin$AttrName != null and params.begin$AttrName != '' and params.end$AttrName != null and params.end$AttrName != ''">
|
||||
and $columnName between #{params.begin$AttrName} and #{params.end$AttrName}
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</where>
|
||||
#if($table.tree)
|
||||
order by ${tree_parent_code}
|
||||
#end
|
||||
</select>
|
||||
|
||||
<select id="select${ClassName}ById" parameterType="${pkColumn.javaType}"
|
||||
resultMap="${ClassName}Result">
|
||||
#if($table.crud)
|
||||
<include refid="select${ClassName}Vo"/>
|
||||
<where>
|
||||
#foreach($column in $columns)
|
||||
#set($queryType=$column.queryType)
|
||||
#set($javaField=$column.javaField)
|
||||
#set($javaType=$column.javaType)
|
||||
#set($columnName=$column.columnName)
|
||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||
#if($column.query)
|
||||
#if($column.queryType == "EQ")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName = #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "NE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName != #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "GT")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName > #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "GTE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName >= #{$javaField
|
||||
}
|
||||
</if>
|
||||
#elseif($queryType == "LT")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName < #{$javaField}
|
||||
</if>
|
||||
#elseif($queryType == "LTE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName <= #{$javaField
|
||||
}
|
||||
</if>
|
||||
#elseif($queryType == "LIKE")
|
||||
<if test="$javaField != null #if($javaType ==
|
||||
'String' ) and $javaField.trim() != ''#end">and $columnName like concat('%',
|
||||
#{$javaField}, '%')
|
||||
</if>
|
||||
#elseif($queryType == "BETWEEN")
|
||||
<if
|
||||
test="params.begin$AttrName != null and params.begin$AttrName != '' and params.end$AttrName != null and params.end$AttrName != ''">
|
||||
and $columnName between #{params.begin$AttrName} and #{params.end$AttrName}
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</where>
|
||||
#if($table.tree)
|
||||
order by ${tree_parent_code}
|
||||
#end
|
||||
</select>
|
||||
|
||||
<select id="select${ClassName}ById" parameterType="${pkColumn.javaType}"
|
||||
resultMap="${ClassName}Result">
|
||||
#if($table.crud)
|
||||
<include refid="select${ClassName}Vo"/>
|
||||
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
#elseif($table.tree)
|
||||
select#foreach($column in $columns) t.$column.columnName,#end p.${tree_name} as parent_name
|
||||
from ${tableName} t
|
||||
left join ${tableName} p on p.${pkColumn.columnName} = t.${tree_parent_code}
|
||||
where t.${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
#end
|
||||
</select>
|
||||
|
||||
<insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment)
|
||||
useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
|
||||
insert into ${tableName}
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">$column.columnName,
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">#{$column.javaField},
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="update${ClassName}" parameterType="${ClassName}">
|
||||
update ${tableName}
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">$column.columnName =
|
||||
#{$column.javaField},
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
</update>
|
||||
#elseif($table.tree)
|
||||
select#foreach($column in $columns) t.$column.columnName,#end p.${tree_name} as parent_name
|
||||
from ${tableName} t
|
||||
left join ${tableName} p on p.${pkColumn.columnName} = t.${tree_parent_code}
|
||||
where t.${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
#end
|
||||
</select>
|
||||
|
||||
<delete id="delete${ClassName}ById" parameterType="${pkColumn.javaType}">
|
||||
delete
|
||||
from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
</delete>
|
||||
<insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment)
|
||||
useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
|
||||
insert into ${tableName}
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">$column.columnName,
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">#{$column.javaField},
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<delete id="delete${ClassName}ByIds" parameterType="${pkColumn.javaType}">
|
||||
delete from ${tableName} where ${pkColumn.columnName} in
|
||||
<foreach item="${pkColumn.javaField}" collection="array" open="(" separator="," close=")">
|
||||
#{${pkColumn.javaField}}
|
||||
</foreach>
|
||||
</delete>
|
||||
<update id="update${ClassName}" parameterType="${ClassName}">
|
||||
update ${tableName}
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
#foreach($column in $columns)
|
||||
#if($column.columnName != $pkColumn.columnName)
|
||||
<if test="$column.javaField != null #if($column.javaType ==
|
||||
'String' ) and $column.javaField != ''#end">$column.columnName =
|
||||
#{$column.javaField},
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
</trim>
|
||||
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
</update>
|
||||
|
||||
<delete id="delete${ClassName}ById" parameterType="${pkColumn.javaType}">
|
||||
delete
|
||||
from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
</delete>
|
||||
|
||||
<delete id="delete${ClassName}ByIds" parameterType="${pkColumn.javaType}">
|
||||
delete from ${tableName} where ${pkColumn.columnName} in
|
||||
<foreach item="${pkColumn.javaField}" collection="array" open="(" separator="," close=")">
|
||||
#{${pkColumn.javaField}}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
@@ -1,88 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-core</name>
|
||||
<description>数据桥接核心模块</description>
|
||||
<name>cn-universal-data-bridge-core</name>
|
||||
<description>数据桥接核心模块</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 父项目依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-persistence</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 父项目依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-persistence</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<!-- Hutool -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL Driver -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- MySQL Driver -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Kafka Client -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
<!-- Kafka Client -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MQTT Client -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
</dependency>
|
||||
<!-- MQTT Client -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- IoTDB JDBC Driver -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- IoTDB JDBC Driver -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-cache</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-cache</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+44
-46
@@ -34,63 +34,61 @@ import lombok.NoArgsConstructor;
|
||||
@Table(name = "iot_data_input_log")
|
||||
public class DataInputLog implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 配置ID */
|
||||
@Column(name = "config_id")
|
||||
private Long configId;
|
||||
/** 配置ID */
|
||||
@Column(name = "config_id")
|
||||
private Long configId;
|
||||
|
||||
/** 配置名称 */
|
||||
@Column(name = "config_name")
|
||||
private String configName;
|
||||
/** 配置名称 */
|
||||
@Column(name = "config_name")
|
||||
private String configName;
|
||||
|
||||
/** 源系统 */
|
||||
@Column(name = "source_system")
|
||||
private String sourceSystem;
|
||||
/** 源系统 */
|
||||
@Column(name = "source_system")
|
||||
private String sourceSystem;
|
||||
|
||||
/** 消息数量 */
|
||||
@Column(name = "message_count")
|
||||
private Integer messageCount;
|
||||
/** 消息数量 */
|
||||
@Column(name = "message_count")
|
||||
private Integer messageCount;
|
||||
|
||||
/** 成功数量 */
|
||||
@Column(name = "success_count")
|
||||
private Integer successCount;
|
||||
/** 成功数量 */
|
||||
@Column(name = "success_count")
|
||||
private Integer successCount;
|
||||
|
||||
/** 失败数量 */
|
||||
@Column(name = "failed_count")
|
||||
private Integer failedCount;
|
||||
/** 失败数量 */
|
||||
@Column(name = "failed_count")
|
||||
private Integer failedCount;
|
||||
|
||||
/** 错误信息 */
|
||||
@Column(name = "error_message")
|
||||
private String errorMessage;
|
||||
/** 错误信息 */
|
||||
@Column(name = "error_message")
|
||||
private String errorMessage;
|
||||
|
||||
/** 执行时间(毫秒) */
|
||||
@Column(name = "execution_time")
|
||||
private Long executionTime;
|
||||
/** 执行时间(毫秒) */
|
||||
@Column(name = "execution_time")
|
||||
private Long executionTime;
|
||||
|
||||
/** 状态 */
|
||||
@Column(name = "status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
/** 状态 */
|
||||
@Column(name = "status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 状态枚举
|
||||
*/
|
||||
public enum Status {
|
||||
SUCCESS, // 成功
|
||||
FAILED, // 失败
|
||||
RUNNING // 运行中
|
||||
}
|
||||
/** 状态枚举 */
|
||||
public enum Status {
|
||||
SUCCESS, // 成功
|
||||
FAILED, // 失败
|
||||
RUNNING // 运行中
|
||||
}
|
||||
}
|
||||
|
||||
+53
-57
@@ -34,76 +34,72 @@ import lombok.NoArgsConstructor;
|
||||
@Table(name = "iot_data_input_task")
|
||||
public class DataInputTask implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 配置ID */
|
||||
@Column(name = "config_id")
|
||||
private Long configId;
|
||||
/** 配置ID */
|
||||
@Column(name = "config_id")
|
||||
private Long configId;
|
||||
|
||||
/** 任务名称 */
|
||||
@Column(name = "task_name")
|
||||
private String taskName;
|
||||
/** 任务名称 */
|
||||
@Column(name = "task_name")
|
||||
private String taskName;
|
||||
|
||||
/** 任务类型 */
|
||||
@Column(name = "task_type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TaskType taskType;
|
||||
/** 任务类型 */
|
||||
@Column(name = "task_type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TaskType taskType;
|
||||
|
||||
/** Cron表达式 */
|
||||
@Column(name = "cron_expression")
|
||||
private String cronExpression;
|
||||
/** Cron表达式 */
|
||||
@Column(name = "cron_expression")
|
||||
private String cronExpression;
|
||||
|
||||
/** 最后执行时间 */
|
||||
@Column(name = "last_execution_time")
|
||||
private LocalDateTime lastExecutionTime;
|
||||
/** 最后执行时间 */
|
||||
@Column(name = "last_execution_time")
|
||||
private LocalDateTime lastExecutionTime;
|
||||
|
||||
/** 下次执行时间 */
|
||||
@Column(name = "next_execution_time")
|
||||
private LocalDateTime nextExecutionTime;
|
||||
/** 下次执行时间 */
|
||||
@Column(name = "next_execution_time")
|
||||
private LocalDateTime nextExecutionTime;
|
||||
|
||||
/** 状态 */
|
||||
@Column(name = "status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
/** 状态 */
|
||||
@Column(name = "status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
|
||||
/** 错误信息 */
|
||||
@Column(name = "error_message")
|
||||
private String errorMessage;
|
||||
/** 错误信息 */
|
||||
@Column(name = "error_message")
|
||||
private String errorMessage;
|
||||
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
@Column(name = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
/** 更新时间 */
|
||||
@Column(name = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
|
||||
/** 更新者 */
|
||||
@Column(name = "update_by")
|
||||
private String updateBy;
|
||||
/** 更新者 */
|
||||
@Column(name = "update_by")
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 任务类型枚举
|
||||
*/
|
||||
public enum TaskType {
|
||||
SCHEDULED, // 定时任务
|
||||
REALTIME // 实时任务
|
||||
}
|
||||
/** 任务类型枚举 */
|
||||
public enum TaskType {
|
||||
SCHEDULED, // 定时任务
|
||||
REALTIME // 实时任务
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态枚举
|
||||
*/
|
||||
public enum Status {
|
||||
RUNNING, // 运行中
|
||||
STOPPED, // 已停止
|
||||
ERROR // 错误状态
|
||||
}
|
||||
/** 状态枚举 */
|
||||
public enum Status {
|
||||
RUNNING, // 运行中
|
||||
STOPPED, // 已停止
|
||||
ERROR // 错误状态
|
||||
}
|
||||
}
|
||||
|
||||
+86
-83
@@ -34,109 +34,112 @@ import lombok.NoArgsConstructor;
|
||||
@Table(name = "iot_resource_connection")
|
||||
public class ResourceConnection implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 资源名称 */
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
/** 资源名称 */
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
/** 资源类型 */
|
||||
@Column(name = "type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ResourceType type;
|
||||
/** 资源类型 */
|
||||
@Column(name = "type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ResourceType type;
|
||||
|
||||
/** 插件类型 */
|
||||
@Column(name = "plugin_type")
|
||||
private String pluginType;
|
||||
/** 插件类型 */
|
||||
@Column(name = "plugin_type")
|
||||
private String pluginType;
|
||||
|
||||
/** 主机地址 */
|
||||
@Column(name = "host")
|
||||
private String host;
|
||||
/** 主机地址 */
|
||||
@Column(name = "host")
|
||||
private String host;
|
||||
|
||||
/** 端口号 */
|
||||
@Column(name = "port")
|
||||
private Integer port;
|
||||
/** 端口号 */
|
||||
@Column(name = "port")
|
||||
private Integer port;
|
||||
|
||||
/** 用户名 */
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
/** 用户名 */
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
/** 密码 */
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
/** 密码 */
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
/** 数据库名 */
|
||||
@Column(name = "database_name")
|
||||
private String databaseName;
|
||||
/** 数据库名 */
|
||||
@Column(name = "database_name")
|
||||
private String databaseName;
|
||||
|
||||
/** 扩展配置JSON */
|
||||
@Column(name = "extra_config")
|
||||
private String extraConfig;
|
||||
/** 扩展配置JSON */
|
||||
@Column(name = "extra_config")
|
||||
private String extraConfig;
|
||||
|
||||
/** 状态:0禁用,1启用 */
|
||||
@Column(name = "status")
|
||||
private Integer status;
|
||||
/** 状态:0禁用,1启用 */
|
||||
@Column(name = "status")
|
||||
private Integer status;
|
||||
|
||||
/** 描述 */
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
/** 描述 */
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
/** 创建者 */
|
||||
@Column(name = "create_by")
|
||||
private String createBy;
|
||||
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
/** 创建时间 */
|
||||
@Column(name = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新者 */
|
||||
@Column(name = "update_by")
|
||||
private String updateBy;
|
||||
/** 更新者 */
|
||||
@Column(name = "update_by")
|
||||
private String updateBy;
|
||||
|
||||
/** 更新时间 */
|
||||
@Column(name = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
/** 更新时间 */
|
||||
@Column(name = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 方向:IN-输入,OUT-输出,BOTH-双向 */
|
||||
@Column(name = "direction")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Direction direction;
|
||||
/** 方向:IN-输入,OUT-输出,BOTH-双向 */
|
||||
@Column(name = "direction")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Direction direction;
|
||||
|
||||
/** 数据流向:INPUT-输入,OUTPUT-输出,BIDIRECTIONAL-双向 */
|
||||
@Column(name = "data_direction")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private DataDirection dataDirection;
|
||||
/** 数据流向:INPUT-输入,OUTPUT-输出,BIDIRECTIONAL-双向 */
|
||||
@Column(name = "data_direction")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private DataDirection dataDirection;
|
||||
|
||||
/** 动态配置JSON - 使用现有的extra_config字段存储 */
|
||||
@Transient
|
||||
private String dynamicConfig;
|
||||
/** 动态配置JSON - 使用现有的extra_config字段存储 */
|
||||
@Transient private String dynamicConfig;
|
||||
|
||||
/**
|
||||
* 资源类型枚举
|
||||
*/
|
||||
public enum ResourceType {
|
||||
MYSQL, KAFKA, IOTDB, INFLUXDB, MQTT, HTTP, REDIS, ELASTICSEARCH, ALIYUN_IOT, TENCENT_IOT, HUAWEI_IOT
|
||||
}
|
||||
/** 资源类型枚举 */
|
||||
public enum ResourceType {
|
||||
MYSQL,
|
||||
KAFKA,
|
||||
IOTDB,
|
||||
INFLUXDB,
|
||||
MQTT,
|
||||
HTTP,
|
||||
REDIS,
|
||||
ELASTICSEARCH,
|
||||
ALIYUN_IOT,
|
||||
TENCENT_IOT,
|
||||
HUAWEI_IOT
|
||||
}
|
||||
|
||||
/**
|
||||
* 方向枚举
|
||||
*/
|
||||
public enum Direction {
|
||||
IN, // 输入
|
||||
OUT, // 输出
|
||||
BOTH // 双向
|
||||
}
|
||||
/** 方向枚举 */
|
||||
public enum Direction {
|
||||
IN, // 输入
|
||||
OUT, // 输出
|
||||
BOTH // 双向
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据流向枚举
|
||||
*/
|
||||
public enum DataDirection {
|
||||
INPUT, // 数据输入
|
||||
OUTPUT, // 数据输出
|
||||
BIDIRECTIONAL // 双向流转
|
||||
}
|
||||
/** 数据流向枚举 */
|
||||
public enum DataDirection {
|
||||
INPUT, // 数据输入
|
||||
OUTPUT, // 数据输出
|
||||
BIDIRECTIONAL // 双向流转
|
||||
}
|
||||
}
|
||||
|
||||
+198
-203
@@ -44,224 +44,219 @@ import org.springframework.stereotype.Component;
|
||||
@Slf4j
|
||||
public class DataInputManager {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Map<String, DataInputPlugin> inputPlugins;
|
||||
@Autowired(required = false)
|
||||
private Map<String, DataInputPlugin> inputPlugins;
|
||||
|
||||
@Resource
|
||||
private ResourceConnectionService resourceConnectionService;
|
||||
@Resource private ResourceConnectionService resourceConnectionService;
|
||||
|
||||
@Resource
|
||||
private DataBridgeConfigService dataBridgeConfigService;
|
||||
@Resource private DataBridgeConfigService dataBridgeConfigService;
|
||||
|
||||
@Resource
|
||||
private DataInputLogService dataInputLogService;
|
||||
@Resource private DataInputLogService dataInputLogService;
|
||||
|
||||
@Resource
|
||||
private DataBridgeLogger dataBridgeLogger;
|
||||
@Resource private DataBridgeLogger dataBridgeLogger;
|
||||
|
||||
// 存储正在运行的输入任务
|
||||
private final Map<Long, Boolean> runningTasks = new ConcurrentHashMap<>();
|
||||
// 存储正在运行的输入任务
|
||||
private final Map<Long, Boolean> runningTasks = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 启动数据输入任务
|
||||
*/
|
||||
public void startInputTask(Long configId) {
|
||||
/** 启动数据输入任务 */
|
||||
public void startInputTask(Long configId) {
|
||||
try {
|
||||
DataBridgeConfig config = dataBridgeConfigService.getById(configId);
|
||||
if (config == null) {
|
||||
throw new RuntimeException("配置不存在");
|
||||
}
|
||||
// TODO: 需要实现新的输入配置判断逻辑,因为Direction枚举已被移除
|
||||
|
||||
ResourceConnection connection =
|
||||
resourceConnectionService.getById(config.getTargetResourceId());
|
||||
if (connection == null || connection.getStatus() != 1) {
|
||||
throw new RuntimeException("资源连接不存在或已禁用");
|
||||
}
|
||||
|
||||
DataInputPlugin plugin = inputPlugins.get(config.getBridgeType().name().toUpperCase());
|
||||
if (plugin == null) {
|
||||
throw new RuntimeException("未找到对应的输入插件: " + config.getBridgeType());
|
||||
}
|
||||
|
||||
// 标记任务为运行状态
|
||||
runningTasks.put(configId, true);
|
||||
|
||||
// 创建数据消费者回调
|
||||
Consumer<List<BaseUPRequest>> dataConsumer = data -> processInputData(data, config);
|
||||
|
||||
// 启动数据输入
|
||||
// TODO: 需要重新设计输入插件的启动机制,因为相关方法已被移除
|
||||
log.info("数据输入任务启动逻辑需要重新实现,配置ID: {}", configId);
|
||||
|
||||
} catch (Exception e) {
|
||||
runningTasks.remove(configId);
|
||||
log.error("启动数据输入任务失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/** 停止数据输入任务 */
|
||||
public void stopInputTask(Long configId) {
|
||||
try {
|
||||
DataBridgeConfig config = dataBridgeConfigService.getById(configId);
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceConnection connection =
|
||||
resourceConnectionService.getById(config.getTargetResourceId());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataInputPlugin plugin = inputPlugins.get(config.getBridgeType().name().toUpperCase());
|
||||
if (plugin != null) {
|
||||
// TODO: 需要重新设计输入插件的停止机制,因为stopDataInput方法已被移除
|
||||
log.info("停止数据输入插件逻辑需要重新实现");
|
||||
}
|
||||
|
||||
// 移除运行标记
|
||||
runningTasks.remove(configId);
|
||||
|
||||
log.info("停止数据输入任务成功,配置ID: {}", configId);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("停止数据输入任务失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查任务是否正在运行 */
|
||||
public Boolean isTaskRunning(Long configId) {
|
||||
return runningTasks.getOrDefault(configId, false);
|
||||
}
|
||||
|
||||
/** 启动定时拉取任务 */
|
||||
@Async("dataBridgeExecutor")
|
||||
public void startScheduledPullTask(
|
||||
DataBridgeConfig config, ResourceConnection connection, DataInputPlugin plugin) {
|
||||
Long configId = config.getId();
|
||||
|
||||
while (runningTasks.getOrDefault(configId, false)) {
|
||||
try {
|
||||
// 拉取数据
|
||||
// TODO: 需要重新设计数据拉取机制,因为pullInputData方法已被移除
|
||||
List<BaseUPRequest> data = null; // plugin.pullInputData(config, connection);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(data)) {
|
||||
processInputData(data, config);
|
||||
}
|
||||
|
||||
// 等待下次拉取(可以从配置中读取间隔时间)
|
||||
Thread.sleep(getScheduleInterval(config));
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.error("定时拉取数据失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
|
||||
// 记录错误日志
|
||||
logInputExecution(config, 0, 0, 0, e.getMessage(), 0);
|
||||
|
||||
// 等待一段时间后重试
|
||||
try {
|
||||
DataBridgeConfig config = dataBridgeConfigService.getById(configId);
|
||||
if (config == null) {
|
||||
throw new RuntimeException("配置不存在");
|
||||
}
|
||||
// TODO: 需要实现新的输入配置判断逻辑,因为Direction枚举已被移除
|
||||
|
||||
ResourceConnection connection = resourceConnectionService.getById(config.getTargetResourceId());
|
||||
if (connection == null || connection.getStatus() != 1) {
|
||||
throw new RuntimeException("资源连接不存在或已禁用");
|
||||
}
|
||||
|
||||
DataInputPlugin plugin = inputPlugins.get(config.getBridgeType().name().toUpperCase());
|
||||
if (plugin == null) {
|
||||
throw new RuntimeException("未找到对应的输入插件: " + config.getBridgeType());
|
||||
}
|
||||
|
||||
// 标记任务为运行状态
|
||||
runningTasks.put(configId, true);
|
||||
|
||||
// 创建数据消费者回调
|
||||
Consumer<List<BaseUPRequest>> dataConsumer = data -> processInputData(data, config);
|
||||
|
||||
// 启动数据输入
|
||||
// TODO: 需要重新设计输入插件的启动机制,因为相关方法已被移除
|
||||
log.info("数据输入任务启动逻辑需要重新实现,配置ID: {}", configId);
|
||||
|
||||
} catch (Exception e) {
|
||||
runningTasks.remove(configId);
|
||||
log.error("启动数据输入任务失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
throw e;
|
||||
Thread.sleep(30000); // 30秒后重试
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理输入数据 */
|
||||
private void processInputData(List<BaseUPRequest> data, DataBridgeConfig config) {
|
||||
if (CollectionUtil.isEmpty(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止数据输入任务
|
||||
*/
|
||||
public void stopInputTask(Long configId) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
int successCount = 0;
|
||||
int failedCount = 0;
|
||||
String errorMessage = null;
|
||||
|
||||
try {
|
||||
// TODO: 这里需要将数据推送到IoT平台
|
||||
// 可以通过消息队列或直接调用IoT平台的API
|
||||
for (BaseUPRequest request : data) {
|
||||
try {
|
||||
DataBridgeConfig config = dataBridgeConfigService.getById(configId);
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceConnection connection = resourceConnectionService.getById(config.getTargetResourceId());
|
||||
if (connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataInputPlugin plugin = inputPlugins.get(config.getBridgeType().name().toUpperCase());
|
||||
if (plugin != null) {
|
||||
// TODO: 需要重新设计输入插件的停止机制,因为stopDataInput方法已被移除
|
||||
log.info("停止数据输入插件逻辑需要重新实现");
|
||||
}
|
||||
|
||||
// 移除运行标记
|
||||
runningTasks.remove(configId);
|
||||
|
||||
log.info("停止数据输入任务成功,配置ID: {}", configId);
|
||||
|
||||
// 推送到IoT平台的逻辑
|
||||
pushToIoTPlatform(request, config);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
log.error("停止数据输入任务失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
throw e;
|
||||
failedCount++;
|
||||
log.error("推送数据到IoT平台失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(
|
||||
"处理输入数据完成,配置: {}, 总数: {}, 成功: {}, 失败: {}",
|
||||
config.getName(),
|
||||
data.size(),
|
||||
successCount,
|
||||
failedCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
failedCount = data.size();
|
||||
errorMessage = e.getMessage();
|
||||
log.error("处理输入数据异常,配置: {}, 错误: {}", config.getName(), e.getMessage(), e);
|
||||
} finally {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
logInputExecution(
|
||||
config, data.size(), successCount, failedCount, errorMessage, executionTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查任务是否正在运行
|
||||
*/
|
||||
public Boolean isTaskRunning(Long configId) {
|
||||
return runningTasks.getOrDefault(configId, false);
|
||||
/** 推送数据到IoT平台 */
|
||||
private void pushToIoTPlatform(BaseUPRequest request, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug(
|
||||
"推送数据到IoT平台: 设备={}, 产品={}",
|
||||
request.getIoTDeviceDTO() != null ? request.getIoTDeviceDTO().getDeviceId() : "unknown",
|
||||
config.getTargetResourceId());
|
||||
}
|
||||
|
||||
/** 记录输入执行日志 */
|
||||
private void logInputExecution(
|
||||
DataBridgeConfig config,
|
||||
int messageCount,
|
||||
int successCount,
|
||||
int failedCount,
|
||||
String errorMessage,
|
||||
long executionTime) {
|
||||
try {
|
||||
DataInputLog log =
|
||||
DataInputLog.builder()
|
||||
.configId(config.getId())
|
||||
.configName(config.getName())
|
||||
.sourceSystem(config.getBridgeType().name())
|
||||
.messageCount(messageCount)
|
||||
.successCount(successCount)
|
||||
.failedCount(failedCount)
|
||||
.errorMessage(errorMessage)
|
||||
.executionTime(executionTime)
|
||||
.status(failedCount > 0 ? DataInputLog.Status.FAILED : DataInputLog.Status.SUCCESS)
|
||||
.createTime(LocalDateTime.now())
|
||||
.createBy(config.getCreateBy())
|
||||
.build();
|
||||
|
||||
dataInputLogService.save(log);
|
||||
} catch (Exception e) {
|
||||
log.error("记录输入执行日志失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动定时拉取任务
|
||||
*/
|
||||
@Async("dataBridgeExecutor")
|
||||
public void startScheduledPullTask(DataBridgeConfig config, ResourceConnection connection, DataInputPlugin plugin) {
|
||||
Long configId = config.getId();
|
||||
|
||||
while (runningTasks.getOrDefault(configId, false)) {
|
||||
try {
|
||||
// 拉取数据
|
||||
// TODO: 需要重新设计数据拉取机制,因为pullInputData方法已被移除
|
||||
List<BaseUPRequest> data = null; // plugin.pullInputData(config, connection);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(data)) {
|
||||
processInputData(data, config);
|
||||
}
|
||||
|
||||
// 等待下次拉取(可以从配置中读取间隔时间)
|
||||
Thread.sleep(getScheduleInterval(config));
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.error("定时拉取数据失败,配置ID: {}, 错误: {}", configId, e.getMessage(), e);
|
||||
|
||||
// 记录错误日志
|
||||
logInputExecution(config, 0, 0, 0, e.getMessage(), 0);
|
||||
|
||||
// 等待一段时间后重试
|
||||
try {
|
||||
Thread.sleep(30000); // 30秒后重试
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理输入数据
|
||||
*/
|
||||
private void processInputData(List<BaseUPRequest> data, DataBridgeConfig config) {
|
||||
if (CollectionUtil.isEmpty(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
int successCount = 0;
|
||||
int failedCount = 0;
|
||||
String errorMessage = null;
|
||||
|
||||
try {
|
||||
// TODO: 这里需要将数据推送到IoT平台
|
||||
// 可以通过消息队列或直接调用IoT平台的API
|
||||
for (BaseUPRequest request : data) {
|
||||
try {
|
||||
// 推送到IoT平台的逻辑
|
||||
pushToIoTPlatform(request, config);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
failedCount++;
|
||||
log.error("推送数据到IoT平台失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("处理输入数据完成,配置: {}, 总数: {}, 成功: {}, 失败: {}",
|
||||
config.getName(), data.size(), successCount, failedCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
failedCount = data.size();
|
||||
errorMessage = e.getMessage();
|
||||
log.error("处理输入数据异常,配置: {}, 错误: {}", config.getName(), e.getMessage(), e);
|
||||
} finally {
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
logInputExecution(config, data.size(), successCount, failedCount, errorMessage, executionTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送数据到IoT平台
|
||||
*/
|
||||
private void pushToIoTPlatform(BaseUPRequest request, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug("推送数据到IoT平台: 设备={}, 产品={}",
|
||||
request.getIoTDeviceDTO() != null ? request.getIoTDeviceDTO().getDeviceId() : "unknown",
|
||||
config.getTargetResourceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录输入执行日志
|
||||
*/
|
||||
private void logInputExecution(DataBridgeConfig config, int messageCount, int successCount,
|
||||
int failedCount, String errorMessage, long executionTime) {
|
||||
try {
|
||||
DataInputLog log = DataInputLog.builder()
|
||||
.configId(config.getId())
|
||||
.configName(config.getName())
|
||||
.sourceSystem(config.getBridgeType().name())
|
||||
.messageCount(messageCount)
|
||||
.successCount(successCount)
|
||||
.failedCount(failedCount)
|
||||
.errorMessage(errorMessage)
|
||||
.executionTime(executionTime)
|
||||
.status(failedCount > 0 ? DataInputLog.Status.FAILED : DataInputLog.Status.SUCCESS)
|
||||
.createTime(LocalDateTime.now())
|
||||
.createBy(config.getCreateBy())
|
||||
.build();
|
||||
|
||||
dataInputLogService.save(log);
|
||||
} catch (Exception e) {
|
||||
log.error("记录输入执行日志失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调度间隔时间(毫秒)
|
||||
*/
|
||||
private long getScheduleInterval(DataBridgeConfig config) {
|
||||
// 从配置中读取间隔时间,默认60秒
|
||||
// TODO: 可以从 inputConfig 中解析间隔时间
|
||||
return 60000; // 60秒
|
||||
}
|
||||
/** 获取调度间隔时间(毫秒) */
|
||||
private long getScheduleInterval(DataBridgeConfig config) {
|
||||
// 从配置中读取间隔时间,默认60秒
|
||||
// TODO: 可以从 inputConfig 中解析间隔时间
|
||||
return 60000; // 60秒
|
||||
}
|
||||
}
|
||||
|
||||
+49
-45
@@ -26,54 +26,58 @@ import org.apache.ibatis.annotations.Param;
|
||||
*/
|
||||
public interface DataBridgeConfigMapper extends BaseMapper<DataBridgeConfig> {
|
||||
|
||||
/**
|
||||
* 根据产品KEY获取活跃配置
|
||||
*
|
||||
* @param productKey 产品KEY
|
||||
* @return 活跃配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectActiveConfigsByProductKey(@Param("productKey") String productKey);
|
||||
/**
|
||||
* 根据产品KEY获取活跃配置
|
||||
*
|
||||
* @param productKey 产品KEY
|
||||
* @return 活跃配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectActiveConfigsByProductKey(@Param("productKey") String productKey);
|
||||
|
||||
/**
|
||||
* 根据桥接类型获取配置
|
||||
*
|
||||
* @param bridgeType 桥接类型
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectConfigsByBridgeType(@Param("bridgeType") String bridgeType);
|
||||
/**
|
||||
* 根据桥接类型获取配置
|
||||
*
|
||||
* @param bridgeType 桥接类型
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectConfigsByBridgeType(@Param("bridgeType") String bridgeType);
|
||||
|
||||
/**
|
||||
* 根据目标资源ID获取配置
|
||||
*
|
||||
* @param targetResourceId 目标资源ID
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectConfigsByTargetResourceId(@Param("targetResourceId") Long targetResourceId);
|
||||
/**
|
||||
* 根据目标资源ID获取配置
|
||||
*
|
||||
* @param targetResourceId 目标资源ID
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectConfigsByTargetResourceId(
|
||||
@Param("targetResourceId") Long targetResourceId);
|
||||
|
||||
/**
|
||||
* 批量更新配置状态
|
||||
*
|
||||
* @param ids 配置ID列表
|
||||
* @param status 状态
|
||||
* @param updateBy 更新者
|
||||
* @return 更新数量
|
||||
*/
|
||||
int batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") Integer status, @Param("updateBy") String updateBy);
|
||||
/**
|
||||
* 批量更新配置状态
|
||||
*
|
||||
* @param ids 配置ID列表
|
||||
* @param status 状态
|
||||
* @param updateBy 更新者
|
||||
* @return 更新数量
|
||||
*/
|
||||
int batchUpdateStatus(
|
||||
@Param("ids") List<Long> ids,
|
||||
@Param("status") Integer status,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
/**
|
||||
* 根据名称查询配置(用于重名检查)
|
||||
*
|
||||
* @param name 配置名称
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 配置
|
||||
*/
|
||||
DataBridgeConfig selectByName(@Param("name") String name, @Param("excludeId") Long excludeId);
|
||||
/**
|
||||
* 根据名称查询配置(用于重名检查)
|
||||
*
|
||||
* @param name 配置名称
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 配置
|
||||
*/
|
||||
DataBridgeConfig selectByName(@Param("name") String name, @Param("excludeId") Long excludeId);
|
||||
|
||||
/**
|
||||
* 根据创建者查询配置列表
|
||||
*
|
||||
* @param createBy 创建者
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectByCreateBy(@Param("createBy") String createBy);
|
||||
/**
|
||||
* 根据创建者查询配置列表
|
||||
*
|
||||
* @param createBy 创建者
|
||||
* @return 配置列表
|
||||
*/
|
||||
List<DataBridgeConfig> selectByCreateBy(@Param("createBy") String createBy);
|
||||
}
|
||||
|
||||
+44
-40
@@ -26,48 +26,52 @@ import org.apache.ibatis.annotations.Param;
|
||||
*/
|
||||
public interface ResourceConnectionMapper extends BaseMapper<ResourceConnection> {
|
||||
|
||||
/**
|
||||
* 根据资源类型获取活跃连接
|
||||
*
|
||||
* @param type 资源类型
|
||||
* @return 活跃连接列表
|
||||
*/
|
||||
List<ResourceConnection> selectActiveConnectionsByType(@Param("type") String type);
|
||||
/**
|
||||
* 根据资源类型获取活跃连接
|
||||
*
|
||||
* @param type 资源类型
|
||||
* @return 活跃连接列表
|
||||
*/
|
||||
List<ResourceConnection> selectActiveConnectionsByType(@Param("type") String type);
|
||||
|
||||
/**
|
||||
* 根据主机和端口查询连接
|
||||
*
|
||||
* @param host 主机地址
|
||||
* @param port 端口号
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 连接
|
||||
*/
|
||||
ResourceConnection selectByHostAndPort(@Param("host") String host, @Param("port") Integer port, @Param("excludeId") Long excludeId);
|
||||
/**
|
||||
* 根据主机和端口查询连接
|
||||
*
|
||||
* @param host 主机地址
|
||||
* @param port 端口号
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 连接
|
||||
*/
|
||||
ResourceConnection selectByHostAndPort(
|
||||
@Param("host") String host, @Param("port") Integer port, @Param("excludeId") Long excludeId);
|
||||
|
||||
/**
|
||||
* 根据名称查询连接(用于重名检查)
|
||||
*
|
||||
* @param name 连接名称
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 连接
|
||||
*/
|
||||
ResourceConnection selectByName(@Param("name") String name, @Param("excludeId") Long excludeId);
|
||||
/**
|
||||
* 根据名称查询连接(用于重名检查)
|
||||
*
|
||||
* @param name 连接名称
|
||||
* @param excludeId 排除的ID(用于更新时检查)
|
||||
* @return 连接
|
||||
*/
|
||||
ResourceConnection selectByName(@Param("name") String name, @Param("excludeId") Long excludeId);
|
||||
|
||||
/**
|
||||
* 批量更新连接状态
|
||||
*
|
||||
* @param ids 连接ID列表
|
||||
* @param status 状态
|
||||
* @param updateBy 更新者
|
||||
* @return 更新数量
|
||||
*/
|
||||
int batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") Integer status, @Param("updateBy") String updateBy);
|
||||
/**
|
||||
* 批量更新连接状态
|
||||
*
|
||||
* @param ids 连接ID列表
|
||||
* @param status 状态
|
||||
* @param updateBy 更新者
|
||||
* @return 更新数量
|
||||
*/
|
||||
int batchUpdateStatus(
|
||||
@Param("ids") List<Long> ids,
|
||||
@Param("status") Integer status,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
/**
|
||||
* 测试连接可用性(通过查询系统表)
|
||||
*
|
||||
* @param id 连接ID
|
||||
* @return 是否可用
|
||||
*/
|
||||
int testConnection(@Param("id") Long id);
|
||||
/**
|
||||
* 测试连接可用性(通过查询系统表)
|
||||
*
|
||||
* @param id 连接ID
|
||||
* @return 是否可用
|
||||
*/
|
||||
int testConnection(@Param("id") Long id);
|
||||
}
|
||||
|
||||
+139
-147
@@ -30,170 +30,162 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class ConfigValidator {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 名称正则表达式:字母、数字、下划线、中划线,长度1-100
|
||||
private static final Pattern NAME_PATTERN = Pattern.compile("^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{1,100}$");
|
||||
// 名称正则表达式:字母、数字、下划线、中划线,长度1-100
|
||||
private static final Pattern NAME_PATTERN =
|
||||
Pattern.compile("^[\\u4e00-\\u9fa5a-zA-Z0-9_-]{1,100}$");
|
||||
|
||||
// 产品KEY正则表达式:字母、数字、下划线,长度1-50
|
||||
private static final Pattern PRODUCT_KEY_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{1,50}$");
|
||||
// 产品KEY正则表达式:字母、数字、下划线,长度1-50
|
||||
private static final Pattern PRODUCT_KEY_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{1,50}$");
|
||||
|
||||
// 主机地址正则表达式
|
||||
private static final Pattern HOST_PATTERN = Pattern.compile("^[a-zA-Z0-9.-]{1,255}$");
|
||||
// 主机地址正则表达式
|
||||
private static final Pattern HOST_PATTERN = Pattern.compile("^[a-zA-Z0-9.-]{1,255}$");
|
||||
|
||||
// 数据库名正则表达式:字母、数字、下划线,长度1-64
|
||||
private static final Pattern DATABASE_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{1,64}$");
|
||||
// 数据库名正则表达式:字母、数字、下划线,长度1-64
|
||||
private static final Pattern DATABASE_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{1,64}$");
|
||||
|
||||
/**
|
||||
* 验证桥接配置
|
||||
*/
|
||||
public static void validateDataBridgeConfig(DataBridgeConfig config) {
|
||||
if (config == null) {
|
||||
throw new DataBridgeException("CONFIG_NULL", "桥接配置不能为空");
|
||||
}
|
||||
|
||||
// 验证名称
|
||||
validateName(config.getName(), "桥接配置名称");
|
||||
|
||||
// 验证源范围
|
||||
if (config.getSourceScope() == null) {
|
||||
throw new DataBridgeException("INVALID_SOURCE_SCOPE", "源范围不能为空");
|
||||
}
|
||||
|
||||
// 验证目标资源ID
|
||||
if (config.getTargetResourceId() == null || config.getTargetResourceId() <= 0) {
|
||||
throw new DataBridgeException("INVALID_TARGET_RESOURCE_ID", "目标资源ID无效");
|
||||
}
|
||||
|
||||
// 验证桥接类型
|
||||
if (config.getBridgeType() == null) {
|
||||
throw new DataBridgeException("INVALID_BRIDGE_TYPE", "桥接类型不能为空");
|
||||
}
|
||||
|
||||
// 验证模板
|
||||
if (config.getTemplate() == null || config.getTemplate().trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_TEMPLATE", "模板内容不能为空");
|
||||
}
|
||||
|
||||
// 验证统一配置JSON格式
|
||||
if (config.getConfig() != null && !config.getConfig().trim().isEmpty()) {
|
||||
validateJsonFormat(config.getConfig(), "统一配置");
|
||||
}
|
||||
|
||||
// 验证Magic脚本
|
||||
if (config.getMagicScript() != null && !config.getMagicScript().trim().isEmpty()) {
|
||||
// Magic脚本内容验证可以在这里添加
|
||||
}
|
||||
|
||||
// 验证状态
|
||||
if (config.getStatus() != null && config.getStatus() != 0 && config.getStatus() != 1) {
|
||||
throw new DataBridgeException("INVALID_STATUS", "状态值无效,只能为0或1");
|
||||
}
|
||||
|
||||
// 验证描述长度
|
||||
if (config.getDescription() != null && config.getDescription().length() > 500) {
|
||||
throw new DataBridgeException("INVALID_DESCRIPTION", "描述长度不能超过500个字符");
|
||||
}
|
||||
/** 验证桥接配置 */
|
||||
public static void validateDataBridgeConfig(DataBridgeConfig config) {
|
||||
if (config == null) {
|
||||
throw new DataBridgeException("CONFIG_NULL", "桥接配置不能为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源连接
|
||||
*/
|
||||
public static void validateResourceConnection(ResourceConnection connection) {
|
||||
if (connection == null) {
|
||||
throw new DataBridgeException("CONNECTION_NULL", "资源连接不能为空");
|
||||
}
|
||||
// 验证名称
|
||||
validateName(config.getName(), "桥接配置名称");
|
||||
|
||||
// 验证名称
|
||||
validateName(connection.getName(), "资源连接名称");
|
||||
|
||||
// 验证资源类型
|
||||
if (connection.getType() == null) {
|
||||
throw new DataBridgeException("INVALID_RESOURCE_TYPE", "资源类型不能为空");
|
||||
}
|
||||
|
||||
// 验证数据流向
|
||||
if (connection.getDataDirection() == null) {
|
||||
throw new DataBridgeException("INVALID_DATA_DIRECTION", "数据流向不能为空");
|
||||
}
|
||||
|
||||
// 验证扩展配置JSON格式(包含动态配置)
|
||||
if (connection.getExtraConfig() != null && !connection.getExtraConfig().trim().isEmpty()) {
|
||||
validateJsonFormat(connection.getExtraConfig(), "扩展配置");
|
||||
}
|
||||
|
||||
// 验证状态
|
||||
if (connection.getStatus() != null && connection.getStatus() != 0 && connection.getStatus() != 1) {
|
||||
throw new DataBridgeException("INVALID_STATUS", "状态值无效,只能为0或1");
|
||||
}
|
||||
|
||||
// 验证描述长度
|
||||
if (connection.getDescription() != null && connection.getDescription().length() > 500) {
|
||||
throw new DataBridgeException("INVALID_DESCRIPTION", "描述长度不能超过500个字符");
|
||||
}
|
||||
|
||||
// 验证方向
|
||||
if (connection.getDirection() == null) {
|
||||
throw new DataBridgeException("INVALID_DIRECTION", "连接方向不能为空");
|
||||
}
|
||||
// 验证源范围
|
||||
if (config.getSourceScope() == null) {
|
||||
throw new DataBridgeException("INVALID_SOURCE_SCOPE", "源范围不能为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证名称
|
||||
*/
|
||||
private static void validateName(String name, String fieldName) {
|
||||
if (name == null || name.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_NAME", fieldName + "不能为空");
|
||||
}
|
||||
if (!NAME_PATTERN.matcher(name).matches()) {
|
||||
throw new DataBridgeException("INVALID_NAME_FORMAT", fieldName + "格式无效,只能包含中文、字母、数字、下划线、中划线,长度1-100");
|
||||
}
|
||||
// 验证目标资源ID
|
||||
if (config.getTargetResourceId() == null || config.getTargetResourceId() <= 0) {
|
||||
throw new DataBridgeException("INVALID_TARGET_RESOURCE_ID", "目标资源ID无效");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证产品KEY
|
||||
*/
|
||||
private static void validateProductKey(String productKey) {
|
||||
if (productKey == null || productKey.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_PRODUCT_KEY", "产品KEY不能为空");
|
||||
}
|
||||
if (!PRODUCT_KEY_PATTERN.matcher(productKey).matches()) {
|
||||
throw new DataBridgeException("INVALID_PRODUCT_KEY_FORMAT", "产品KEY格式无效,只能包含字母、数字、下划线,长度1-50");
|
||||
}
|
||||
// 验证桥接类型
|
||||
if (config.getBridgeType() == null) {
|
||||
throw new DataBridgeException("INVALID_BRIDGE_TYPE", "桥接类型不能为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证主机地址
|
||||
*/
|
||||
private static void validateHost(String host) {
|
||||
if (host == null || host.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_HOST", "主机地址不能为空");
|
||||
}
|
||||
if (!HOST_PATTERN.matcher(host).matches()) {
|
||||
throw new DataBridgeException("INVALID_HOST_FORMAT", "主机地址格式无效");
|
||||
}
|
||||
// 验证模板
|
||||
if (config.getTemplate() == null || config.getTemplate().trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_TEMPLATE", "模板内容不能为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据库名
|
||||
*/
|
||||
private static void validateDatabaseName(String databaseName) {
|
||||
if (databaseName == null || databaseName.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_DATABASE_NAME", "数据库名不能为空");
|
||||
}
|
||||
if (!DATABASE_NAME_PATTERN.matcher(databaseName).matches()) {
|
||||
throw new DataBridgeException("INVALID_DATABASE_NAME_FORMAT", "数据库名格式无效,只能包含字母、数字、下划线,长度1-64");
|
||||
}
|
||||
// 验证统一配置JSON格式
|
||||
if (config.getConfig() != null && !config.getConfig().trim().isEmpty()) {
|
||||
validateJsonFormat(config.getConfig(), "统一配置");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JSON格式
|
||||
*/
|
||||
private static void validateJsonFormat(String json, String fieldName) {
|
||||
try {
|
||||
objectMapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new DataBridgeException("INVALID_JSON_FORMAT", fieldName + "JSON格式无效: " + e.getMessage());
|
||||
}
|
||||
// 验证Magic脚本
|
||||
if (config.getMagicScript() != null && !config.getMagicScript().trim().isEmpty()) {
|
||||
// Magic脚本内容验证可以在这里添加
|
||||
}
|
||||
|
||||
// 验证状态
|
||||
if (config.getStatus() != null && config.getStatus() != 0 && config.getStatus() != 1) {
|
||||
throw new DataBridgeException("INVALID_STATUS", "状态值无效,只能为0或1");
|
||||
}
|
||||
|
||||
// 验证描述长度
|
||||
if (config.getDescription() != null && config.getDescription().length() > 500) {
|
||||
throw new DataBridgeException("INVALID_DESCRIPTION", "描述长度不能超过500个字符");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证资源连接 */
|
||||
public static void validateResourceConnection(ResourceConnection connection) {
|
||||
if (connection == null) {
|
||||
throw new DataBridgeException("CONNECTION_NULL", "资源连接不能为空");
|
||||
}
|
||||
|
||||
// 验证名称
|
||||
validateName(connection.getName(), "资源连接名称");
|
||||
|
||||
// 验证资源类型
|
||||
if (connection.getType() == null) {
|
||||
throw new DataBridgeException("INVALID_RESOURCE_TYPE", "资源类型不能为空");
|
||||
}
|
||||
|
||||
// 验证数据流向
|
||||
if (connection.getDataDirection() == null) {
|
||||
throw new DataBridgeException("INVALID_DATA_DIRECTION", "数据流向不能为空");
|
||||
}
|
||||
|
||||
// 验证扩展配置JSON格式(包含动态配置)
|
||||
if (connection.getExtraConfig() != null && !connection.getExtraConfig().trim().isEmpty()) {
|
||||
validateJsonFormat(connection.getExtraConfig(), "扩展配置");
|
||||
}
|
||||
|
||||
// 验证状态
|
||||
if (connection.getStatus() != null
|
||||
&& connection.getStatus() != 0
|
||||
&& connection.getStatus() != 1) {
|
||||
throw new DataBridgeException("INVALID_STATUS", "状态值无效,只能为0或1");
|
||||
}
|
||||
|
||||
// 验证描述长度
|
||||
if (connection.getDescription() != null && connection.getDescription().length() > 500) {
|
||||
throw new DataBridgeException("INVALID_DESCRIPTION", "描述长度不能超过500个字符");
|
||||
}
|
||||
|
||||
// 验证方向
|
||||
if (connection.getDirection() == null) {
|
||||
throw new DataBridgeException("INVALID_DIRECTION", "连接方向不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证名称 */
|
||||
private static void validateName(String name, String fieldName) {
|
||||
if (name == null || name.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_NAME", fieldName + "不能为空");
|
||||
}
|
||||
if (!NAME_PATTERN.matcher(name).matches()) {
|
||||
throw new DataBridgeException(
|
||||
"INVALID_NAME_FORMAT", fieldName + "格式无效,只能包含中文、字母、数字、下划线、中划线,长度1-100");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证产品KEY */
|
||||
private static void validateProductKey(String productKey) {
|
||||
if (productKey == null || productKey.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_PRODUCT_KEY", "产品KEY不能为空");
|
||||
}
|
||||
if (!PRODUCT_KEY_PATTERN.matcher(productKey).matches()) {
|
||||
throw new DataBridgeException("INVALID_PRODUCT_KEY_FORMAT", "产品KEY格式无效,只能包含字母、数字、下划线,长度1-50");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证主机地址 */
|
||||
private static void validateHost(String host) {
|
||||
if (host == null || host.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_HOST", "主机地址不能为空");
|
||||
}
|
||||
if (!HOST_PATTERN.matcher(host).matches()) {
|
||||
throw new DataBridgeException("INVALID_HOST_FORMAT", "主机地址格式无效");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证数据库名 */
|
||||
private static void validateDatabaseName(String databaseName) {
|
||||
if (databaseName == null || databaseName.trim().isEmpty()) {
|
||||
throw new DataBridgeException("INVALID_DATABASE_NAME", "数据库名不能为空");
|
||||
}
|
||||
if (!DATABASE_NAME_PATTERN.matcher(databaseName).matches()) {
|
||||
throw new DataBridgeException(
|
||||
"INVALID_DATABASE_NAME_FORMAT", "数据库名格式无效,只能包含字母、数字、下划线,长度1-64");
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证JSON格式 */
|
||||
private static void validateJsonFormat(String json, String fieldName) {
|
||||
try {
|
||||
objectMapper.readTree(json);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new DataBridgeException(
|
||||
"INVALID_JSON_FORMAT", fieldName + "JSON格式无效: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+356
-348
@@ -23,8 +23,7 @@ import javax.sql.DataSource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 数据桥接连接池管理器
|
||||
* 独立管理数据桥接的数据库连接池,避免与框架的tk.mybatis冲突
|
||||
* 数据桥接连接池管理器 独立管理数据桥接的数据库连接池,避免与框架的tk.mybatis冲突
|
||||
*
|
||||
* @author gitee.com/NexIoT
|
||||
* @version 1.0
|
||||
@@ -33,375 +32,384 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class DataBridgeConnectionManager {
|
||||
|
||||
// 连接池缓存 - 使用独立的命名空间避免冲突
|
||||
private final ConcurrentMap<String, DataSource> connectionPoolCache = new ConcurrentHashMap<>();
|
||||
|
||||
// 保活调度器
|
||||
private final ScheduledExecutorService keepAliveScheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
// 连接池配置 - 针对数据桥接优化
|
||||
private static final int MAX_POOL_SIZE = 3; // 数据桥接不需要太多连接
|
||||
private static final int MIN_IDLE = 1;
|
||||
private static final long CONNECTION_TIMEOUT = 20000L; // 20秒连接超时
|
||||
private static final long IDLE_TIMEOUT = 300000L; // 5分钟空闲超时
|
||||
private static final long MAX_LIFETIME = 1800000L; // 30分钟最大生命周期
|
||||
private static final long VALIDATION_TIMEOUT = 2000L; // 2秒验证超时
|
||||
|
||||
// 保活配置
|
||||
private static final long KEEP_ALIVE_INTERVAL = 300000L; // 5分钟保活间隔
|
||||
|
||||
public DataBridgeConnectionManager() {
|
||||
// 启动保活任务
|
||||
startKeepAliveTask();
|
||||
// 连接池缓存 - 使用独立的命名空间避免冲突
|
||||
private final ConcurrentMap<String, DataSource> connectionPoolCache = new ConcurrentHashMap<>();
|
||||
|
||||
// 保活调度器
|
||||
private final ScheduledExecutorService keepAliveScheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
// 连接池配置 - 针对数据桥接优化
|
||||
private static final int MAX_POOL_SIZE = 3; // 数据桥接不需要太多连接
|
||||
private static final int MIN_IDLE = 1;
|
||||
private static final long CONNECTION_TIMEOUT = 20000L; // 20秒连接超时
|
||||
private static final long IDLE_TIMEOUT = 300000L; // 5分钟空闲超时
|
||||
private static final long MAX_LIFETIME = 1800000L; // 30分钟最大生命周期
|
||||
private static final long VALIDATION_TIMEOUT = 2000L; // 2秒验证超时
|
||||
|
||||
// 保活配置
|
||||
private static final long KEEP_ALIVE_INTERVAL = 300000L; // 5分钟保活间隔
|
||||
|
||||
public DataBridgeConnectionManager() {
|
||||
// 启动保活任务
|
||||
startKeepAliveTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建数据源
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据源
|
||||
*/
|
||||
public DataSource getOrCreateDataSource(ResourceConnection connection) {
|
||||
String cacheKey = generateCacheKey(connection);
|
||||
|
||||
// 先检查缓存中是否已有连接池
|
||||
DataSource existingDataSource = connectionPoolCache.get(cacheKey);
|
||||
if (existingDataSource != null) {
|
||||
// 检查连接池是否健康
|
||||
if (isDataSourceHealthy(existingDataSource)) {
|
||||
log.debug("复用现有数据桥接连接池: {}", cacheKey);
|
||||
return existingDataSource;
|
||||
} else {
|
||||
log.warn("连接池不健康,移除并重新创建: {}", cacheKey);
|
||||
removeDataSource(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建数据源
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据源
|
||||
*/
|
||||
public DataSource getOrCreateDataSource(ResourceConnection connection) {
|
||||
String cacheKey = generateCacheKey(connection);
|
||||
|
||||
// 先检查缓存中是否已有连接池
|
||||
DataSource existingDataSource = connectionPoolCache.get(cacheKey);
|
||||
if (existingDataSource != null) {
|
||||
// 检查连接池是否健康
|
||||
if (isDataSourceHealthy(existingDataSource)) {
|
||||
log.debug("复用现有数据桥接连接池: {}", cacheKey);
|
||||
return existingDataSource;
|
||||
} else {
|
||||
log.warn("连接池不健康,移除并重新创建: {}", cacheKey);
|
||||
removeDataSource(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的连接池
|
||||
return connectionPoolCache.computeIfAbsent(cacheKey, key -> {
|
||||
log.info("创建数据桥接连接池: {}", key);
|
||||
return createDataBridgeDataSource(connection);
|
||||
// 创建新的连接池
|
||||
return connectionPoolCache.computeIfAbsent(
|
||||
cacheKey,
|
||||
key -> {
|
||||
log.info("创建数据桥接连接池: {}", key);
|
||||
return createDataBridgeDataSource(connection);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据桥接专用的数据源
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据源
|
||||
*/
|
||||
private DataSource createDataBridgeDataSource(ResourceConnection connection) {
|
||||
try {
|
||||
log.info("创建数据桥接连接池: {}:{}", connection.getHost(), connection.getPort());
|
||||
|
||||
// 测试网络连接
|
||||
testNetworkConnection(connection);
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
|
||||
// 基本连接信息
|
||||
String driverClassName = getDriverClassName(connection.getType().name());
|
||||
String jdbcUrl = buildConnectionUrl(connection);
|
||||
|
||||
config.setDriverClassName(driverClassName);
|
||||
config.setJdbcUrl(jdbcUrl);
|
||||
config.setUsername(connection.getUsername());
|
||||
config.setPassword(connection.getPassword());
|
||||
|
||||
// 数据桥接专用连接池配置
|
||||
config.setMaximumPoolSize(MAX_POOL_SIZE);
|
||||
config.setMinimumIdle(MIN_IDLE);
|
||||
config.setConnectionTimeout(CONNECTION_TIMEOUT);
|
||||
config.setIdleTimeout(IDLE_TIMEOUT);
|
||||
config.setMaxLifetime(MAX_LIFETIME);
|
||||
config.setValidationTimeout(VALIDATION_TIMEOUT);
|
||||
|
||||
// 连接池名称 - 使用独立前缀避免冲突
|
||||
config.setPoolName("DataBridge-" + connection.getId());
|
||||
|
||||
// 连接测试配置
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
config.setConnectionInitSql("SET SESSION wait_timeout=28800");
|
||||
|
||||
// 数据桥接专用配置
|
||||
config.setInitializationFailTimeout(15000); // 15秒初始化超时
|
||||
config.setRegisterMbeans(false); // 关闭JMX监控,避免冲突
|
||||
config.setLeakDetectionThreshold(30000); // 30秒连接泄漏检测
|
||||
config.setAutoCommit(true);
|
||||
config.setKeepaliveTime(30000); // 30秒保活
|
||||
|
||||
// MySQL连接属性 - 数据桥接优化
|
||||
if ("MYSQL".equalsIgnoreCase(connection.getType().name())) {
|
||||
addMySQLProperties(config);
|
||||
}
|
||||
|
||||
// 创建数据源
|
||||
HikariDataSource dataSource = new HikariDataSource(config);
|
||||
|
||||
// 测试连接
|
||||
testDataSource(dataSource);
|
||||
|
||||
log.info("数据桥接连接池创建成功");
|
||||
return dataSource;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"创建数据桥接连接池失败: {}:{} - {}", connection.getHost(), connection.getPort(), e.getMessage());
|
||||
|
||||
// 根据错误类型提供解决建议
|
||||
if (e.getMessage().contains("Access denied")) {
|
||||
log.error(
|
||||
"解决建议: 检查MySQL用户权限,确保用户 '{}' 有访问数据库 '{}' 的权限",
|
||||
connection.getUsername(),
|
||||
getDatabaseName(connection));
|
||||
} else if (e.getMessage().contains("Could not create connection")) {
|
||||
log.error("解决建议: 检查网络连接和MySQL服务,确保端口 {} 可访问", connection.getPort());
|
||||
}
|
||||
|
||||
throw new RuntimeException("数据桥接连接池创建失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据桥接专用的数据源
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据源
|
||||
*/
|
||||
private DataSource createDataBridgeDataSource(ResourceConnection connection) {
|
||||
try {
|
||||
log.info("创建数据桥接连接池: {}:{}", connection.getHost(), connection.getPort());
|
||||
|
||||
// 测试网络连接
|
||||
testNetworkConnection(connection);
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
/**
|
||||
* 添加MySQL特定属性
|
||||
*
|
||||
* @param config HikariCP配置
|
||||
*/
|
||||
private void addMySQLProperties(HikariConfig config) {
|
||||
config.addDataSourceProperty("useSSL", "false");
|
||||
config.addDataSourceProperty("allowPublicKeyRetrieval", "true");
|
||||
config.addDataSourceProperty("autoReconnect", "true");
|
||||
config.addDataSourceProperty("failOverReadOnly", "false");
|
||||
config.addDataSourceProperty("maxReconnects", "2"); // 减少重连次数
|
||||
config.addDataSourceProperty("initialTimeout", "1");
|
||||
config.addDataSourceProperty("connectTimeout", "20000");
|
||||
config.addDataSourceProperty("socketTimeout", "20000");
|
||||
config.addDataSourceProperty("useLocalSessionState", "true");
|
||||
config.addDataSourceProperty("rewriteBatchedStatements", "true");
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("useServerPrepStmts", "true");
|
||||
config.addDataSourceProperty("maintainTimeStats", "false");
|
||||
config.addDataSourceProperty("elideSetAutoCommits", "true");
|
||||
config.addDataSourceProperty("useLocalTransactionState", "true");
|
||||
config.addDataSourceProperty("alwaysSendSetIsolation", "false");
|
||||
config.addDataSourceProperty("cacheCallableStmts", "true");
|
||||
config.addDataSourceProperty("cacheResultSetMetadata", "true");
|
||||
config.addDataSourceProperty("cacheServerConfiguration", "true");
|
||||
}
|
||||
|
||||
// 基本连接信息
|
||||
String driverClassName = getDriverClassName(connection.getType().name());
|
||||
String jdbcUrl = buildConnectionUrl(connection);
|
||||
|
||||
config.setDriverClassName(driverClassName);
|
||||
config.setJdbcUrl(jdbcUrl);
|
||||
config.setUsername(connection.getUsername());
|
||||
config.setPassword(connection.getPassword());
|
||||
/**
|
||||
* 测试网络连接
|
||||
*
|
||||
* @param connection 连接配置
|
||||
*/
|
||||
private void testNetworkConnection(ResourceConnection connection) {
|
||||
try {
|
||||
java.net.Socket socket = new java.net.Socket();
|
||||
socket.connect(
|
||||
new java.net.InetSocketAddress(connection.getHost(), connection.getPort()), 5000);
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
log.error("网络连接测试失败: {}:{} - {}", connection.getHost(), connection.getPort(), e.getMessage());
|
||||
throw new RuntimeException("无法连接到数据库服务器: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 数据桥接专用连接池配置
|
||||
config.setMaximumPoolSize(MAX_POOL_SIZE);
|
||||
config.setMinimumIdle(MIN_IDLE);
|
||||
config.setConnectionTimeout(CONNECTION_TIMEOUT);
|
||||
config.setIdleTimeout(IDLE_TIMEOUT);
|
||||
config.setMaxLifetime(MAX_LIFETIME);
|
||||
config.setValidationTimeout(VALIDATION_TIMEOUT);
|
||||
/**
|
||||
* 测试数据源连接
|
||||
*
|
||||
* @param dataSource 数据源
|
||||
*/
|
||||
private void testDataSource(DataSource dataSource) {
|
||||
try (java.sql.Connection conn = dataSource.getConnection()) {
|
||||
if (!conn.isValid(2)) {
|
||||
throw new RuntimeException("连接无效");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("数据桥接连接测试失败: {}", e.getMessage());
|
||||
throw new RuntimeException("数据桥接连接测试失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 连接池名称 - 使用独立前缀避免冲突
|
||||
config.setPoolName("DataBridge-" + connection.getId());
|
||||
/**
|
||||
* 检查数据源是否健康
|
||||
*
|
||||
* @param dataSource 数据源
|
||||
* @return 是否健康
|
||||
*/
|
||||
private boolean isDataSourceHealthy(DataSource dataSource) {
|
||||
try {
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
|
||||
|
||||
// 连接测试配置
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
config.setConnectionInitSql("SET SESSION wait_timeout=28800");
|
||||
|
||||
// 数据桥接专用配置
|
||||
config.setInitializationFailTimeout(15000); // 15秒初始化超时
|
||||
config.setRegisterMbeans(false); // 关闭JMX监控,避免冲突
|
||||
config.setLeakDetectionThreshold(30000); // 30秒连接泄漏检测
|
||||
config.setAutoCommit(true);
|
||||
config.setKeepaliveTime(30000); // 30秒保活
|
||||
|
||||
// MySQL连接属性 - 数据桥接优化
|
||||
if ("MYSQL".equalsIgnoreCase(connection.getType().name())) {
|
||||
addMySQLProperties(config);
|
||||
}
|
||||
|
||||
// 创建数据源
|
||||
HikariDataSource dataSource = new HikariDataSource(config);
|
||||
|
||||
// 测试连接
|
||||
testDataSource(dataSource);
|
||||
|
||||
log.info("数据桥接连接池创建成功");
|
||||
return dataSource;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建数据桥接连接池失败: {}:{} - {}",
|
||||
connection.getHost(), connection.getPort(), e.getMessage());
|
||||
|
||||
// 根据错误类型提供解决建议
|
||||
if (e.getMessage().contains("Access denied")) {
|
||||
log.error("解决建议: 检查MySQL用户权限,确保用户 '{}' 有访问数据库 '{}' 的权限",
|
||||
connection.getUsername(), getDatabaseName(connection));
|
||||
} else if (e.getMessage().contains("Could not create connection")) {
|
||||
log.error("解决建议: 检查网络连接和MySQL服务,确保端口 {} 可访问", connection.getPort());
|
||||
}
|
||||
|
||||
throw new RuntimeException("数据桥接连接池创建失败: " + e.getMessage(), e);
|
||||
// 检查连接池是否已关闭
|
||||
if (hikariDataSource.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加MySQL特定属性
|
||||
*
|
||||
* @param config HikariCP配置
|
||||
*/
|
||||
private void addMySQLProperties(HikariConfig config) {
|
||||
config.addDataSourceProperty("useSSL", "false");
|
||||
config.addDataSourceProperty("allowPublicKeyRetrieval", "true");
|
||||
config.addDataSourceProperty("autoReconnect", "true");
|
||||
config.addDataSourceProperty("failOverReadOnly", "false");
|
||||
config.addDataSourceProperty("maxReconnects", "2"); // 减少重连次数
|
||||
config.addDataSourceProperty("initialTimeout", "1");
|
||||
config.addDataSourceProperty("connectTimeout", "20000");
|
||||
config.addDataSourceProperty("socketTimeout", "20000");
|
||||
config.addDataSourceProperty("useLocalSessionState", "true");
|
||||
config.addDataSourceProperty("rewriteBatchedStatements", "true");
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("useServerPrepStmts", "true");
|
||||
config.addDataSourceProperty("maintainTimeStats", "false");
|
||||
config.addDataSourceProperty("elideSetAutoCommits", "true");
|
||||
config.addDataSourceProperty("useLocalTransactionState", "true");
|
||||
config.addDataSourceProperty("alwaysSendSetIsolation", "false");
|
||||
config.addDataSourceProperty("cacheCallableStmts", "true");
|
||||
config.addDataSourceProperty("cacheResultSetMetadata", "true");
|
||||
config.addDataSourceProperty("cacheServerConfiguration", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试网络连接
|
||||
*
|
||||
* @param connection 连接配置
|
||||
*/
|
||||
private void testNetworkConnection(ResourceConnection connection) {
|
||||
try {
|
||||
java.net.Socket socket = new java.net.Socket();
|
||||
socket.connect(new java.net.InetSocketAddress(connection.getHost(), connection.getPort()), 5000);
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
log.error("网络连接测试失败: {}:{} - {}", connection.getHost(), connection.getPort(), e.getMessage());
|
||||
throw new RuntimeException("无法连接到数据库服务器: " + e.getMessage(), e);
|
||||
// 检查连接池状态
|
||||
try (java.sql.Connection conn = hikariDataSource.getConnection()) {
|
||||
return conn.isValid(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据源连接
|
||||
*
|
||||
* @param dataSource 数据源
|
||||
*/
|
||||
private void testDataSource(DataSource dataSource) {
|
||||
try (java.sql.Connection conn = dataSource.getConnection()) {
|
||||
if (!conn.isValid(2)) {
|
||||
throw new RuntimeException("连接无效");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("数据桥接连接测试失败: {}", e.getMessage());
|
||||
throw new RuntimeException("数据桥接连接测试失败: " + e.getMessage(), e);
|
||||
}
|
||||
// 对于非HikariCP数据源,尝试获取连接
|
||||
try (java.sql.Connection conn = dataSource.getConnection()) {
|
||||
return conn.isValid(2);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据源是否健康
|
||||
*
|
||||
* @param dataSource 数据源
|
||||
* @return 是否健康
|
||||
*/
|
||||
private boolean isDataSourceHealthy(DataSource dataSource) {
|
||||
try {
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
|
||||
|
||||
// 检查连接池是否已关闭
|
||||
if (hikariDataSource.isClosed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查连接池状态
|
||||
try (java.sql.Connection conn = hikariDataSource.getConnection()) {
|
||||
return conn.isValid(2);
|
||||
}
|
||||
}
|
||||
|
||||
// 对于非HikariCP数据源,尝试获取连接
|
||||
try (java.sql.Connection conn = dataSource.getConnection()) {
|
||||
return conn.isValid(2);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 构建连接URL
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 连接URL
|
||||
*/
|
||||
private String buildConnectionUrl(ResourceConnection connection) {
|
||||
return String.format(
|
||||
"jdbc:mysql://%s:%d/%s?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=2&initialTimeout=1&connectTimeout=20000&socketTimeout=20000&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&useServerPrepStmts=true&maintainTimeStats=false&elideSetAutoCommits=true&useLocalTransactionState=true&alwaysSendSetIsolation=false&cacheCallableStmts=true&cacheResultSetMetadata=true&cacheServerConfiguration=true",
|
||||
connection.getHost(), connection.getPort(), getDatabaseName(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库名称
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据库名称
|
||||
*/
|
||||
private String getDatabaseName(ResourceConnection connection) {
|
||||
// 使用数据库名称字段,如果为空则使用默认值
|
||||
String databaseName = connection.getDatabaseName();
|
||||
return databaseName != null && !databaseName.trim().isEmpty() ? databaseName : "mysql";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动类名
|
||||
*
|
||||
* @param dbType 数据库类型
|
||||
* @return 驱动类名
|
||||
*/
|
||||
private String getDriverClassName(String dbType) {
|
||||
return switch (dbType.toUpperCase()) {
|
||||
case "MYSQL" -> "com.mysql.cj.jdbc.Driver";
|
||||
case "POSTGRESQL" -> "org.postgresql.Driver";
|
||||
case "ORACLE" -> "oracle.jdbc.driver.OracleDriver";
|
||||
case "SQLSERVER" -> "com.microsoft.sqlserver.jdbc.SQLServerDriver";
|
||||
default -> throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存键 - 使用独立前缀避免冲突
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 缓存键
|
||||
*/
|
||||
private String generateCacheKey(ResourceConnection connection) {
|
||||
return String.format(
|
||||
"DataBridge_%s_%s_%s_%s_%s",
|
||||
connection.getType().name(),
|
||||
connection.getHost(),
|
||||
connection.getPort(),
|
||||
connection.getUsername(),
|
||||
getDatabaseName(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定连接池
|
||||
*
|
||||
* @param connection 连接配置
|
||||
*/
|
||||
public void removeDataSource(ResourceConnection connection) {
|
||||
String cacheKey = generateCacheKey(connection);
|
||||
DataSource dataSource = connectionPoolCache.remove(cacheKey);
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
try {
|
||||
((HikariDataSource) dataSource).close();
|
||||
log.info("已关闭数据桥接连接池: {}", cacheKey);
|
||||
} catch (Exception e) {
|
||||
log.warn("关闭数据桥接连接池失败: {} - {}", cacheKey, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建连接URL
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 连接URL
|
||||
*/
|
||||
private String buildConnectionUrl(ResourceConnection connection) {
|
||||
return String.format("jdbc:mysql://%s:%d/%s?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=2&initialTimeout=1&connectTimeout=20000&socketTimeout=20000&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&useServerPrepStmts=true&maintainTimeStats=false&elideSetAutoCommits=true&useLocalTransactionState=true&alwaysSendSetIsolation=false&cacheCallableStmts=true&cacheResultSetMetadata=true&cacheServerConfiguration=true",
|
||||
connection.getHost(),
|
||||
connection.getPort(),
|
||||
getDatabaseName(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库名称
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 数据库名称
|
||||
*/
|
||||
private String getDatabaseName(ResourceConnection connection) {
|
||||
// 使用数据库名称字段,如果为空则使用默认值
|
||||
String databaseName = connection.getDatabaseName();
|
||||
return databaseName != null && !databaseName.trim().isEmpty() ? databaseName : "mysql";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动类名
|
||||
*
|
||||
* @param dbType 数据库类型
|
||||
* @return 驱动类名
|
||||
*/
|
||||
private String getDriverClassName(String dbType) {
|
||||
return switch (dbType.toUpperCase()) {
|
||||
case "MYSQL" -> "com.mysql.cj.jdbc.Driver";
|
||||
case "POSTGRESQL" -> "org.postgresql.Driver";
|
||||
case "ORACLE" -> "oracle.jdbc.driver.OracleDriver";
|
||||
case "SQLSERVER" -> "com.microsoft.sqlserver.jdbc.SQLServerDriver";
|
||||
default -> throw new IllegalArgumentException("不支持的数据库类型: " + dbType);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缓存键 - 使用独立前缀避免冲突
|
||||
*
|
||||
* @param connection 连接配置
|
||||
* @return 缓存键
|
||||
*/
|
||||
private String generateCacheKey(ResourceConnection connection) {
|
||||
return String.format("DataBridge_%s_%s_%s_%s_%s",
|
||||
connection.getType().name(),
|
||||
connection.getHost(),
|
||||
connection.getPort(),
|
||||
connection.getUsername(),
|
||||
getDatabaseName(connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定连接池
|
||||
*
|
||||
* @param connection 连接配置
|
||||
*/
|
||||
public void removeDataSource(ResourceConnection connection) {
|
||||
String cacheKey = generateCacheKey(connection);
|
||||
DataSource dataSource = connectionPoolCache.remove(cacheKey);
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
try {
|
||||
((HikariDataSource) dataSource).close();
|
||||
log.info("已关闭数据桥接连接池: {}", cacheKey);
|
||||
} catch (Exception e) {
|
||||
log.warn("关闭数据桥接连接池失败: {} - {}", cacheKey, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动保活任务
|
||||
*/
|
||||
private void startKeepAliveTask() {
|
||||
keepAliveScheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
connectionPoolCache.forEach((key, dataSource) -> {
|
||||
if (!isDataSourceHealthy(dataSource)) {
|
||||
log.warn("检测到不健康的连接池: {}", key);
|
||||
}
|
||||
/** 启动保活任务 */
|
||||
private void startKeepAliveTask() {
|
||||
keepAliveScheduler.scheduleWithFixedDelay(
|
||||
() -> {
|
||||
try {
|
||||
connectionPoolCache.forEach(
|
||||
(key, dataSource) -> {
|
||||
if (!isDataSourceHealthy(dataSource)) {
|
||||
log.warn("检测到不健康的连接池: {}", key);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("连接池保活检查失败: {}", e.getMessage());
|
||||
}
|
||||
}, KEEP_ALIVE_INTERVAL, KEEP_ALIVE_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
|
||||
log.info("数据桥接连接池保活任务已启动,检查间隔: {}秒", KEEP_ALIVE_INTERVAL / 1000);
|
||||
} catch (Exception e) {
|
||||
log.error("连接池保活检查失败: {}", e.getMessage());
|
||||
}
|
||||
},
|
||||
KEEP_ALIVE_INTERVAL,
|
||||
KEEP_ALIVE_INTERVAL,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
log.info("数据桥接连接池保活任务已启动,检查间隔: {}秒", KEEP_ALIVE_INTERVAL / 1000);
|
||||
}
|
||||
|
||||
/** 关闭所有连接池 */
|
||||
public void shutdown() {
|
||||
log.info("开始关闭所有数据桥接连接池...");
|
||||
|
||||
// 关闭保活调度器
|
||||
keepAliveScheduler.shutdown();
|
||||
try {
|
||||
if (!keepAliveScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
keepAliveScheduler.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
keepAliveScheduler.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有连接池
|
||||
*/
|
||||
public void shutdown() {
|
||||
log.info("开始关闭所有数据桥接连接池...");
|
||||
|
||||
// 关闭保活调度器
|
||||
keepAliveScheduler.shutdown();
|
||||
try {
|
||||
if (!keepAliveScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
keepAliveScheduler.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
keepAliveScheduler.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
// 关闭所有连接池
|
||||
connectionPoolCache.forEach((key, dataSource) -> {
|
||||
try {
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
((HikariDataSource) dataSource).close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("关闭数据桥接连接池失败: {} - {}", key, e.getMessage());
|
||||
}
|
||||
});
|
||||
connectionPoolCache.clear();
|
||||
log.info("所有数据桥接连接池已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接池状态
|
||||
*
|
||||
* @return 连接池状态信息
|
||||
*/
|
||||
public String getPoolStatus() {
|
||||
StringBuilder status = new StringBuilder();
|
||||
status.append("数据桥接连接池状态:\n");
|
||||
connectionPoolCache.forEach((key, dataSource) -> {
|
||||
// 关闭所有连接池
|
||||
connectionPoolCache.forEach(
|
||||
(key, dataSource) -> {
|
||||
try {
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
|
||||
status.append(String.format(" %s: 活跃=%d, 空闲=%d, 等待=%d\n",
|
||||
key,
|
||||
hikariDataSource.getHikariPoolMXBean().getActiveConnections(),
|
||||
hikariDataSource.getHikariPoolMXBean().getIdleConnections(),
|
||||
hikariDataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()));
|
||||
((HikariDataSource) dataSource).close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("关闭数据桥接连接池失败: {} - {}", key, e.getMessage());
|
||||
}
|
||||
});
|
||||
return status.toString();
|
||||
}
|
||||
connectionPoolCache.clear();
|
||||
log.info("所有数据桥接连接池已关闭");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接池数量
|
||||
*
|
||||
* @return 连接池数量
|
||||
*/
|
||||
public int getPoolCount() {
|
||||
return connectionPoolCache.size();
|
||||
}
|
||||
/**
|
||||
* 获取连接池状态
|
||||
*
|
||||
* @return 连接池状态信息
|
||||
*/
|
||||
public String getPoolStatus() {
|
||||
StringBuilder status = new StringBuilder();
|
||||
status.append("数据桥接连接池状态:\n");
|
||||
connectionPoolCache.forEach(
|
||||
(key, dataSource) -> {
|
||||
if (dataSource instanceof HikariDataSource) {
|
||||
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
|
||||
status.append(
|
||||
String.format(
|
||||
" %s: 活跃=%d, 空闲=%d, 等待=%d\n",
|
||||
key,
|
||||
hikariDataSource.getHikariPoolMXBean().getActiveConnections(),
|
||||
hikariDataSource.getHikariPoolMXBean().getIdleConnections(),
|
||||
hikariDataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()));
|
||||
}
|
||||
});
|
||||
return status.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接池数量
|
||||
*
|
||||
* @return 连接池数量
|
||||
*/
|
||||
public int getPoolCount() {
|
||||
return connectionPoolCache.size();
|
||||
}
|
||||
}
|
||||
|
||||
+128
-129
@@ -18,8 +18,7 @@ import java.util.regex.Pattern;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* SQL模板处理器
|
||||
* 智能处理SQL模板中的变量替换,避免引号冲突
|
||||
* SQL模板处理器 智能处理SQL模板中的变量替换,避免引号冲突
|
||||
*
|
||||
* @author gitee.com/NexIoT
|
||||
* @version 1.0
|
||||
@@ -28,142 +27,142 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class SqlTemplateProcessor {
|
||||
|
||||
/**
|
||||
* 处理SQL模板,智能处理引号冲突
|
||||
*
|
||||
* @param template SQL模板
|
||||
* @param variables 变量映射
|
||||
* @return 处理后的SQL
|
||||
*/
|
||||
public static String processTemplate(String template, Map<String, Object> variables) {
|
||||
if (template == null || template.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 使用正则表达式匹配所有 #{...} 占位符
|
||||
Pattern pattern = Pattern.compile("#\\{([a-zA-Z0-9_.]+)\\}");
|
||||
Matcher matcher = pattern.matcher(template);
|
||||
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String paramPath = matcher.group(1);
|
||||
Object value = getNestedValue(variables, paramPath);
|
||||
String replacement = convertToSqlValue(value, template, matcher.start());
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
/**
|
||||
* 处理SQL模板,智能处理引号冲突
|
||||
*
|
||||
* @param template SQL模板
|
||||
* @param variables 变量映射
|
||||
* @return 处理后的SQL
|
||||
*/
|
||||
public static String processTemplate(String template, Map<String, Object> variables) {
|
||||
if (template == null || template.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能转换为SQL值,根据上下文选择合适的引号
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @param template 完整模板
|
||||
* @param position 当前位置
|
||||
* @return SQL安全格式的字符串
|
||||
*/
|
||||
private static String convertToSqlValue(Object value, String template, int position) {
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
// 使用正则表达式匹配所有 #{...} 占位符
|
||||
Pattern pattern = Pattern.compile("#\\{([a-zA-Z0-9_.]+)\\}");
|
||||
Matcher matcher = pattern.matcher(template);
|
||||
|
||||
// 检查上下文,判断是否在JSON字符串中
|
||||
boolean inJsonContext = isInJsonContext(template, position);
|
||||
|
||||
if (inJsonContext) {
|
||||
// 在JSON上下文中,使用双引号
|
||||
return convertToJsonValue(value);
|
||||
} else {
|
||||
// 在普通SQL上下文中,使用单引号
|
||||
return SqlValueConverter.convertToSqlValue(value);
|
||||
}
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
while (matcher.find()) {
|
||||
String paramPath = matcher.group(1);
|
||||
Object value = getNestedValue(variables, paramPath);
|
||||
String replacement = convertToSqlValue(value, template, matcher.start());
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能转换为SQL值,根据上下文选择合适的引号
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @param template 完整模板
|
||||
* @param position 当前位置
|
||||
* @return SQL安全格式的字符串
|
||||
*/
|
||||
private static String convertToSqlValue(Object value, String template, int position) {
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前位置是否在JSON字符串上下文中
|
||||
*
|
||||
* @param template 模板
|
||||
* @param position 位置
|
||||
* @return 是否在JSON上下文中
|
||||
*/
|
||||
private static boolean isInJsonContext(String template, int position) {
|
||||
// 向前查找最近的引号
|
||||
int singleQuotePos = template.lastIndexOf("'", position);
|
||||
int doubleQuotePos = template.lastIndexOf("\"", position);
|
||||
|
||||
// 如果最近的是双引号,说明在JSON上下文中
|
||||
if (doubleQuotePos > singleQuotePos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查是否在JSON对象中(包含大括号)
|
||||
String beforePosition = template.substring(0, position);
|
||||
int lastOpenBrace = beforePosition.lastIndexOf("{");
|
||||
int lastCloseBrace = beforePosition.lastIndexOf("}");
|
||||
|
||||
// 如果最近的大括号是开括号,说明在JSON对象中
|
||||
if (lastOpenBrace > lastCloseBrace) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// 检查上下文,判断是否在JSON字符串中
|
||||
boolean inJsonContext = isInJsonContext(template, position);
|
||||
|
||||
if (inJsonContext) {
|
||||
// 在JSON上下文中,使用双引号
|
||||
return convertToJsonValue(value);
|
||||
} else {
|
||||
// 在普通SQL上下文中,使用单引号
|
||||
return SqlValueConverter.convertToSqlValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前位置是否在JSON字符串上下文中
|
||||
*
|
||||
* @param template 模板
|
||||
* @param position 位置
|
||||
* @return 是否在JSON上下文中
|
||||
*/
|
||||
private static boolean isInJsonContext(String template, int position) {
|
||||
// 向前查找最近的引号
|
||||
int singleQuotePos = template.lastIndexOf("'", position);
|
||||
int doubleQuotePos = template.lastIndexOf("\"", position);
|
||||
|
||||
// 如果最近的是双引号,说明在JSON上下文中
|
||||
if (doubleQuotePos > singleQuotePos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON值
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @return JSON格式的字符串
|
||||
*/
|
||||
private static String convertToJsonValue(Object value) {
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
// 字符串:转义双引号
|
||||
String strValue = (String) value;
|
||||
return "\"" + strValue.replace("\"", "\\\"") + "\"";
|
||||
} else if (value instanceof Number || value instanceof Boolean) {
|
||||
// 数字和布尔类型直接转换
|
||||
return value.toString();
|
||||
} else {
|
||||
// 其他类型:转为JSON字符串
|
||||
return JSONUtil.toJsonStr(value);
|
||||
}
|
||||
// 检查是否在JSON对象中(包含大括号)
|
||||
String beforePosition = template.substring(0, position);
|
||||
int lastOpenBrace = beforePosition.lastIndexOf("{");
|
||||
int lastCloseBrace = beforePosition.lastIndexOf("}");
|
||||
|
||||
// 如果最近的大括号是开括号,说明在JSON对象中
|
||||
if (lastOpenBrace > lastCloseBrace) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌套属性值
|
||||
*
|
||||
* @param variables 变量映射
|
||||
* @param path 属性路径
|
||||
* @return 属性值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object getNestedValue(Map<String, Object> variables, String path) {
|
||||
if (path == null || path.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] keys = path.split("\\.");
|
||||
Object current = variables;
|
||||
|
||||
for (String key : keys) {
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (current instanceof Map) {
|
||||
current = ((Map<String, Object>) current).get(key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
/**
|
||||
* 转换为JSON值
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @return JSON格式的字符串
|
||||
*/
|
||||
private static String convertToJsonValue(Object value) {
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
// 字符串:转义双引号
|
||||
String strValue = (String) value;
|
||||
return "\"" + strValue.replace("\"", "\\\"") + "\"";
|
||||
} else if (value instanceof Number || value instanceof Boolean) {
|
||||
// 数字和布尔类型直接转换
|
||||
return value.toString();
|
||||
} else {
|
||||
// 其他类型:转为JSON字符串
|
||||
return JSONUtil.toJsonStr(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌套属性值
|
||||
*
|
||||
* @param variables 变量映射
|
||||
* @param path 属性路径
|
||||
* @return 属性值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object getNestedValue(Map<String, Object> variables, String path) {
|
||||
if (path == null || path.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] keys = path.split("\\.");
|
||||
Object current = variables;
|
||||
|
||||
for (String key : keys) {
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (current instanceof Map) {
|
||||
current = ((Map<String, Object>) current).get(key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-15
@@ -16,8 +16,7 @@ import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* SQL值转换工具类
|
||||
* 提供将Java对象转换为SQL安全格式的通用方法
|
||||
* SQL值转换工具类 提供将Java对象转换为SQL安全格式的通用方法
|
||||
*
|
||||
* @author gitee.com/NexIoT
|
||||
* @version 1.0
|
||||
@@ -27,12 +26,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public class SqlValueConverter {
|
||||
|
||||
/**
|
||||
* 将变量值转换为SQL安全格式
|
||||
* 1. 字符串类型:添加单引号,转义内部单引号
|
||||
* 2. 数字/布尔类型:直接转换为字符串
|
||||
* 3. null:转换为SQL的NULL关键字
|
||||
* 4. 日期类型:转换为SQL标准格式
|
||||
* 5. 其他类型:转为JSON字符串
|
||||
* 将变量值转换为SQL安全格式 1. 字符串类型:添加单引号,转义内部单引号 2. 数字/布尔类型:直接转换为字符串 3. null:转换为SQL的NULL关键字 4.
|
||||
* 日期类型:转换为SQL标准格式 5. 其他类型:转为JSON字符串
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @return SQL安全格式的字符串
|
||||
@@ -41,7 +36,7 @@ public class SqlValueConverter {
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
|
||||
if (value instanceof String) {
|
||||
// 转义单引号(防止SQL注入和语法错误)
|
||||
String strValue = (String) value;
|
||||
@@ -69,7 +64,7 @@ public class SqlValueConverter {
|
||||
if (values == null) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
|
||||
String[] result = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
result[i] = convertToSqlValue(values[i]);
|
||||
@@ -78,8 +73,7 @@ public class SqlValueConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将变量值转换为SQL安全格式(JSON字符串专用)
|
||||
* 使用双引号包围JSON字符串,避免单引号冲突
|
||||
* 将变量值转换为SQL安全格式(JSON字符串专用) 使用双引号包围JSON字符串,避免单引号冲突
|
||||
*
|
||||
* @param value 要转换的值
|
||||
* @return SQL安全格式的字符串
|
||||
@@ -88,7 +82,7 @@ public class SqlValueConverter {
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
|
||||
if (value instanceof String) {
|
||||
// JSON字符串:使用双引号包围,转义内部双引号
|
||||
String strValue = (String) value;
|
||||
@@ -116,12 +110,12 @@ public class SqlValueConverter {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (value instanceof String) {
|
||||
String strValue = (String) value;
|
||||
return strValue.contains("'") || strValue.contains("\\") || strValue.contains("\"");
|
||||
}
|
||||
|
||||
|
||||
return !(value instanceof Number || value instanceof Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
+85
-84
@@ -1,98 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.universal.databridge.mapper.DataBridgeConfigMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="cn.universal.databridge.entity.DataBridgeConfig">
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="source_scope" jdbcType="VARCHAR" property="sourceScope"/>
|
||||
<result column="source_product_keys" jdbcType="LONGVARCHAR" property="sourceProductKeys"/>
|
||||
<result column="source_application_id" jdbcType="BIGINT" property="sourceApplicationId"/>
|
||||
<result column="target_resource_id" jdbcType="BIGINT" property="targetResourceId"/>
|
||||
<result column="bridge_type" jdbcType="VARCHAR" property="bridgeType"/>
|
||||
<result column="template" jdbcType="LONGVARCHAR" property="template"/>
|
||||
<result column="magic_script" jdbcType="LONGVARCHAR" property="magicScript"/>
|
||||
<result column="config" jdbcType="LONGVARCHAR" property="config"/>
|
||||
<result column="status" jdbcType="TINYINT" property="status"/>
|
||||
<result column="description" jdbcType="VARCHAR" property="description"/>
|
||||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
</resultMap>
|
||||
<resultMap id="BaseResultMap" type="cn.universal.databridge.entity.DataBridgeConfig">
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="source_scope" jdbcType="VARCHAR" property="sourceScope"/>
|
||||
<result column="source_product_keys" jdbcType="LONGVARCHAR" property="sourceProductKeys"/>
|
||||
<result column="source_application_id" jdbcType="BIGINT" property="sourceApplicationId"/>
|
||||
<result column="target_resource_id" jdbcType="BIGINT" property="targetResourceId"/>
|
||||
<result column="bridge_type" jdbcType="VARCHAR" property="bridgeType"/>
|
||||
<result column="template" jdbcType="LONGVARCHAR" property="template"/>
|
||||
<result column="magic_script" jdbcType="LONGVARCHAR" property="magicScript"/>
|
||||
<result column="config" jdbcType="LONGVARCHAR" property="config"/>
|
||||
<result column="status" jdbcType="TINYINT" property="status"/>
|
||||
<result column="description" jdbcType="VARCHAR" property="description"/>
|
||||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id
|
||||
, name, source_scope, source_product_keys, source_application_id,
|
||||
<sql id="Base_Column_List">
|
||||
id
|
||||
, name, source_scope, source_product_keys, source_application_id,
|
||||
target_resource_id, bridge_type, template, magic_script, config,
|
||||
status, description, create_by, create_time, update_by, update_time
|
||||
</sql>
|
||||
</sql>
|
||||
|
||||
<!-- 根据产品KEY获取活跃配置(兼容新结构:ALL_PRODUCTS 直接命中;SPECIFIC_PRODUCTS 判断JSON列表包含;APPLICATION 由上层按应用过滤) -->
|
||||
<select id="selectActiveConfigsByProductKey" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE status = 1
|
||||
AND (
|
||||
source_scope = 'ALL_PRODUCTS'
|
||||
OR (
|
||||
source_scope = 'SPECIFIC_PRODUCTS'
|
||||
AND JSON_CONTAINS(CAST(source_product_keys AS JSON), CONCAT('"', #{productKey}, '"'))
|
||||
)
|
||||
)
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<!-- 根据产品KEY获取活跃配置(兼容新结构:ALL_PRODUCTS 直接命中;SPECIFIC_PRODUCTS 判断JSON列表包含;APPLICATION 由上层按应用过滤) -->
|
||||
<select id="selectActiveConfigsByProductKey" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE status = 1
|
||||
AND (
|
||||
source_scope = 'ALL_PRODUCTS'
|
||||
OR (
|
||||
source_scope = 'SPECIFIC_PRODUCTS'
|
||||
AND JSON_CONTAINS(CAST(source_product_keys AS JSON), CONCAT('"', #{productKey}, '"'))
|
||||
)
|
||||
)
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据桥接类型获取配置 -->
|
||||
<select id="selectConfigsByBridgeType" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE bridge_type = #{bridgeType}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<!-- 根据桥接类型获取配置 -->
|
||||
<select id="selectConfigsByBridgeType" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE bridge_type = #{bridgeType}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据目标资源ID获取配置 -->
|
||||
<select id="selectConfigsByTargetResourceId" parameterType="long" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE target_resource_id = #{targetResourceId}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<!-- 根据目标资源ID获取配置 -->
|
||||
<select id="selectConfigsByTargetResourceId" parameterType="long" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE target_resource_id = #{targetResourceId}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 批量更新配置状态 -->
|
||||
<update id="batchUpdateStatus">
|
||||
UPDATE iot_data_bridge_config
|
||||
SET status = #{status},
|
||||
update_by = #{updateBy},
|
||||
update_time = NOW()
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
<!-- 批量更新配置状态 -->
|
||||
<update id="batchUpdateStatus">
|
||||
UPDATE iot_data_bridge_config
|
||||
SET status = #{status},
|
||||
update_by = #{updateBy},
|
||||
update_time = NOW()
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<!-- 根据名称查询配置(用于重名检查) -->
|
||||
<select id="selectByName" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE name = #{name}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
<!-- 根据名称查询配置(用于重名检查) -->
|
||||
<select id="selectByName" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE name = #{name}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据创建者查询配置列表 -->
|
||||
<select id="selectByCreateBy" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE create_by = #{createBy}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<!-- 根据创建者查询配置列表 -->
|
||||
<select id="selectByCreateBy" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_data_bridge_config
|
||||
WHERE create_by = #{createBy}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
+32
-31
@@ -1,38 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.universal.databridge.mapper.DataInputLogMapper">
|
||||
|
||||
<!-- 查询最近的日志 -->
|
||||
<select id="selectRecentLogs" resultType="cn.universal.databridge.entity.DataInputLog">
|
||||
SELECT *
|
||||
FROM iot_data_input_log
|
||||
WHERE config_id = #{configId}
|
||||
ORDER BY create_time DESC
|
||||
LIMIT #{limit}
|
||||
</select>
|
||||
<!-- 查询最近的日志 -->
|
||||
<select id="selectRecentLogs" resultType="cn.universal.databridge.entity.DataInputLog">
|
||||
SELECT *
|
||||
FROM iot_data_input_log
|
||||
WHERE config_id = #{configId}
|
||||
ORDER BY create_time DESC
|
||||
LIMIT #{limit}
|
||||
</select>
|
||||
|
||||
<!-- 计算成功率 -->
|
||||
<select id="calculateSuccessRate" resultType="java.lang.Double">
|
||||
SELECT
|
||||
CASE
|
||||
WHEN SUM(message_count) = 0 THEN 0.0
|
||||
ELSE ROUND(SUM(success_count) * 100.0 / SUM(message_count), 2)
|
||||
END as success_rate
|
||||
FROM iot_data_input_log
|
||||
WHERE config_id = #{configId}
|
||||
<if test="startTime != null">
|
||||
AND create_time >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND create_time <= #{endTime}
|
||||
</if>
|
||||
</select>
|
||||
<!-- 计算成功率 -->
|
||||
<select id="calculateSuccessRate" resultType="java.lang.Double">
|
||||
SELECT
|
||||
CASE
|
||||
WHEN SUM(message_count) = 0 THEN 0.0
|
||||
ELSE ROUND(SUM(success_count) * 100.0 / SUM(message_count), 2)
|
||||
END as success_rate
|
||||
FROM iot_data_input_log
|
||||
WHERE config_id = #{configId}
|
||||
<if test="startTime != null">
|
||||
AND create_time >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND create_time <= #{endTime}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 删除过期日志 -->
|
||||
<delete id="deleteExpiredLogs">
|
||||
DELETE
|
||||
FROM iot_data_input_log
|
||||
WHERE create_time < #{expireTime}
|
||||
</delete>
|
||||
<!-- 删除过期日志 -->
|
||||
<delete id="deleteExpiredLogs">
|
||||
DELETE
|
||||
FROM iot_data_input_log
|
||||
WHERE create_time < #{expireTime}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
||||
+73
-72
@@ -1,84 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.universal.databridge.mapper.ResourceConnectionMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="cn.universal.databridge.entity.ResourceConnection">
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="type" jdbcType="VARCHAR" property="type"/>
|
||||
<result column="host" jdbcType="VARCHAR" property="host"/>
|
||||
<result column="port" jdbcType="INTEGER" property="port"/>
|
||||
<result column="username" jdbcType="VARCHAR" property="username"/>
|
||||
<result column="password" jdbcType="VARCHAR" property="password"/>
|
||||
<result column="database_name" jdbcType="VARCHAR" property="databaseName"/>
|
||||
<result column="extra_config" jdbcType="LONGVARCHAR" property="extraConfig"/>
|
||||
<result column="status" jdbcType="TINYINT" property="status"/>
|
||||
<result column="description" jdbcType="VARCHAR" property="description"/>
|
||||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
</resultMap>
|
||||
<resultMap id="BaseResultMap" type="cn.universal.databridge.entity.ResourceConnection">
|
||||
<id column="id" jdbcType="BIGINT" property="id"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="type" jdbcType="VARCHAR" property="type"/>
|
||||
<result column="host" jdbcType="VARCHAR" property="host"/>
|
||||
<result column="port" jdbcType="INTEGER" property="port"/>
|
||||
<result column="username" jdbcType="VARCHAR" property="username"/>
|
||||
<result column="password" jdbcType="VARCHAR" property="password"/>
|
||||
<result column="database_name" jdbcType="VARCHAR" property="databaseName"/>
|
||||
<result column="extra_config" jdbcType="LONGVARCHAR" property="extraConfig"/>
|
||||
<result column="status" jdbcType="TINYINT" property="status"/>
|
||||
<result column="description" jdbcType="VARCHAR" property="description"/>
|
||||
<result column="create_by" jdbcType="VARCHAR" property="createBy"/>
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
|
||||
<result column="update_by" jdbcType="VARCHAR" property="updateBy"/>
|
||||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id
|
||||
, name, type, host, port, username, password, database_name, extra_config,
|
||||
<sql id="Base_Column_List">
|
||||
id
|
||||
, name, type, host, port, username, password, database_name, extra_config,
|
||||
status, description, create_by, create_time, update_by, update_time
|
||||
</sql>
|
||||
</sql>
|
||||
|
||||
<!-- 根据资源类型获取活跃连接 -->
|
||||
<select id="selectActiveConnectionsByType" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE type = #{type}
|
||||
AND status = 1
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<!-- 根据资源类型获取活跃连接 -->
|
||||
<select id="selectActiveConnectionsByType" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE type = #{type}
|
||||
AND status = 1
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据主机和端口查询连接 -->
|
||||
<select id="selectByHostAndPort" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE host = #{host}
|
||||
AND port = #{port}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
<!-- 根据主机和端口查询连接 -->
|
||||
<select id="selectByHostAndPort" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE host = #{host}
|
||||
AND port = #{port}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 根据名称查询连接(用于重名检查) -->
|
||||
<select id="selectByName" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE name = #{name}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
<!-- 根据名称查询连接(用于重名检查) -->
|
||||
<select id="selectByName" resultMap="BaseResultMap">
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM iot_resource_connection
|
||||
WHERE name = #{name}
|
||||
<if test="excludeId != null">
|
||||
AND id != #{excludeId}
|
||||
</if>
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 批量更新连接状态 -->
|
||||
<update id="batchUpdateStatus">
|
||||
UPDATE iot_resource_connection
|
||||
SET status = #{status},
|
||||
update_by = #{updateBy},
|
||||
update_time = NOW()
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
<!-- 批量更新连接状态 -->
|
||||
<update id="batchUpdateStatus">
|
||||
UPDATE iot_resource_connection
|
||||
SET status = #{status},
|
||||
update_by = #{updateBy},
|
||||
update_time = NOW()
|
||||
WHERE id IN
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<!-- 测试连接可用性 -->
|
||||
<select id="testConnection" parameterType="long" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM iot_resource_connection
|
||||
WHERE id = #{id}
|
||||
AND status = 1
|
||||
</select>
|
||||
<!-- 测试连接可用性 -->
|
||||
<select id="testConnection" parameterType="long" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM iot_resource_connection
|
||||
WHERE id = #{id}
|
||||
AND status = 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
+27
-27
@@ -1,36 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-http</name>
|
||||
<description>HTTP数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-http</name>
|
||||
<description>HTTP数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
+169
-158
@@ -22,181 +22,192 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Component("http")
|
||||
@ConditionalOnProperty(prefix = "databridge.plugins.http", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
prefix = "databridge.plugins.http",
|
||||
name = "enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
@Slf4j
|
||||
public class HttpDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return PluginInfo.builder()
|
||||
.name("HTTP数据桥接插件")
|
||||
.version("1.0.0")
|
||||
.description("支持HTTP上报")
|
||||
.author("gitee.com/NexIoT")
|
||||
.pluginType("HTTP")
|
||||
.supportedResourceTypes(List.of("HTTP"))
|
||||
.dataDirection(PluginInfo.DataDirection.BIDIRECTIONAL)
|
||||
.category("网络")
|
||||
.icon("api")
|
||||
.build();
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return PluginInfo.builder()
|
||||
.name("HTTP数据桥接插件")
|
||||
.version("1.0.0")
|
||||
.description("支持HTTP上报")
|
||||
.author("gitee.com/NexIoT")
|
||||
.pluginType("HTTP")
|
||||
.supportedResourceTypes(List.of("HTTP"))
|
||||
.dataDirection(PluginInfo.DataDirection.BIDIRECTIONAL)
|
||||
.category("网络")
|
||||
.icon("api")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processProcessedData(
|
||||
Object processedData,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = buildHeaders(config);
|
||||
|
||||
String payload;
|
||||
if (processedData instanceof String) {
|
||||
payload = (String) processedData;
|
||||
} else {
|
||||
// 如果Magic脚本返回的不是字符串,使用模板处理
|
||||
Map<String, Object> variables = buildTemplateVariables(request, config);
|
||||
payload = processTemplate(config.getTemplate(), variables);
|
||||
}
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(payload, headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("HTTP请求成功: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
} else {
|
||||
log.warn("HTTP请求失败: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP数据处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS,
|
||||
SourceScope.SPECIFIC_PRODUCTS,
|
||||
SourceScope.APPLICATION
|
||||
);
|
||||
@Override
|
||||
protected void processTemplateResult(
|
||||
String templateResult,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = buildHeaders(config);
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(templateResult, headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("HTTP请求成功: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
} else {
|
||||
log.warn("HTTP请求失败: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP模板处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processProcessedData(Object processedData, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = buildHeaders(config);
|
||||
|
||||
String payload;
|
||||
if (processedData instanceof String) {
|
||||
payload = (String) processedData;
|
||||
} else {
|
||||
// 如果Magic脚本返回的不是字符串,使用模板处理
|
||||
Map<String, Object> variables = buildTemplateVariables(request, config);
|
||||
payload = processTemplate(config.getTemplate(), variables);
|
||||
}
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(payload, headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("HTTP请求成功: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
} else {
|
||||
log.warn("HTTP请求失败: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP数据处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>("{}", headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
return response.getStatusCode().is2xxSuccessful();
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return validateMagicScript(config.getMagicScript());
|
||||
}
|
||||
|
||||
private String buildUrl(ResourceConnection connection) {
|
||||
// 默认使用http协议,可以通过extraConfig配置SSL
|
||||
String protocol = "http";
|
||||
if (StrUtil.isNotBlank(connection.getExtraConfig())) {
|
||||
try {
|
||||
JSONObject extraConfig = JSONUtil.parseObj(connection.getExtraConfig());
|
||||
if (extraConfig.getBool("ssl", false)) {
|
||||
protocol = "https";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析SSL配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processTemplateResult(String templateResult, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = buildHeaders(config);
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(templateResult, headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("HTTP请求成功: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
} else {
|
||||
log.warn("HTTP请求失败: status={}, body={}", response.getStatusCode(), response.getBody());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP模板处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String path = "/";
|
||||
if (StrUtil.isNotBlank(connection.getExtraConfig())) {
|
||||
try {
|
||||
JSONObject extraConfig = JSONUtil.parseObj(connection.getExtraConfig());
|
||||
path = extraConfig.getStr("path", "/");
|
||||
} catch (Exception e) {
|
||||
log.warn("解析路径配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try {
|
||||
String url = buildUrl(connection);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>("{}", headers);
|
||||
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
|
||||
|
||||
return response.getStatusCode().is2xxSuccessful();
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
return String.format(
|
||||
"%s://%s:%d%s", protocol, connection.getHost(), connection.getPort(), path);
|
||||
}
|
||||
|
||||
private HttpHeaders buildHeaders(DataBridgeConfig config) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 从统一配置中提取headers
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
if (configJson.containsKey("headers")) {
|
||||
JSONObject headersJson = configJson.getJSONObject("headers");
|
||||
for (String key : headersJson.keySet()) {
|
||||
headers.set(key, headersJson.getStr(key));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析HTTP配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return validateMagicScript(config.getMagicScript());
|
||||
return headers;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTemplateVariables(
|
||||
BaseUPRequest request, DataBridgeConfig config) {
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
// 设备信息
|
||||
if (request.getIoTDeviceDTO() != null) {
|
||||
variables.put("deviceKey", request.getIoTDeviceDTO().getDeviceId());
|
||||
variables.put("productKey", request.getIoTDeviceDTO().getProductKey());
|
||||
}
|
||||
|
||||
private String buildUrl(ResourceConnection connection) {
|
||||
// 默认使用http协议,可以通过extraConfig配置SSL
|
||||
String protocol = "http";
|
||||
if (StrUtil.isNotBlank(connection.getExtraConfig())) {
|
||||
try {
|
||||
JSONObject extraConfig = JSONUtil.parseObj(connection.getExtraConfig());
|
||||
if (extraConfig.getBool("ssl", false)) {
|
||||
protocol = "https";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析SSL配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
String path = "/";
|
||||
if (StrUtil.isNotBlank(connection.getExtraConfig())) {
|
||||
try {
|
||||
JSONObject extraConfig = JSONUtil.parseObj(connection.getExtraConfig());
|
||||
path = extraConfig.getStr("path", "/");
|
||||
} catch (Exception e) {
|
||||
log.warn("解析路径配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return String.format("%s://%s:%d%s", protocol, connection.getHost(), connection.getPort(), path);
|
||||
// 消息信息
|
||||
variables.put("messageType", request.getMessageType().name());
|
||||
variables.put("timestamp", System.currentTimeMillis());
|
||||
variables.put("properties", request.getProperties());
|
||||
|
||||
// 配置信息
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
variables.put("configJson", JSONUtil.parseObj(config.getConfig()));
|
||||
} catch (Exception e) {
|
||||
log.warn("解析配置JSON失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHeaders buildHeaders(DataBridgeConfig config) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// 从统一配置中提取headers
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
if (configJson.containsKey("headers")) {
|
||||
JSONObject headersJson = configJson.getJSONObject("headers");
|
||||
for (String key : headersJson.keySet()) {
|
||||
headers.set(key, headersJson.getStr(key));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析HTTP配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTemplateVariables(BaseUPRequest request, DataBridgeConfig config) {
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
// 设备信息
|
||||
if (request.getIoTDeviceDTO() != null) {
|
||||
variables.put("deviceKey", request.getIoTDeviceDTO().getDeviceId());
|
||||
variables.put("productKey", request.getIoTDeviceDTO().getProductKey());
|
||||
}
|
||||
|
||||
// 消息信息
|
||||
variables.put("messageType", request.getMessageType().name());
|
||||
variables.put("timestamp", System.currentTimeMillis());
|
||||
variables.put("properties", request.getProperties());
|
||||
|
||||
// 配置信息
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
variables.put("configJson", JSONUtil.parseObj(config.getConfig()));
|
||||
} catch (Exception e) {
|
||||
log.warn("解析配置JSON失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
|
||||
+42
-42
@@ -1,54 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-influxdb</name>
|
||||
<description>InfluxDB数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-influxdb</name>
|
||||
<description>InfluxDB数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web (用于HTTP客户端) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Web (用于HTTP客户端) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- InfluxDB客户端 -->
|
||||
<dependency>
|
||||
<groupId>com.influxdb</groupId>
|
||||
<artifactId>influxdb-client-java</artifactId>
|
||||
<version>6.10.0</version>
|
||||
</dependency>
|
||||
<!-- InfluxDB客户端 -->
|
||||
<dependency>
|
||||
<groupId>com.influxdb</groupId>
|
||||
<artifactId>influxdb-client-java</artifactId>
|
||||
<version>6.10.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+43
-43
@@ -1,55 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-iotdb</name>
|
||||
<description>IoTDB数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-iotdb</name>
|
||||
<description>IoTDB数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- IoTDB JDBC驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-jdbc</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
<!-- IoTDB JDBC驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-jdbc</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- IoTDB Session API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-session</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
<!-- IoTDB Session API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.iotdb</groupId>
|
||||
<artifactId>iotdb-session</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+52
-52
@@ -1,67 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-jdbc</name>
|
||||
<description>JDBC数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-jdbc</name>
|
||||
<description>JDBC数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JDBC -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- JDBC -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- MySQL驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- PostgreSQL驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<!-- PostgreSQL驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- H2数据库(用于测试) -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- H2数据库(用于测试) -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+5
-8
@@ -106,22 +106,21 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin {
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
|
||||
|
||||
try {
|
||||
// 获取数据源
|
||||
DataSource dataSource = connectionManager.getOrCreateDataSource(connection);
|
||||
|
||||
|
||||
// 使用Spring的JdbcTemplate执行SQL
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
jdbcTemplate.update(templateResult);
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("执行SQL失败: {} - {}", templateResult, e.getMessage());
|
||||
|
||||
|
||||
// 清除可能损坏的连接池
|
||||
connectionManager.removeDataSource(connection);
|
||||
|
||||
|
||||
throw new RuntimeException("SQL执行失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
@@ -183,6 +182,4 @@ public class DefaultJdbcOutPlugin extends AbstractDataOutputPlugin {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+51
-45
@@ -16,53 +16,59 @@ import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* MySQL连接诊断工具
|
||||
* 用于排查连接问题
|
||||
*/
|
||||
/** MySQL连接诊断工具 用于排查连接问题 */
|
||||
public class ConnectionDiagnostic {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 测试不同的连接配置
|
||||
testConnection("jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true", "root", "password");
|
||||
testConnection("jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true", "root", "password");
|
||||
testConnection("jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true&useLocalSessionState=true", "root", "password");
|
||||
public static void main(String[] args) {
|
||||
// 测试不同的连接配置
|
||||
testConnection(
|
||||
"jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true",
|
||||
"root",
|
||||
"password");
|
||||
testConnection(
|
||||
"jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true",
|
||||
"root",
|
||||
"password");
|
||||
testConnection(
|
||||
"jdbc:mysql://192.168.31.194:3306/test?useSSL=false&allowPublicKeyRetrieval=true&useLocalSessionState=true",
|
||||
"root",
|
||||
"password");
|
||||
}
|
||||
|
||||
private static void testConnection(String url, String username, String password) {
|
||||
System.out.println("=== 测试连接配置 ===");
|
||||
System.out.println("URL: " + url);
|
||||
System.out.println("Username: " + username);
|
||||
|
||||
try {
|
||||
// 设置连接属性
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", username);
|
||||
props.setProperty("password", password);
|
||||
props.setProperty("useSSL", "false");
|
||||
props.setProperty("allowPublicKeyRetrieval", "true");
|
||||
props.setProperty("autoReconnect", "true");
|
||||
props.setProperty("failOverReadOnly", "false");
|
||||
props.setProperty("maxReconnects", "3");
|
||||
props.setProperty("connectTimeout", "30000");
|
||||
props.setProperty("socketTimeout", "30000");
|
||||
|
||||
Connection conn = DriverManager.getConnection(url, props);
|
||||
System.out.println("✅ 连接成功!");
|
||||
|
||||
// 测试查询
|
||||
conn.createStatement().executeQuery("SELECT 1");
|
||||
System.out.println("✅ 查询测试成功!");
|
||||
|
||||
conn.close();
|
||||
System.out.println("✅ 连接关闭成功!");
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println("❌ 连接失败: " + e.getMessage());
|
||||
System.out.println("错误代码: " + e.getErrorCode());
|
||||
System.out.println("SQL状态: " + e.getSQLState());
|
||||
}
|
||||
|
||||
private static void testConnection(String url, String username, String password) {
|
||||
System.out.println("=== 测试连接配置 ===");
|
||||
System.out.println("URL: " + url);
|
||||
System.out.println("Username: " + username);
|
||||
|
||||
try {
|
||||
// 设置连接属性
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", username);
|
||||
props.setProperty("password", password);
|
||||
props.setProperty("useSSL", "false");
|
||||
props.setProperty("allowPublicKeyRetrieval", "true");
|
||||
props.setProperty("autoReconnect", "true");
|
||||
props.setProperty("failOverReadOnly", "false");
|
||||
props.setProperty("maxReconnects", "3");
|
||||
props.setProperty("connectTimeout", "30000");
|
||||
props.setProperty("socketTimeout", "30000");
|
||||
|
||||
Connection conn = DriverManager.getConnection(url, props);
|
||||
System.out.println("✅ 连接成功!");
|
||||
|
||||
// 测试查询
|
||||
conn.createStatement().executeQuery("SELECT 1");
|
||||
System.out.println("✅ 查询测试成功!");
|
||||
|
||||
conn.close();
|
||||
System.out.println("✅ 连接关闭成功!");
|
||||
|
||||
} catch (SQLException e) {
|
||||
System.out.println("❌ 连接失败: " + e.getMessage());
|
||||
System.out.println("错误代码: " + e.getErrorCode());
|
||||
System.out.println("SQL状态: " + e.getSQLState());
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
+37
-33
@@ -34,29 +34,31 @@ public class QuickTemplateTest {
|
||||
|
||||
try {
|
||||
// 测试通用实现
|
||||
AbstractDataBridgePlugin genericPlugin = new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
AbstractDataBridgePlugin genericPlugin =
|
||||
new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of();
|
||||
}
|
||||
};
|
||||
java.lang.reflect.Method genericMethod =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class);
|
||||
}
|
||||
};
|
||||
java.lang.reflect.Method genericMethod =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod(
|
||||
"processTemplate", String.class, Map.class);
|
||||
genericMethod.setAccessible(true);
|
||||
String genericResult = (String) genericMethod.invoke(genericPlugin, template, variables);
|
||||
|
||||
@@ -64,7 +66,7 @@ public class QuickTemplateTest {
|
||||
|
||||
// 测试JDBC实现
|
||||
DefaultJdbcOutPlugin jdbcPlugin = new DefaultJdbcOutPlugin();
|
||||
java.lang.reflect.Method jdbcMethod =
|
||||
java.lang.reflect.Method jdbcMethod =
|
||||
DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class);
|
||||
jdbcMethod.setAccessible(true);
|
||||
String jdbcResult = (String) jdbcMethod.invoke(jdbcPlugin, template, variables);
|
||||
@@ -104,31 +106,33 @@ public class QuickTemplateTest {
|
||||
|
||||
String template = "Nested: #{root.level2.value}";
|
||||
|
||||
AbstractDataBridgePlugin plugin = new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
AbstractDataBridgePlugin plugin =
|
||||
new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
java.lang.reflect.Method method =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class);
|
||||
java.lang.reflect.Method method =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod(
|
||||
"processTemplate", String.class, Map.class);
|
||||
method.setAccessible(true);
|
||||
String result = (String) method.invoke(plugin, template, variables);
|
||||
|
||||
|
||||
+25
-22
@@ -29,10 +29,10 @@ public class TemplateDemo {
|
||||
variables.put("properties", properties);
|
||||
|
||||
// 测试模板
|
||||
String template =
|
||||
"Device: #{deviceId}, Product: #{productKey}, Name: #{deviceName}, " +
|
||||
"CSQ: #{properties.csq}, Code: #{properties.code}, " +
|
||||
"City: #{properties.location.city}, Country: #{properties.location.country}";
|
||||
String template =
|
||||
"Device: #{deviceId}, Product: #{productKey}, Name: #{deviceName}, "
|
||||
+ "CSQ: #{properties.csq}, Code: #{properties.code}, "
|
||||
+ "City: #{properties.location.city}, Country: #{properties.location.country}";
|
||||
|
||||
System.out.println("原始模板:");
|
||||
System.out.println(template);
|
||||
@@ -41,29 +41,31 @@ public class TemplateDemo {
|
||||
try {
|
||||
// 1. 通用模板处理
|
||||
System.out.println("1. 通用模板处理 (AbstractDataBridgePlugin):");
|
||||
AbstractDataBridgePlugin genericPlugin = new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
AbstractDataBridgePlugin genericPlugin =
|
||||
new AbstractDataBridgePlugin() {
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of();
|
||||
}
|
||||
};
|
||||
java.lang.reflect.Method genericMethod =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class);
|
||||
}
|
||||
};
|
||||
java.lang.reflect.Method genericMethod =
|
||||
AbstractDataBridgePlugin.class.getDeclaredMethod(
|
||||
"processTemplate", String.class, Map.class);
|
||||
genericMethod.setAccessible(true);
|
||||
String genericResult = (String) genericMethod.invoke(genericPlugin, template, variables);
|
||||
System.out.println("结果: " + genericResult);
|
||||
@@ -73,7 +75,7 @@ public class TemplateDemo {
|
||||
// 2. JDBC专用模板处理
|
||||
System.out.println("2. JDBC专用模板处理 (DefaultJdbcOutPlugin):");
|
||||
DefaultJdbcOutPlugin jdbcPlugin = new DefaultJdbcOutPlugin();
|
||||
java.lang.reflect.Method jdbcMethod =
|
||||
java.lang.reflect.Method jdbcMethod =
|
||||
DefaultJdbcOutPlugin.class.getDeclaredMethod("processTemplate", String.class, Map.class);
|
||||
jdbcMethod.setAccessible(true);
|
||||
String jdbcResult = (String) jdbcMethod.invoke(jdbcPlugin, template, variables);
|
||||
@@ -102,7 +104,8 @@ public class TemplateDemo {
|
||||
System.out.println("- 文本模板: Device #{deviceId} is online");
|
||||
System.out.println();
|
||||
System.out.println("JDBC实现适用于:");
|
||||
System.out.println("- SQL模板: INSERT INTO devices(id, name) VALUES(#{deviceId}, #{deviceName})");
|
||||
System.out.println(
|
||||
"- SQL模板: INSERT INTO devices(id, name) VALUES(#{deviceId}, #{deviceName})");
|
||||
System.out.println("- 需要SQL注入防护的场景");
|
||||
System.out.println();
|
||||
|
||||
|
||||
+12
-12
@@ -22,14 +22,14 @@ public class TemplateProcessingTest {
|
||||
variables.put("productKey", "product001");
|
||||
variables.put("deviceName", "测试设备");
|
||||
variables.put("iotId", "iot001");
|
||||
variables.put("deviceNode", IoTConstant.DeviceNode.DEVICE.toString());
|
||||
variables.put("deviceNode", IoTConstant.DeviceNode.DEVICE.toString());
|
||||
variables.put("messageType", "PROPERTIES");
|
||||
variables.put("properties", properties);
|
||||
variables.put("data", "raw_data_content");
|
||||
|
||||
// 测试模板
|
||||
String template =
|
||||
"""
|
||||
// 测试模板
|
||||
String template =
|
||||
"""
|
||||
{
|
||||
"deviceId": "#{deviceId}",
|
||||
"location": {
|
||||
@@ -91,10 +91,10 @@ public class TemplateProcessingTest {
|
||||
variables.put("properties", properties);
|
||||
variables.put("data", "raw_data_content");
|
||||
|
||||
// 测试模板
|
||||
String template =
|
||||
"INSERT INTO device_data(device_id,product_key,device_name,iot_id,device_node,message_type,csq,code,temperature,properties,raw_data) "
|
||||
+ "VALUES(#{deviceId},#{productKey},#{deviceName},#{iotId},#{deviceNode},#{messageType},#{properties.csq},#{properties.code},#{properties.temperature},#{properties},#{data});";
|
||||
// 测试模板
|
||||
String template =
|
||||
"INSERT INTO device_data(device_id,product_key,device_name,iot_id,device_node,message_type,csq,code,temperature,properties,raw_data) "
|
||||
+ "VALUES(#{deviceId},#{productKey},#{deviceName},#{iotId},#{deviceNode},#{messageType},#{properties.csq},#{properties.code},#{properties.temperature},#{properties},#{data});";
|
||||
|
||||
// 创建插件实例进行测试
|
||||
DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin();
|
||||
@@ -141,8 +141,8 @@ public class TemplateProcessingTest {
|
||||
variables.put("deviceId", "device001");
|
||||
variables.put("properties", properties);
|
||||
|
||||
String template =
|
||||
"INSERT INTO test_table(csq,code,temperature) VALUES(#{properties.csq},#{properties.code},#{properties.temperature});";
|
||||
String template =
|
||||
"INSERT INTO test_table(csq,code,temperature) VALUES(#{properties.csq},#{properties.code},#{properties.temperature});";
|
||||
|
||||
DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin();
|
||||
|
||||
@@ -178,8 +178,8 @@ public class TemplateProcessingTest {
|
||||
variables.put("deviceId", "device001");
|
||||
variables.put("properties", properties);
|
||||
|
||||
String template =
|
||||
"INSERT INTO test_table(csq,non_existent) VALUES(#{properties.csq},#{properties.nonExistent});";
|
||||
String template =
|
||||
"INSERT INTO test_table(csq,non_existent) VALUES(#{properties.csq},#{properties.nonExistent});";
|
||||
|
||||
DefaultJdbcOutPlugin plugin = new DefaultJdbcOutPlugin();
|
||||
|
||||
|
||||
+41
-41
@@ -1,53 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-kafka</name>
|
||||
<description>Kafka数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-kafka</name>
|
||||
<description>Kafka数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Kafka -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
<!-- Kafka -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+67
-46
@@ -57,17 +57,18 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS,
|
||||
SourceScope.SPECIFIC_PRODUCTS,
|
||||
SourceScope.APPLICATION
|
||||
);
|
||||
SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processProcessedData(Object processedData, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
protected void processProcessedData(
|
||||
Object processedData,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try (KafkaProducer<String, String> producer = createKafkaProducer(connection)) {
|
||||
String topic = extractTopic(config);
|
||||
|
||||
|
||||
String payload;
|
||||
if (processedData instanceof String) {
|
||||
payload = (String) processedData;
|
||||
@@ -76,17 +77,22 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
Map<String, Object> variables = buildTemplateVariables(request, config);
|
||||
payload = processTemplate(config.getTemplate(), variables);
|
||||
}
|
||||
|
||||
|
||||
ProducerRecord<String, String> record = new ProducerRecord<>(topic, payload);
|
||||
producer.send(record, (metadata, exception) -> {
|
||||
if (exception != null) {
|
||||
log.error("Kafka消息发送失败: {}", exception.getMessage(), exception);
|
||||
} else {
|
||||
log.debug("Kafka消息发送成功: topic={}, partition={}, offset={}",
|
||||
metadata.topic(), metadata.partition(), metadata.offset());
|
||||
}
|
||||
});
|
||||
|
||||
producer.send(
|
||||
record,
|
||||
(metadata, exception) -> {
|
||||
if (exception != null) {
|
||||
log.error("Kafka消息发送失败: {}", exception.getMessage(), exception);
|
||||
} else {
|
||||
log.debug(
|
||||
"Kafka消息发送成功: topic={}, partition={}, offset={}",
|
||||
metadata.topic(),
|
||||
metadata.partition(),
|
||||
metadata.offset());
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Kafka数据处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -94,20 +100,29 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processTemplateResult(String templateResult, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
protected void processTemplateResult(
|
||||
String templateResult,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try (KafkaProducer<String, String> producer = createKafkaProducer(connection)) {
|
||||
String topic = extractTopic(config);
|
||||
|
||||
|
||||
ProducerRecord<String, String> record = new ProducerRecord<>(topic, templateResult);
|
||||
producer.send(record, (metadata, exception) -> {
|
||||
if (exception != null) {
|
||||
log.error("Kafka消息发送失败: {}", exception.getMessage(), exception);
|
||||
} else {
|
||||
log.debug("Kafka消息发送成功: topic={}, partition={}, offset={}",
|
||||
metadata.topic(), metadata.partition(), metadata.offset());
|
||||
}
|
||||
});
|
||||
|
||||
producer.send(
|
||||
record,
|
||||
(metadata, exception) -> {
|
||||
if (exception != null) {
|
||||
log.error("Kafka消息发送失败: {}", exception.getMessage(), exception);
|
||||
} else {
|
||||
log.debug(
|
||||
"Kafka消息发送成功: topic={}, partition={}, offset={}",
|
||||
metadata.topic(),
|
||||
metadata.partition(),
|
||||
metadata.offset());
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Kafka模板处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -128,13 +143,13 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return StrUtil.isNotBlank(extractTopic(config))
|
||||
&& validateMagicScript(config.getMagicScript());
|
||||
return StrUtil.isNotBlank(extractTopic(config)) && validateMagicScript(config.getMagicScript());
|
||||
}
|
||||
|
||||
private KafkaProducer<String, String> createKafkaProducer(ResourceConnection connection) {
|
||||
Properties props = new Properties();
|
||||
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
props.put(
|
||||
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
|
||||
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
|
||||
@@ -143,32 +158,37 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
|
||||
props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
|
||||
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
|
||||
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
props.put("sasl.mechanism", "PLAIN");
|
||||
props.put("sasl.jaas.config",
|
||||
String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
props.put(
|
||||
"sasl.jaas.config",
|
||||
String.format(
|
||||
"org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
connection.getUsername(), connection.getPassword()));
|
||||
}
|
||||
|
||||
|
||||
return new KafkaProducer<>(props);
|
||||
}
|
||||
|
||||
private AdminClient createAdminClient(ResourceConnection connection) {
|
||||
Properties props = new Properties();
|
||||
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
props.put(
|
||||
AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
|
||||
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
props.put("sasl.mechanism", "PLAIN");
|
||||
props.put("sasl.jaas.config",
|
||||
String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
props.put(
|
||||
"sasl.jaas.config",
|
||||
String.format(
|
||||
"org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
connection.getUsername(), connection.getPassword()));
|
||||
}
|
||||
|
||||
|
||||
return AdminClient.create(props);
|
||||
}
|
||||
|
||||
@@ -177,7 +197,7 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return "iot-device-data"; // 默认topic
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
return configJson.getStr("topic", "iot-device-data");
|
||||
@@ -187,20 +207,21 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTemplateVariables(BaseUPRequest request, DataBridgeConfig config) {
|
||||
private Map<String, Object> buildTemplateVariables(
|
||||
BaseUPRequest request, DataBridgeConfig config) {
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
|
||||
// 设备信息
|
||||
if (request.getIoTDeviceDTO() != null) {
|
||||
variables.put("deviceKey", request.getIoTDeviceDTO().getDeviceId());
|
||||
variables.put("productKey", request.getIoTDeviceDTO().getProductKey());
|
||||
}
|
||||
|
||||
|
||||
// 消息信息
|
||||
variables.put("messageType", request.getMessageType().name());
|
||||
variables.put("timestamp", System.currentTimeMillis());
|
||||
variables.put("properties", request.getProperties());
|
||||
|
||||
|
||||
// 配置信息
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
@@ -209,7 +230,7 @@ public class KafkaDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
log.warn("解析配置JSON失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+162
-157
@@ -18,174 +18,179 @@ import org.apache.kafka.clients.admin.AdminClientConfig;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Kafka数据输入插件
|
||||
* 支持从Kafka消费数据
|
||||
*/
|
||||
/** Kafka数据输入插件 支持从Kafka消费数据 */
|
||||
@Component("kafkaInput")
|
||||
@ConditionalOnProperty(prefix = "databridge.plugins.kafka-input", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
prefix = "databridge.plugins.kafka-input",
|
||||
name = "enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
@Slf4j
|
||||
public class KafkaDataInputPlugin extends AbstractDataInputPlugin {
|
||||
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return PluginInfo.builder()
|
||||
.name("Kafka数据输入插件")
|
||||
.version("1.0.0")
|
||||
.description("支持从Kafka消费数据,实现数据输入功能")
|
||||
.author("gitee.com/NexIoT")
|
||||
.pluginType("KAFKA_INPUT")
|
||||
.supportedResourceTypes(List.of("KAFKA"))
|
||||
.dataDirection(PluginInfo.DataDirection.INPUT)
|
||||
.category("消息队列")
|
||||
.icon("message")
|
||||
.build();
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
return PluginInfo.builder()
|
||||
.name("Kafka数据输入插件")
|
||||
.version("1.0.0")
|
||||
.description("支持从Kafka消费数据,实现数据输入功能")
|
||||
.author("gitee.com/NexIoT")
|
||||
.pluginType("KAFKA_INPUT")
|
||||
.supportedResourceTypes(List.of("KAFKA"))
|
||||
.dataDirection(PluginInfo.DataDirection.INPUT)
|
||||
.category("消息队列")
|
||||
.icon("message")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendToIoTPlatform(Object processedData, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug("推送数据到IoT平台: 配置ID={}", config.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processExternalDataWithDefaultLogic(
|
||||
Object externalData, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
// 处理外部数据,转换为IoT平台格式
|
||||
BaseUPRequest request = transformToIoTData(externalData, config);
|
||||
if (request != null) {
|
||||
sendToIoTPlatform(request, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理外部数据失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try (AdminClient adminClient = createAdminClient(connection)) {
|
||||
// 尝试列出topics来测试连接
|
||||
adminClient.listTopics().names().get();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Kafka连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
// 验证统一配置
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS,
|
||||
SourceScope.SPECIFIC_PRODUCTS,
|
||||
SourceScope.APPLICATION
|
||||
);
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// 验证必要的配置项
|
||||
return configJson.containsKey("topic") && StrUtil.isNotBlank(configJson.getStr("topic"));
|
||||
} catch (Exception e) {
|
||||
log.error("验证Kafka输入配置失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendToIoTPlatform(Object processedData, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug("推送数据到IoT平台: 配置ID={}", config.getId());
|
||||
}
|
||||
private BaseUPRequest transformToIoTData(Object externalData, DataBridgeConfig config) {
|
||||
try {
|
||||
if (externalData instanceof String) {
|
||||
String payload = (String) externalData;
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
@Override
|
||||
protected void processExternalDataWithDefaultLogic(Object externalData, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
// 处理外部数据,转换为IoT平台格式
|
||||
BaseUPRequest request = transformToIoTData(externalData, config);
|
||||
if (request != null) {
|
||||
sendToIoTPlatform(request, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理外部数据失败: {}", e.getMessage(), e);
|
||||
// 构建BaseUPRequest
|
||||
BaseUPRequest request = new BaseUPRequest();
|
||||
request.setMessageType(MessageType.PROPERTIES);
|
||||
request.setProperties(new HashMap<>());
|
||||
|
||||
// 设置设备信息
|
||||
IoTDeviceDTO deviceDTO = new IoTDeviceDTO();
|
||||
deviceDTO.setDeviceId(data.getStr("deviceId", "unknown"));
|
||||
deviceDTO.setProductKey(data.getStr("productKey", "unknown"));
|
||||
request.setIoTDeviceDTO(deviceDTO);
|
||||
|
||||
// 设置属性
|
||||
if (data.containsKey("properties")) {
|
||||
Object properties = data.get("properties");
|
||||
if (properties instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> props = (Map<String, Object>) properties;
|
||||
request.setProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("转换外部数据为IoT格式失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AdminClient createAdminClient(ResourceConnection connection) {
|
||||
Properties props = new Properties();
|
||||
props.put(
|
||||
AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
props.put("sasl.mechanism", "PLAIN");
|
||||
props.put(
|
||||
"sasl.jaas.config",
|
||||
String.format(
|
||||
"org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
connection.getUsername(), connection.getPassword()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try (AdminClient adminClient = createAdminClient(connection)) {
|
||||
// 尝试列出topics来测试连接
|
||||
adminClient.listTopics().names().get();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Kafka连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return AdminClient.create(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
// 验证统一配置
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// 验证必要的配置项
|
||||
return configJson.containsKey("topic") &&
|
||||
StrUtil.isNotBlank(configJson.getStr("topic"));
|
||||
} catch (Exception e) {
|
||||
log.error("验证Kafka输入配置失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// TODO: Kafka消费者创建方法,用于数据输入
|
||||
// private KafkaConsumer<String, String> createKafkaConsumer(ResourceConnection connection, String
|
||||
// topic) {
|
||||
// Properties props = new Properties();
|
||||
// props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
// String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
// props.put(ConsumerConfig.GROUP_ID_CONFIG, "databridge-input-group");
|
||||
// props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
|
||||
// StringDeserializer.class.getName());
|
||||
// props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
|
||||
// StringDeserializer.class.getName());
|
||||
// props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
|
||||
// props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
|
||||
// props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
|
||||
//
|
||||
// if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
// props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
// props.put("sasl.mechanism", "PLAIN");
|
||||
// props.put("sasl.jaas.config",
|
||||
// String.format("org.apache.kafka.common.security.plain.PlainLoginModule required
|
||||
// username=\"%s\" password=\"%s\";",
|
||||
// connection.getUsername(), connection.getPassword()));
|
||||
// }
|
||||
//
|
||||
// KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
|
||||
// consumer.subscribe(Collections.singletonList(topic));
|
||||
// return consumer;
|
||||
// }
|
||||
|
||||
private BaseUPRequest transformToIoTData(Object externalData, DataBridgeConfig config) {
|
||||
try {
|
||||
if (externalData instanceof String) {
|
||||
String payload = (String) externalData;
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
// 构建BaseUPRequest
|
||||
BaseUPRequest request = new BaseUPRequest();
|
||||
request.setMessageType(MessageType.PROPERTIES);
|
||||
request.setProperties(new HashMap<>());
|
||||
|
||||
// 设置设备信息
|
||||
IoTDeviceDTO deviceDTO = new IoTDeviceDTO();
|
||||
deviceDTO.setDeviceId(data.getStr("deviceId", "unknown"));
|
||||
deviceDTO.setProductKey(data.getStr("productKey", "unknown"));
|
||||
request.setIoTDeviceDTO(deviceDTO);
|
||||
|
||||
// 设置属性
|
||||
if (data.containsKey("properties")) {
|
||||
Object properties = data.get("properties");
|
||||
if (properties instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> props = (Map<String, Object>) properties;
|
||||
request.setProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("转换外部数据为IoT格式失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AdminClient createAdminClient(ResourceConnection connection) {
|
||||
Properties props = new Properties();
|
||||
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
props.put("sasl.mechanism", "PLAIN");
|
||||
props.put("sasl.jaas.config",
|
||||
String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
connection.getUsername(), connection.getPassword()));
|
||||
}
|
||||
|
||||
return AdminClient.create(props);
|
||||
}
|
||||
|
||||
// TODO: Kafka消费者创建方法,用于数据输入
|
||||
// private KafkaConsumer<String, String> createKafkaConsumer(ResourceConnection connection, String topic) {
|
||||
// Properties props = new Properties();
|
||||
// props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,
|
||||
// String.format("%s:%d", connection.getHost(), connection.getPort()));
|
||||
// props.put(ConsumerConfig.GROUP_ID_CONFIG, "databridge-input-group");
|
||||
// props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
|
||||
// props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
|
||||
// props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
|
||||
// props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
|
||||
// props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
|
||||
//
|
||||
// if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
// props.put("security.protocol", "SASL_PLAINTEXT");
|
||||
// props.put("sasl.mechanism", "PLAIN");
|
||||
// props.put("sasl.jaas.config",
|
||||
// String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%s\" password=\"%s\";",
|
||||
// connection.getUsername(), connection.getPassword()));
|
||||
// }
|
||||
//
|
||||
// KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
|
||||
// consumer.subscribe(Collections.singletonList(topic));
|
||||
// return consumer;
|
||||
// }
|
||||
|
||||
// TODO: 提取topic的方法,用于Kafka订阅
|
||||
// private String extractTopic(DataBridgeConfig config) {
|
||||
// try {
|
||||
// JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// return configJson.getStr("topic", "iot-input-data");
|
||||
// } catch (Exception e) {
|
||||
// log.warn("解析Kafka输入配置失败,使用默认topic: {}", e.getMessage());
|
||||
// return "iot-input-data";
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// TODO: 提取topic的方法,用于Kafka订阅
|
||||
// private String extractTopic(DataBridgeConfig config) {
|
||||
// try {
|
||||
// JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// return configJson.getStr("topic", "iot-input-data");
|
||||
// } catch (Exception e) {
|
||||
// log.warn("解析Kafka输入配置失败,使用默认topic: {}", e.getMessage());
|
||||
// return "iot-input-data";
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
+37
-37
@@ -1,48 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugin-mqtt</name>
|
||||
<description>MQTT数据桥接插件</description>
|
||||
<name>cn-universal-data-bridge-plugin-mqtt</name>
|
||||
<description>MQTT数据桥接插件</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MQTT -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.2.5</version>
|
||||
</dependency>
|
||||
<!-- MQTT -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
+27
-22
@@ -44,17 +44,18 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS,
|
||||
SourceScope.SPECIFIC_PRODUCTS,
|
||||
SourceScope.APPLICATION
|
||||
);
|
||||
SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processProcessedData(Object processedData, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
protected void processProcessedData(
|
||||
Object processedData,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try (MqttClient client = createMqttClient(connection)) {
|
||||
client.connect(buildOptions(connection));
|
||||
|
||||
|
||||
String payload;
|
||||
if (processedData instanceof String) {
|
||||
payload = (String) processedData;
|
||||
@@ -63,11 +64,11 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
Map<String, Object> variables = buildTemplateVariables(request, config);
|
||||
payload = processTemplate(config.getTemplate(), variables);
|
||||
}
|
||||
|
||||
|
||||
String topic = extractTopic(config);
|
||||
client.publish(topic, new MqttMessage(payload.getBytes()));
|
||||
client.disconnect();
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("MQTT数据处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -75,14 +76,18 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processTemplateResult(String templateResult, BaseUPRequest request, DataBridgeConfig config, ResourceConnection connection) {
|
||||
protected void processTemplateResult(
|
||||
String templateResult,
|
||||
BaseUPRequest request,
|
||||
DataBridgeConfig config,
|
||||
ResourceConnection connection) {
|
||||
try (MqttClient client = createMqttClient(connection)) {
|
||||
client.connect(buildOptions(connection));
|
||||
|
||||
|
||||
String topic = extractTopic(config);
|
||||
client.publish(topic, new MqttMessage(templateResult.getBytes()));
|
||||
client.disconnect();
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("MQTT模板处理失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -106,8 +111,7 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
return StrUtil.isNotBlank(extractTopic(config))
|
||||
&& validateMagicScript(config.getMagicScript());
|
||||
return StrUtil.isNotBlank(extractTopic(config)) && validateMagicScript(config.getMagicScript());
|
||||
}
|
||||
|
||||
private MqttClient createMqttClient(ResourceConnection connection) throws Exception {
|
||||
@@ -120,14 +124,14 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
options.setCleanSession(true);
|
||||
options.setConnectionTimeout(30);
|
||||
options.setKeepAliveInterval(60);
|
||||
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
options.setUserName(connection.getUsername());
|
||||
}
|
||||
if (StrUtil.isNotBlank(connection.getPassword())) {
|
||||
options.setPassword(connection.getPassword().toCharArray());
|
||||
}
|
||||
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -136,7 +140,7 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return "/iot/bridge"; // 默认topic
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
return configJson.getStr("topic", "/iot/bridge");
|
||||
@@ -146,20 +150,21 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> buildTemplateVariables(BaseUPRequest request, DataBridgeConfig config) {
|
||||
private Map<String, Object> buildTemplateVariables(
|
||||
BaseUPRequest request, DataBridgeConfig config) {
|
||||
Map<String, Object> variables = new HashMap<>();
|
||||
|
||||
|
||||
// 设备信息
|
||||
if (request.getIoTDeviceDTO() != null) {
|
||||
variables.put("deviceKey", request.getIoTDeviceDTO().getDeviceId());
|
||||
variables.put("productKey", request.getIoTDeviceDTO().getProductKey());
|
||||
}
|
||||
|
||||
|
||||
// 消息信息
|
||||
variables.put("messageType", request.getMessageType().name());
|
||||
variables.put("timestamp", System.currentTimeMillis());
|
||||
variables.put("properties", request.getProperties());
|
||||
|
||||
|
||||
// 配置信息
|
||||
if (StrUtil.isNotBlank(config.getConfig())) {
|
||||
try {
|
||||
@@ -168,7 +173,7 @@ public class MqttDataBridgePlugin extends AbstractDataOutputPlugin {
|
||||
log.warn("解析配置JSON失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+119
-121
@@ -17,17 +17,18 @@ import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* MQTT数据输入插件
|
||||
* 支持从MQTT订阅数据
|
||||
*/
|
||||
/** MQTT数据输入插件 支持从MQTT订阅数据 */
|
||||
@Component("mqttInput")
|
||||
@ConditionalOnProperty(prefix = "databridge.plugins.mqtt-input", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
prefix = "databridge.plugins.mqtt-input",
|
||||
name = "enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
@Slf4j
|
||||
public class MqttDataInputPlugin extends AbstractDataInputPlugin {
|
||||
|
||||
// TODO: 存储正在运行的MQTT客户端,用于管理连接生命周期
|
||||
// private final Map<Long, MqttClient> runningClients = new ConcurrentHashMap<>();
|
||||
// TODO: 存储正在运行的MQTT客户端,用于管理连接生命周期
|
||||
// private final Map<Long, MqttClient> runningClients = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public PluginInfo getPluginInfo() {
|
||||
@@ -47,125 +48,122 @@ public class MqttDataInputPlugin extends AbstractDataInputPlugin {
|
||||
@Override
|
||||
public List<SourceScope> getSupportedSourceScopes() {
|
||||
return List.of(
|
||||
SourceScope.ALL_PRODUCTS,
|
||||
SourceScope.SPECIFIC_PRODUCTS,
|
||||
SourceScope.APPLICATION
|
||||
);
|
||||
SourceScope.ALL_PRODUCTS, SourceScope.SPECIFIC_PRODUCTS, SourceScope.APPLICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendToIoTPlatform(Object processedData, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug("推送数据到IoT平台: 配置ID={}", config.getId());
|
||||
@Override
|
||||
protected void sendToIoTPlatform(Object processedData, DataBridgeConfig config) {
|
||||
// TODO: 实现推送到IoT平台的逻辑
|
||||
// 这里可以通过消息队列、HTTP API等方式将数据推送到IoT平台
|
||||
log.debug("推送数据到IoT平台: 配置ID={}", config.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processExternalDataWithDefaultLogic(
|
||||
Object externalData, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
// 处理外部数据,转换为IoT平台格式
|
||||
BaseUPRequest request = transformToIoTData(externalData, config);
|
||||
if (request != null) {
|
||||
sendToIoTPlatform(request, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理外部数据失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try {
|
||||
String broker = String.format("tcp://%s:%d", connection.getHost(), connection.getPort());
|
||||
MqttClient client = new MqttClient(broker, MqttClient.generateClientId());
|
||||
MqttConnectOptions options = buildOptions(connection);
|
||||
client.connect(options);
|
||||
client.disconnect();
|
||||
client.close();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("MQTT连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
// 验证统一配置
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processExternalDataWithDefaultLogic(Object externalData, DataBridgeConfig config, ResourceConnection connection) {
|
||||
try {
|
||||
// 处理外部数据,转换为IoT平台格式
|
||||
BaseUPRequest request = transformToIoTData(externalData, config);
|
||||
if (request != null) {
|
||||
sendToIoTPlatform(request, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理外部数据失败: {}", e.getMessage(), e);
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// 验证必要的配置项
|
||||
return configJson.containsKey("topic") && StrUtil.isNotBlank(configJson.getStr("topic"));
|
||||
} catch (Exception e) {
|
||||
log.error("验证MQTT输入配置失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private BaseUPRequest transformToIoTData(Object externalData, DataBridgeConfig config) {
|
||||
try {
|
||||
if (externalData instanceof String) {
|
||||
String payload = (String) externalData;
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
// 构建BaseUPRequest
|
||||
BaseUPRequest request = new BaseUPRequest();
|
||||
request.setMessageType(MessageType.PROPERTIES);
|
||||
request.setProperties(new HashMap<>());
|
||||
|
||||
// 设置设备信息
|
||||
IoTDeviceDTO deviceDTO = new IoTDeviceDTO();
|
||||
deviceDTO.setDeviceId(data.getStr("deviceId", "unknown"));
|
||||
deviceDTO.setProductKey(data.getStr("productKey", "unknown"));
|
||||
request.setIoTDeviceDTO(deviceDTO);
|
||||
|
||||
// 设置属性
|
||||
if (data.containsKey("properties")) {
|
||||
Object properties = data.get("properties");
|
||||
if (properties instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> props = (Map<String, Object>) properties;
|
||||
request.setProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("转换外部数据为IoT格式失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MqttConnectOptions buildOptions(ResourceConnection connection) {
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true);
|
||||
options.setConnectionTimeout(30);
|
||||
options.setKeepAliveInterval(60);
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
options.setUserName(connection.getUsername());
|
||||
}
|
||||
if (StrUtil.isNotBlank(connection.getPassword())) {
|
||||
options.setPassword(connection.getPassword().toCharArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean testConnection(ResourceConnection connection) {
|
||||
try {
|
||||
String broker = String.format("tcp://%s:%d", connection.getHost(), connection.getPort());
|
||||
MqttClient client = new MqttClient(broker, MqttClient.generateClientId());
|
||||
MqttConnectOptions options = buildOptions(connection);
|
||||
client.connect(options);
|
||||
client.disconnect();
|
||||
client.close();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("MQTT连接测试失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean validateConfig(DataBridgeConfig config) {
|
||||
// 验证统一配置
|
||||
if (StrUtil.isBlank(config.getConfig())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// 验证必要的配置项
|
||||
return configJson.containsKey("topic") &&
|
||||
StrUtil.isNotBlank(configJson.getStr("topic"));
|
||||
} catch (Exception e) {
|
||||
log.error("验证MQTT输入配置失败: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private BaseUPRequest transformToIoTData(Object externalData, DataBridgeConfig config) {
|
||||
try {
|
||||
if (externalData instanceof String) {
|
||||
String payload = (String) externalData;
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
// 构建BaseUPRequest
|
||||
BaseUPRequest request = new BaseUPRequest();
|
||||
request.setMessageType(MessageType.PROPERTIES);
|
||||
request.setProperties(new HashMap<>());
|
||||
|
||||
// 设置设备信息
|
||||
IoTDeviceDTO deviceDTO = new IoTDeviceDTO();
|
||||
deviceDTO.setDeviceId(data.getStr("deviceId", "unknown"));
|
||||
deviceDTO.setProductKey(data.getStr("productKey", "unknown"));
|
||||
request.setIoTDeviceDTO(deviceDTO);
|
||||
|
||||
// 设置属性
|
||||
if (data.containsKey("properties")) {
|
||||
Object properties = data.get("properties");
|
||||
if (properties instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> props = (Map<String, Object>) properties;
|
||||
request.setProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("转换外部数据为IoT格式失败: {}", e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MqttConnectOptions buildOptions(ResourceConnection connection) {
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true);
|
||||
options.setConnectionTimeout(30);
|
||||
options.setKeepAliveInterval(60);
|
||||
|
||||
if (StrUtil.isNotBlank(connection.getUsername())) {
|
||||
options.setUserName(connection.getUsername());
|
||||
}
|
||||
if (StrUtil.isNotBlank(connection.getPassword())) {
|
||||
options.setPassword(connection.getPassword().toCharArray());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
// TODO: 提取topic的方法,用于MQTT订阅
|
||||
// private String extractTopic(DataBridgeConfig config) {
|
||||
// try {
|
||||
// JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// return configJson.getStr("topic", "/iot/input");
|
||||
// } catch (Exception e) {
|
||||
// log.warn("解析MQTT输入配置失败,使用默认topic: {}", e.getMessage());
|
||||
// return "/iot/input";
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// TODO: 提取topic的方法,用于MQTT订阅
|
||||
// private String extractTopic(DataBridgeConfig config) {
|
||||
// try {
|
||||
// JSONObject configJson = JSONUtil.parseObj(config.getConfig());
|
||||
// return configJson.getStr("topic", "/iot/input");
|
||||
// } catch (Exception e) {
|
||||
// log.warn("解析MQTT输入配置失败,使用默认topic: {}", e.getMessage());
|
||||
// return "/iot/input";
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<artifactId>cn-universal-data-bridge-plugins</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-plugins</name>
|
||||
<description>数据桥接插件模块</description>
|
||||
<name>cn-universal-data-bridge-plugins</name>
|
||||
<description>数据桥接插件模块</description>
|
||||
|
||||
<modules>
|
||||
<module>cn-universal-data-bridge-plugin-jdbc</module>
|
||||
<module>cn-universal-data-bridge-plugin-kafka</module>
|
||||
<module>cn-universal-data-bridge-plugin-mqtt</module>
|
||||
<module>cn-universal-data-bridge-plugin-http</module>
|
||||
<module>cn-universal-data-bridge-plugin-iotdb</module>
|
||||
<module>cn-universal-data-bridge-plugin-influxdb</module>
|
||||
</modules>
|
||||
<modules>
|
||||
<module>cn-universal-data-bridge-plugin-jdbc</module>
|
||||
<module>cn-universal-data-bridge-plugin-kafka</module>
|
||||
<module>cn-universal-data-bridge-plugin-mqtt</module>
|
||||
<module>cn-universal-data-bridge-plugin-http</module>
|
||||
<module>cn-universal-data-bridge-plugin-iotdb</module>
|
||||
<module>cn-universal-data-bridge-plugin-influxdb</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-starter</name>
|
||||
<description>数据桥接自动配置</description>
|
||||
<name>cn-universal-data-bridge-starter</name>
|
||||
<description>数据桥接自动配置</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
+2
-3
@@ -11,15 +11,14 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* 数据桥接自动配置类
|
||||
*
|
||||
* <p>负责自动装配数据桥接相关的组件和插件</p>
|
||||
* <p>负责自动装配数据桥接相关的组件和插件
|
||||
*
|
||||
* <p><b>注意:</b>@EnableAsync 已在主启动类中全局启用,此处无需重复配置</p>
|
||||
* <p><b>注意:</b>@EnableAsync 已在主启动类中全局启用,此处无需重复配置
|
||||
*
|
||||
* @author NexIoT
|
||||
* @since 1.0
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge-web</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>cn-universal-data-bridge-web</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge-web</name>
|
||||
<description>数据桥接Web接口(预留)</description>
|
||||
<name>cn-universal-data-bridge-web</name>
|
||||
<description>数据桥接Web接口(预留)</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- 插件依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- 插件依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-jdbc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-kafka</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-mqtt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-iotdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-data-bridge-plugin-influxdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
-- 数据桥接字典类型定义
|
||||
-- 删除现有数据(如果需要重新导入)
|
||||
-- DELETE FROM sys_dict_data WHERE dict_type LIKE 'databridge_%';
|
||||
-- DELETE FROM sys_dict_type WHERE dict_type LIKE 'databridge_%';
|
||||
|
||||
-- 插入字典类型
|
||||
INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (192, '数据桥接资源类型', 'databridge_resource_type', '0', 'admin', NOW(), '', NULL, '数据桥接支持的资源类型'),
|
||||
(193, '资源能力', 'databridge_resource_capability', '0', 'admin', NOW(), '', NULL, '资源支持的数据流向'),
|
||||
(194, 'MySQL配置字段', 'databridge_mysql_fields', '0', 'admin', NOW(), '', NULL, 'MySQL资源配置字段定义'),
|
||||
(195, 'Kafka配置字段', 'databridge_kafka_fields', '0', 'admin', NOW(), '', NULL, 'Kafka资源配置字段定义'),
|
||||
(196, 'MQTT配置字段', 'databridge_mqtt_fields', '0', 'admin', NOW(), '', NULL, 'MQTT资源配置字段定义'),
|
||||
(197, 'HTTP配置字段', 'databridge_http_fields', '0', 'admin', NOW(), '', NULL, 'HTTP资源配置字段定义'),
|
||||
(198, 'IoTDB配置字段', 'databridge_iotdb_fields', '0', 'admin', NOW(), '', NULL, 'IoTDB资源配置字段定义'),
|
||||
(199, 'InfluxDB配置字段', 'databridge_influxdb_fields', '0', 'admin', NOW(), '', NULL,
|
||||
'InfluxDB资源配置字段定义'),
|
||||
(200, 'Elasticsearch配置字段', 'databridge_elasticsearch_fields', '0', 'admin', NOW(), '', NULL,
|
||||
'Elasticsearch资源配置字段定义'),
|
||||
(201, '云平台类型', 'databridge_cloud_platform', '0', 'admin', NOW(), '', NULL, '支持的云平台类型'),
|
||||
(202, '阿里云配置字段', 'databridge_aliyun_iot_fields', '0', 'admin', NOW(), '', NULL, '阿里云IoT平台配置字段'),
|
||||
(203, '腾讯云配置字段', 'databridge_tencent_iot_fields', '0', 'admin', NOW(), '', NULL, '腾讯云IoT平台配置字段'),
|
||||
(205, 'Redis配置字段', 'databridge_redis_fields', '0', 'admin', NOW(), '', NULL, 'Redis缓存配置字段定义'),
|
||||
(206, '数据流向', 'databridge_direction', '0', 'admin', NOW(), '', NULL, '数据桥接流向类型定义'),
|
||||
(207, '桥接状态', 'databridge_status', '0', 'admin', NOW(), '', NULL, '数据桥接配置状态定义'),
|
||||
(208, '桥接类型', 'databridge_type', '0', 'admin', NOW(), '', NULL, '数据桥接类型定义'),
|
||||
(209, '消息类型', 'databridge_message_type', '0', 'admin', NOW(), '', NULL, 'IoT消息类型定义'),
|
||||
(211, '华为云配置字段', 'databridge_huawei_iot_fields', '0', 'admin', NOW(), '', NULL,
|
||||
'华为云IoT平台配置字段定义');
|
||||
@@ -1,93 +0,0 @@
|
||||
-- 基础字典数据
|
||||
-- 资源类型
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (609, 1, 'MySQL数据库', 'MYSQL', 'databridge_resource_type', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'支持双向数据流转'),
|
||||
(610, 2, 'Kafka消息队列', 'KAFKA', 'databridge_resource_type', '', 'processing', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '支持双向数据流转'),
|
||||
(611, 3, 'MQTT消息代理', 'MQTT', 'databridge_resource_type', '', 'processing', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '支持双向数据流转'),
|
||||
(612, 4, 'HTTP接口', 'HTTP', 'databridge_resource_type', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'支持双向数据流转'),
|
||||
(613, 5, 'IoTDB时序数据库', 'IOTDB', 'databridge_resource_type', '', 'default', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '仅支持数据输出'),
|
||||
(614, 6, 'InfluxDB时序数据库', 'INFLUXDB', 'databridge_resource_type', '', 'default', 'Y', '0', 'admin', NOW(),
|
||||
'', NULL, '仅支持数据输出'),
|
||||
(615, 7, 'Elasticsearch搜索引擎', 'ELASTICSEARCH', 'databridge_resource_type', '', 'default', 'Y', '0', 'admin',
|
||||
NOW(), '', NULL, '仅支持数据输出'),
|
||||
(750, 8, 'Redis缓存', 'REDIS', 'databridge_resource_type', '', 'danger', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'支持双向数据流转');
|
||||
|
||||
-- 资源能力
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (616, 1, '双向流转', 'BIDIRECTIONAL', 'databridge_resource_capability', '', 'success', 'Y', '0', 'admin', NOW(),
|
||||
'', NULL, '支持输入和输出'),
|
||||
(617, 2, '仅输出', 'OUTPUT_ONLY', 'databridge_resource_capability', '', 'warning', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '仅支持数据输出');
|
||||
|
||||
-- 云平台类型
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (618, 1, '阿里云IoT平台', 'ALIYUN_IOT', 'databridge_cloud_platform', '', 'success', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '阿里云物联网平台'),
|
||||
(619, 2, '腾讯云IoT平台', 'TENCENT_IOT', 'databridge_cloud_platform', '', 'processing', 'Y', '0', 'admin', NOW(),
|
||||
'', NULL, '腾讯云物联网平台'),
|
||||
(620, 3, '华为云IoT平台', 'HUAWEI_IOT', 'databridge_cloud_platform', '', 'warning', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '华为云物联网平台');
|
||||
|
||||
-- 数据流向
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (634, 1, '数据输出', 'OUT', 'databridge_direction', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'IoT平台数据输出到外部系统'),
|
||||
(635, 2, '数据输入', 'IN', 'databridge_direction', '', 'processing', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'外部系统数据输入到IoT平台'),
|
||||
(636, 3, '双向流转', 'BOTH', 'databridge_direction', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'支持双向数据流转');
|
||||
|
||||
-- 桥接状态
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (720, 1, '已停用', '0', 'databridge_status', '', 'danger', 'Y', '0', 'admin', NOW(), '', NULL, '桥接配置已停用'),
|
||||
(721, 2, '已启用', '1', 'databridge_status', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'桥接配置已启用'),
|
||||
(722, 3, '运行中', '2', 'databridge_status', '', 'processing', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'桥接任务运行中'),
|
||||
(723, 4, '已暂停', '3', 'databridge_status', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'桥接任务已暂停'),
|
||||
(724, 5, '错误', '4', 'databridge_status', '', 'danger', 'Y', '0', 'admin', NOW(), '', NULL, '桥接任务出错');
|
||||
|
||||
-- 桥接类型
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (730, 1, '实时桥接', 'REALTIME', 'databridge_type', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'实时数据桥接'),
|
||||
(731, 2, '定时桥接', 'SCHEDULED', 'databridge_type', '', 'processing', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'定时数据桥接'),
|
||||
(732, 3, '批量桥接', 'BATCH', 'databridge_type', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'批量数据桥接'),
|
||||
(733, 4, '事件触发', 'EVENT_DRIVEN', 'databridge_type', '', 'default', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'事件驱动桥接');
|
||||
|
||||
-- 消息类型
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (740, 1, '属性上报', 'PROPERTIES', 'databridge_message_type', '', 'success', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'设备属性数据上报'),
|
||||
(741, 2, '事件上报', 'EVENT', 'databridge_message_type', '', 'processing', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'设备事件数据上报'),
|
||||
(742, 3, '功能调用', 'FUNCTIONS', 'databridge_message_type', '', 'warning', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'设备功能调用'),
|
||||
(743, 4, '设备上线', 'DEVICE_ONLINE', 'databridge_message_type', '', 'default', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '设备上线消息'),
|
||||
(744, 5, '设备离线', 'DEVICE_OFFLINE', 'databridge_message_type', '', 'default', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '设备离线消息');
|
||||
@@ -1,24 +0,0 @@
|
||||
-- MySQL配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (621, 1, '主机地址', 'host', 'databridge_mysql_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入MySQL主机地址"}'),
|
||||
(622, 2, '端口', 'port', 'databridge_mysql_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":3306,"min":1,"max":65535}'),
|
||||
(623, 3, '用户名', 'username', 'databridge_mysql_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入数据库用户名"}'),
|
||||
(624, 4, '密码', 'password', 'databridge_mysql_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":true,"placeholder":"请输入数据库密码"}'),
|
||||
(625, 5, '数据库名', 'databaseName', 'databridge_mysql_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入数据库名称"}'),
|
||||
(770, 6, '字符集', 'charset', 'databridge_mysql_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["utf8","utf8mb4","latin1"],"default":"utf8mb4"}'),
|
||||
(771, 7, '连接超时', 'connectionTimeout', 'databridge_mysql_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":30000,"min":1000,"max":300000}'),
|
||||
(772, 8, '最大连接数', 'maxConnections', 'databridge_mysql_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":10,"min":1,"max":100}'),
|
||||
(773, 9, '表名前缀', 'tablePrefix', 'databridge_mysql_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入表名前缀(可选)"}'),
|
||||
(774, 10, '批量大小', 'batchSize', 'databridge_mysql_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1000,"min":1,"max":10000}');
|
||||
@@ -1,24 +0,0 @@
|
||||
-- Kafka配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (626, 1, '主机地址', 'host', 'databridge_kafka_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入Kafka主机地址"}'),
|
||||
(627, 2, '端口', 'port', 'databridge_kafka_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":9092}'),
|
||||
(628, 3, '安全协议', 'securityProtocol', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["PLAINTEXT","SSL","SASL_PLAINTEXT","SASL_SSL"],"default":"PLAINTEXT"}'),
|
||||
(629, 4, 'SASL机制', 'saslMechanism', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["PLAIN","SCRAM-SHA-256","SCRAM-SHA-512"]}'),
|
||||
(760, 5, '主题名称', 'topic', 'databridge_kafka_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入Kafka主题名称"}'),
|
||||
(761, 6, '消费者组', 'groupId', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入消费者组ID(输入模式)"}'),
|
||||
(762, 7, '分区数', 'partitions', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1,"min":1,"max":100}'),
|
||||
(763, 8, '副本数', 'replicationFactor', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1,"min":1,"max":10}'),
|
||||
(764, 9, '序列化器', 'serializer', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["StringSerializer","JsonSerializer","AvroSerializer"],"default":"JsonSerializer"}'),
|
||||
(765, 10, '压缩类型', 'compressionType', 'databridge_kafka_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["none","gzip","snappy","lz4","zstd"],"default":"none"}');
|
||||
@@ -1,24 +0,0 @@
|
||||
-- MQTT配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (660, 1, '主机地址', 'host', 'databridge_mqtt_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入MQTT Broker地址"}'),
|
||||
(661, 2, '端口', 'port', 'databridge_mqtt_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":1883,"min":1,"max":65535}'),
|
||||
(662, 3, '用户名', 'username', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入MQTT用户名(可选)"}'),
|
||||
(663, 4, '密码', 'password', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":false,"placeholder":"请输入MQTT密码(可选)"}'),
|
||||
(664, 5, '客户端ID', 'clientId', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入客户端ID(可选)"}'),
|
||||
(665, 6, 'QoS等级', 'qos', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["0","1","2"],"default":"1"}'),
|
||||
(666, 7, '保持连接', 'keepAlive', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":60,"min":10,"max":3600}'),
|
||||
(667, 8, '清除会话', 'cleanSession', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"switch","required":false,"default":true}'),
|
||||
(668, 9, '订阅主题', 'subscribeTopic', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入订阅主题(输入模式)"}'),
|
||||
(669, 10, '发布主题', 'publishTopic', 'databridge_mqtt_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入发布主题(输出模式)"}');
|
||||
@@ -1,20 +0,0 @@
|
||||
-- HTTP配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (670, 1, '基础URL', 'baseUrl', 'databridge_http_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入HTTP基础URL"}'),
|
||||
(671, 2, '请求方法', 'method', 'databridge_http_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":true,"options":["GET","POST","PUT","DELETE"],"default":"POST"}'),
|
||||
(672, 3, '内容类型', 'contentType', 'databridge_http_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":true,"options":["application/json","application/x-www-form-urlencoded","text/plain"],"default":"application/json"}'),
|
||||
(673, 4, '认证类型', 'authType', 'databridge_http_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":false,"options":["none","basic","bearer","apikey"],"default":"none"}'),
|
||||
(674, 5, '认证令牌', 'authToken', 'databridge_http_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":false,"placeholder":"请输入认证令牌"}'),
|
||||
(675, 6, '超时时间', 'timeout', 'databridge_http_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":30000,"min":1000,"max":300000}'),
|
||||
(676, 7, '重试次数', 'retryTimes', 'databridge_http_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":3,"min":0,"max":10}'),
|
||||
(677, 8, '自定义头部', 'customHeaders', 'databridge_http_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"textarea","required":false,"placeholder":"请输入自定义HTTP头部(JSON格式)"}');
|
||||
@@ -1,18 +0,0 @@
|
||||
-- IoTDB配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (690, 1, '主机地址', 'host', 'databridge_iotdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入IoTDB主机地址"}'),
|
||||
(691, 2, '端口', 'port', 'databridge_iotdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":6667,"min":1,"max":65535}'),
|
||||
(692, 3, '用户名', 'username', 'databridge_iotdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"default":"root","placeholder":"请输入IoTDB用户名"}'),
|
||||
(693, 4, '密码', 'password', 'databridge_iotdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":true,"default":"root","placeholder":"请输入IoTDB密码"}'),
|
||||
(694, 5, '存储组', 'storageGroup', 'databridge_iotdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入存储组名称(可选)"}'),
|
||||
(695, 6, '时间序列路径', 'timeseriesPath', 'databridge_iotdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入时间序列路径模板"}'),
|
||||
(696, 7, '批量大小', 'batchSize', 'databridge_iotdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1000,"min":1,"max":10000}');
|
||||
@@ -1,20 +0,0 @@
|
||||
-- InfluxDB配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (700, 1, '主机地址', 'host', 'databridge_influxdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入InfluxDB主机地址"}'),
|
||||
(701, 2, '端口', 'port', 'databridge_influxdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":8086,"min":1,"max":65535}'),
|
||||
(702, 3, '数据库名', 'database', 'databridge_influxdb_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入InfluxDB数据库名"}'),
|
||||
(703, 4, '用户名', 'username', 'databridge_influxdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入InfluxDB用户名(可选)"}'),
|
||||
(704, 5, '密码', 'password', 'databridge_influxdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":false,"placeholder":"请输入InfluxDB密码(可选)"}'),
|
||||
(705, 6, '保留策略', 'retentionPolicy', 'databridge_influxdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"default":"autogen","placeholder":"请输入保留策略"}'),
|
||||
(706, 7, '测量名', 'measurement', 'databridge_influxdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入测量名模板"}'),
|
||||
(707, 8, '批量大小', 'batchSize', 'databridge_influxdb_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1000,"min":1,"max":10000}');
|
||||
@@ -1,20 +0,0 @@
|
||||
-- Elasticsearch配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (710, 1, '主机地址', 'host', 'databridge_elasticsearch_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入Elasticsearch主机地址"}'),
|
||||
(711, 2, '端口', 'port', 'databridge_elasticsearch_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":9200,"min":1,"max":65535}'),
|
||||
(712, 3, '索引名', 'indexName', 'databridge_elasticsearch_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入Elasticsearch索引名"}'),
|
||||
(713, 4, '用户名', 'username', 'databridge_elasticsearch_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入Elasticsearch用户名(可选)"}'),
|
||||
(714, 5, '密码', 'password', 'databridge_elasticsearch_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":false,"placeholder":"请输入Elasticsearch密码(可选)"}'),
|
||||
(715, 6, '文档类型', 'docType', 'databridge_elasticsearch_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"default":"_doc","placeholder":"请输入文档类型"}'),
|
||||
(716, 7, '批量大小', 'batchSize', 'databridge_elasticsearch_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":1000,"min":1,"max":10000}'),
|
||||
(717, 8, '刷新策略', 'refreshPolicy', 'databridge_elasticsearch_fields', '', '', 'N', '0', 'admin', NOW(), '',
|
||||
NULL, '{"type":"select","required":false,"options":["none","wait_for","immediate"],"default":"none"}');
|
||||
@@ -1,20 +0,0 @@
|
||||
-- Redis配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (680, 1, '主机地址', 'host', 'databridge_redis_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入Redis主机地址"}'),
|
||||
(681, 2, '端口', 'port', 'databridge_redis_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":true,"default":6379,"min":1,"max":65535}'),
|
||||
(682, 3, '密码', 'password', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":false,"placeholder":"请输入Redis密码(可选)"}'),
|
||||
(683, 4, '数据库索引', 'database', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":0,"min":0,"max":15}'),
|
||||
(684, 5, '连接超时', 'connectionTimeout', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":5000,"min":1000,"max":60000}'),
|
||||
(685, 6, '读取超时', 'readTimeout', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":3000,"min":1000,"max":60000}'),
|
||||
(686, 7, '最大连接数', 'maxConnections', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"number","required":false,"default":8,"min":1,"max":100}'),
|
||||
(687, 8, '键前缀', 'keyPrefix', 'databridge_redis_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入键前缀(可选)"}');
|
||||
@@ -1,42 +0,0 @@
|
||||
-- 阿里云IoT配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (630, 1, 'Region ID', 'regionId', 'databridge_aliyun_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":true,"options":["cn-shanghai","cn-beijing","cn-shenzhen","ap-southeast-1"],"default":"cn-shanghai"}'),
|
||||
(631, 2, 'Access Key ID', 'accessKeyId', 'databridge_aliyun_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '',
|
||||
NULL, '{"type":"input","required":true,"placeholder":"请输入AccessKey ID"}'),
|
||||
(632, 3, 'Access Key Secret', 'accessKeySecret', 'databridge_aliyun_iot_fields', '', '', 'Y', '0', 'admin',
|
||||
NOW(), '', NULL, '{"type":"password","required":true,"placeholder":"请输入AccessKey Secret"}'),
|
||||
(633, 4, '实例ID', 'instanceId', 'databridge_aliyun_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入IoT实例ID"}');
|
||||
|
||||
-- 腾讯云IoT配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (640, 1, 'Region', 'region', 'databridge_tencent_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":true,"options":["ap-guangzhou","ap-shanghai","ap-beijing","ap-singapore"],"default":"ap-guangzhou","placeholder":"请选择地域"}'),
|
||||
(641, 2, 'Secret ID', 'secretId', 'databridge_tencent_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入腾讯云Secret ID"}'),
|
||||
(642, 3, 'Secret Key', 'secretKey', 'databridge_tencent_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":true,"placeholder":"请输入腾讯云Secret Key"}'),
|
||||
(643, 4, '产品ID', 'productId', 'databridge_tencent_iot_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入产品ID(可选)"}'),
|
||||
(644, 5, '设备名称', 'deviceName', 'databridge_tencent_iot_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入设备名称(可选)"}');
|
||||
|
||||
-- 华为云IoT配置字段
|
||||
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`,
|
||||
`list_class`, `is_default`, `status`, `create_by`, `create_time`, `update_by`,
|
||||
`update_time`, `remark`)
|
||||
VALUES (650, 1, 'Region', 'region', 'databridge_huawei_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"select","required":true,"options":["cn-north-4","cn-south-1","cn-east-2","ap-southeast-1"],"default":"cn-north-4","placeholder":"请选择地域"}'),
|
||||
(651, 2, '项目ID', 'projectId', 'databridge_huawei_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入华为云项目ID"}'),
|
||||
(652, 3, 'App ID', 'appId', 'databridge_huawei_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":true,"placeholder":"请输入应用ID"}'),
|
||||
(653, 4, 'App Secret', 'appSecret', 'databridge_huawei_iot_fields', '', '', 'Y', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"password","required":true,"placeholder":"请输入应用密钥"}'),
|
||||
(654, 5, '应用名称', 'appName', 'databridge_huawei_iot_fields', '', '', 'N', '0', 'admin', NOW(), '', NULL,
|
||||
'{"type":"input","required":false,"placeholder":"请输入应用名称(可选)"}');
|
||||
@@ -1,32 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>cn-universal</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>cn-universal</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<artifactId>cn-universal-data-bridge</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>cn-universal-data-bridge</name>
|
||||
<description>数据桥接模块</description>
|
||||
<name>cn-universal-data-bridge</name>
|
||||
<description>数据桥接模块</description>
|
||||
|
||||
<modules>
|
||||
<module>cn-universal-data-bridge-core</module>
|
||||
<module>cn-universal-data-bridge-plugins</module>
|
||||
<module>cn-universal-data-bridge-web</module>
|
||||
<module>cn-universal-data-bridge-starter</module>
|
||||
</modules>
|
||||
<modules>
|
||||
<module>cn-universal-data-bridge-core</module>
|
||||
<module>cn-universal-data-bridge-plugins</module>
|
||||
<module>cn-universal-data-bridge-web</module>
|
||||
<module>cn-universal-data-bridge-starter</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<!-- Redis 相关依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<!-- excel工具 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
<!-- 文件上传 -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
<!-- IP2Region IP地址定位库 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>2.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<!-- Redis 相关依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<!-- excel工具 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
<!-- 文件上传 -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
</dependency>
|
||||
<!-- IP2Region IP地址定位库 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>2.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+187
-179
@@ -19,8 +19,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 延迟启动工具类
|
||||
* 提供轻量级的延迟启动功能,等待所有Web容器启动完成
|
||||
* 延迟启动工具类 提供轻量级的延迟启动功能,等待所有Web容器启动完成
|
||||
*
|
||||
* @author gitee.com/NexIoT
|
||||
* @version 1.0
|
||||
@@ -30,85 +29,89 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class DelayedStartupUtil {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
@Autowired private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 延迟执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param delaySeconds 延迟秒数
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeWithDelay(Runnable task, long delaySeconds, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
log.info("延迟启动任务,等待{}秒后执行...", delaySeconds);
|
||||
Thread.sleep(delaySeconds * 1000);
|
||||
log.info("开始执行延迟启动任务...");
|
||||
task.run();
|
||||
log.info("延迟启动任务执行完成");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("延迟启动任务被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("延迟启动任务执行失败", e);
|
||||
}
|
||||
}, executorService);
|
||||
}
|
||||
/**
|
||||
* 延迟执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param delaySeconds 延迟秒数
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeWithDelay(Runnable task, long delaySeconds, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
try {
|
||||
log.info("延迟启动任务,等待{}秒后执行...", delaySeconds);
|
||||
Thread.sleep(delaySeconds * 1000);
|
||||
log.info("开始执行延迟启动任务...");
|
||||
task.run();
|
||||
log.info("延迟启动任务执行完成");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("延迟启动任务被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("延迟启动任务执行失败", e);
|
||||
}
|
||||
},
|
||||
executorService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟执行任务(使用默认延迟时间)
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeWithDelay(Runnable task, ExecutorService executorService) {
|
||||
executeWithDelay(task, 5, executorService); // 默认延迟5秒
|
||||
}
|
||||
/**
|
||||
* 延迟执行任务(使用默认延迟时间)
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeWithDelay(Runnable task, ExecutorService executorService) {
|
||||
executeWithDelay(task, 5, executorService); // 默认延迟5秒
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待特定Bean初始化完成后再执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param beanName Bean名称
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterBeanReady(Runnable task, String beanName, int maxWaitSeconds, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
log.info("等待Bean {} 初始化完成...", beanName);
|
||||
|
||||
// 等待Bean初始化完成
|
||||
for (int i = 0; i < maxWaitSeconds * 10; i++) { // 每100ms检查一次
|
||||
try {
|
||||
if (applicationContext.containsBean(beanName)) {
|
||||
Object bean = applicationContext.getBean(beanName);
|
||||
if (bean != null) {
|
||||
log.info("Bean {} 已初始化完成,开始执行任务", beanName);
|
||||
task.run();
|
||||
log.info("任务执行完成");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Bean还未初始化完成,继续等待
|
||||
}
|
||||
Thread.sleep(100);
|
||||
/**
|
||||
* 等待特定Bean初始化完成后再执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param beanName Bean名称
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterBeanReady(
|
||||
Runnable task, String beanName, int maxWaitSeconds, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
try {
|
||||
log.info("等待Bean {} 初始化完成...", beanName);
|
||||
|
||||
// 等待Bean初始化完成
|
||||
for (int i = 0; i < maxWaitSeconds * 10; i++) { // 每100ms检查一次
|
||||
try {
|
||||
if (applicationContext.containsBean(beanName)) {
|
||||
Object bean = applicationContext.getBean(beanName);
|
||||
if (bean != null) {
|
||||
log.info("Bean {} 已初始化完成,开始执行任务", beanName);
|
||||
task.run();
|
||||
log.info("任务执行完成");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("等待Bean {} 超时,强制执行任务", beanName);
|
||||
task.run();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Bean初始化被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Bean初始化后执行任务失败", e);
|
||||
} catch (Exception e) {
|
||||
// Bean还未初始化完成,继续等待
|
||||
}
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}, executorService);
|
||||
}
|
||||
|
||||
log.warn("等待Bean {} 超时,强制执行任务", beanName);
|
||||
task.run();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Bean初始化被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Bean初始化后执行任务失败", e);
|
||||
}
|
||||
},
|
||||
executorService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待Web容器完全启动后再执行任务
|
||||
@@ -117,122 +120,127 @@ public class DelayedStartupUtil {
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterWebReady(Runnable task, ExecutorService executorService) {
|
||||
executeWithDelay(() -> {
|
||||
try {
|
||||
// 检查关键Web组件是否已启动
|
||||
String[] webBeans = {
|
||||
"dispatcherServlet",
|
||||
"requestMappingHandlerMapping",
|
||||
"requestMappingHandlerAdapter",
|
||||
"webMvcConfigurer"
|
||||
};
|
||||
|
||||
boolean allReady = true;
|
||||
for (String beanName : webBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
allReady = false;
|
||||
log.debug("Web组件 {} 尚未就绪", beanName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 额外检查:确保所有Controller都已注册
|
||||
try {
|
||||
Object handlerMapping = applicationContext.getBean("requestMappingHandlerMapping");
|
||||
if (handlerMapping != null) {
|
||||
// 通过反射检查HandlerMapping是否已完全初始化
|
||||
java.lang.reflect.Method getHandlerMethods = handlerMapping.getClass().getMethod("getHandlerMethods");
|
||||
Object handlerMethods = getHandlerMethods.invoke(handlerMapping);
|
||||
if (handlerMethods != null) {
|
||||
log.info("Web容器已完全启动,所有Controller已注册,开始执行任务");
|
||||
task.run();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("检查HandlerMapping状态时出错: {}", e.getMessage());
|
||||
}
|
||||
|
||||
if (allReady) {
|
||||
log.info("Web容器已完全启动,开始执行任务");
|
||||
task.run();
|
||||
} else {
|
||||
log.warn("Web容器未完全启动,但继续执行任务");
|
||||
task.run();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("检查Web容器状态失败,直接执行任务", e);
|
||||
task.run();
|
||||
}
|
||||
}, 5, executorService); // 增加延迟时间到5秒
|
||||
}
|
||||
executeWithDelay(
|
||||
() -> {
|
||||
try {
|
||||
// 检查关键Web组件是否已启动
|
||||
String[] webBeans = {
|
||||
"dispatcherServlet",
|
||||
"requestMappingHandlerMapping",
|
||||
"requestMappingHandlerAdapter",
|
||||
"webMvcConfigurer"
|
||||
};
|
||||
|
||||
/**
|
||||
* 等待Web接口完全就绪(包括所有Controller和路由注册完成)
|
||||
* 特别适用于需要Webhook回调的场景
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(Runnable task, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
log.info("等待Web接口完全就绪...");
|
||||
|
||||
// 等待Web容器启动
|
||||
Thread.sleep(3000);
|
||||
|
||||
// 检查关键Web组件
|
||||
String[] criticalBeans = {
|
||||
"dispatcherServlet",
|
||||
"requestMappingHandlerMapping",
|
||||
"requestMappingHandlerAdapter"
|
||||
};
|
||||
|
||||
for (int attempt = 0; attempt < 10; attempt++) {
|
||||
boolean allReady = true;
|
||||
for (String beanName : criticalBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
allReady = false;
|
||||
break;
|
||||
boolean allReady = true;
|
||||
for (String beanName : webBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
allReady = false;
|
||||
log.debug("Web组件 {} 尚未就绪", beanName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allReady) {
|
||||
// 额外等待,确保所有Controller都已注册
|
||||
Thread.sleep(2000);
|
||||
|
||||
// 检查是否有注册的Handler
|
||||
|
||||
// 额外检查:确保所有Controller都已注册
|
||||
try {
|
||||
Object handlerMapping = applicationContext.getBean("requestMappingHandlerMapping");
|
||||
if (handlerMapping != null) {
|
||||
java.lang.reflect.Method getHandlerMethods = handlerMapping.getClass().getMethod("getHandlerMethods");
|
||||
// 通过反射检查HandlerMapping是否已完全初始化
|
||||
java.lang.reflect.Method getHandlerMethods =
|
||||
handlerMapping.getClass().getMethod("getHandlerMethods");
|
||||
Object handlerMethods = getHandlerMethods.invoke(handlerMapping);
|
||||
if (handlerMethods != null) {
|
||||
log.info("Web接口已完全就绪,所有Controller已注册,开始执行任务");
|
||||
log.info("Web容器已完全启动,所有Controller已注册,开始执行任务");
|
||||
task.run();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("检查HandlerMapping时出错: {}", e.getMessage());
|
||||
log.debug("检查HandlerMapping状态时出错: {}", e.getMessage());
|
||||
}
|
||||
|
||||
if (allReady) {
|
||||
log.info("Web容器已完全启动,开始执行任务");
|
||||
task.run();
|
||||
} else {
|
||||
log.warn("Web容器未完全启动,但继续执行任务");
|
||||
task.run();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("检查Web容器状态失败,直接执行任务", e);
|
||||
task.run();
|
||||
}
|
||||
|
||||
log.debug("Web接口尚未完全就绪,等待中... (第{}次检查)", attempt + 1);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
log.warn("等待Web接口就绪超时,强制执行任务");
|
||||
task.run();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Web接口就绪失败", e);
|
||||
task.run();
|
||||
}
|
||||
}, executorService);
|
||||
},
|
||||
5,
|
||||
executorService); // 增加延迟时间到5秒
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待Web接口完全就绪(包括所有Controller和路由注册完成) 特别适用于需要Webhook回调的场景
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(Runnable task, ExecutorService executorService) {
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
try {
|
||||
log.info("等待Web接口完全就绪...");
|
||||
|
||||
// 等待Web容器启动
|
||||
Thread.sleep(3000);
|
||||
|
||||
// 检查关键Web组件
|
||||
String[] criticalBeans = {
|
||||
"dispatcherServlet", "requestMappingHandlerMapping", "requestMappingHandlerAdapter"
|
||||
};
|
||||
|
||||
for (int attempt = 0; attempt < 10; attempt++) {
|
||||
boolean allReady = true;
|
||||
for (String beanName : criticalBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
allReady = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allReady) {
|
||||
// 额外等待,确保所有Controller都已注册
|
||||
Thread.sleep(2000);
|
||||
|
||||
// 检查是否有注册的Handler
|
||||
try {
|
||||
Object handlerMapping =
|
||||
applicationContext.getBean("requestMappingHandlerMapping");
|
||||
if (handlerMapping != null) {
|
||||
java.lang.reflect.Method getHandlerMethods =
|
||||
handlerMapping.getClass().getMethod("getHandlerMethods");
|
||||
Object handlerMethods = getHandlerMethods.invoke(handlerMapping);
|
||||
if (handlerMethods != null) {
|
||||
log.info("Web接口已完全就绪,所有Controller已注册,开始执行任务");
|
||||
task.run();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("检查HandlerMapping时出错: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Web接口尚未完全就绪,等待中... (第{}次检查)", attempt + 1);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
log.warn("等待Web接口就绪超时,强制执行任务");
|
||||
task.run();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Web接口就绪失败", e);
|
||||
task.run();
|
||||
}
|
||||
},
|
||||
executorService);
|
||||
}
|
||||
}
|
||||
|
||||
+139
-138
@@ -20,8 +20,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
/**
|
||||
* Web接口就绪检查器
|
||||
* 专门用于检查Web接口是否完全就绪,特别适用于Webhook场景
|
||||
* Web接口就绪检查器 专门用于检查Web接口是否完全就绪,特别适用于Webhook场景
|
||||
*
|
||||
* @author gitee.com/NexIoT
|
||||
* @version 1.0
|
||||
@@ -31,148 +30,150 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
@Component
|
||||
public class WebInterfaceReadyChecker {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
@Autowired private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 检查Web接口是否完全就绪
|
||||
*
|
||||
* @return true如果Web接口已就绪,false否则
|
||||
*/
|
||||
public boolean isWebInterfaceReady() {
|
||||
try {
|
||||
// 1. 检查关键Web组件
|
||||
String[] criticalBeans = {
|
||||
"dispatcherServlet",
|
||||
"requestMappingHandlerMapping",
|
||||
"requestMappingHandlerAdapter"
|
||||
};
|
||||
|
||||
for (String beanName : criticalBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
log.debug("Web组件 {} 尚未就绪", beanName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查RequestMappingHandlerMapping是否已完全初始化
|
||||
RequestMappingHandlerMapping handlerMapping =
|
||||
applicationContext.getBean(RequestMappingHandlerMapping.class);
|
||||
|
||||
if (handlerMapping == null) {
|
||||
log.debug("RequestMappingHandlerMapping 尚未就绪");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 检查是否有注册的Handler
|
||||
try {
|
||||
var handlerMethods = handlerMapping.getHandlerMethods();
|
||||
if (handlerMethods == null || handlerMethods.isEmpty()) {
|
||||
log.debug("尚未有Handler注册");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 检查是否有Webhook相关的Controller
|
||||
boolean hasWebhookController = handlerMethods.entrySet().stream()
|
||||
.anyMatch(entry -> {
|
||||
String path = entry.getKey().toString();
|
||||
return path.contains("webhook") ||
|
||||
path.contains("callback") ||
|
||||
path.contains("hook");
|
||||
});
|
||||
|
||||
if (hasWebhookController) {
|
||||
log.info("检测到Webhook相关Controller已注册");
|
||||
} else {
|
||||
log.debug("未检测到Webhook相关Controller");
|
||||
}
|
||||
|
||||
log.info("Web接口已完全就绪,共注册 {} 个Handler", handlerMethods.size());
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug("检查HandlerMapping时出错: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug("检查Web接口状态时出错: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 检查Web接口是否完全就绪
|
||||
*
|
||||
* @return true如果Web接口已就绪,false否则
|
||||
*/
|
||||
public boolean isWebInterfaceReady() {
|
||||
try {
|
||||
// 1. 检查关键Web组件
|
||||
String[] criticalBeans = {
|
||||
"dispatcherServlet", "requestMappingHandlerMapping", "requestMappingHandlerAdapter"
|
||||
};
|
||||
|
||||
/**
|
||||
* 等待Web接口就绪
|
||||
*
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
* @param checkIntervalMs 检查间隔毫秒数
|
||||
* @return true如果Web接口在指定时间内就绪,false否则
|
||||
*/
|
||||
public boolean waitForWebInterfaceReady(int maxWaitSeconds, long checkIntervalMs) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
long maxWaitMs = maxWaitSeconds * 1000L;
|
||||
|
||||
while (System.currentTimeMillis() - startTime < maxWaitMs) {
|
||||
if (isWebInterfaceReady()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(checkIntervalMs);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断");
|
||||
return false;
|
||||
}
|
||||
for (String beanName : criticalBeans) {
|
||||
if (!applicationContext.containsBean(beanName)) {
|
||||
log.debug("Web组件 {} 尚未就绪", beanName);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.warn("等待Web接口就绪超时 ({}秒)", maxWaitSeconds);
|
||||
}
|
||||
|
||||
// 2. 检查RequestMappingHandlerMapping是否已完全初始化
|
||||
RequestMappingHandlerMapping handlerMapping =
|
||||
applicationContext.getBean(RequestMappingHandlerMapping.class);
|
||||
|
||||
if (handlerMapping == null) {
|
||||
log.debug("RequestMappingHandlerMapping 尚未就绪");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 检查是否有注册的Handler
|
||||
try {
|
||||
var handlerMethods = handlerMapping.getHandlerMethods();
|
||||
if (handlerMethods == null || handlerMethods.isEmpty()) {
|
||||
log.debug("尚未有Handler注册");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 检查是否有Webhook相关的Controller
|
||||
boolean hasWebhookController =
|
||||
handlerMethods.entrySet().stream()
|
||||
.anyMatch(
|
||||
entry -> {
|
||||
String path = entry.getKey().toString();
|
||||
return path.contains("webhook")
|
||||
|| path.contains("callback")
|
||||
|| path.contains("hook");
|
||||
});
|
||||
|
||||
if (hasWebhookController) {
|
||||
log.info("检测到Webhook相关Controller已注册");
|
||||
} else {
|
||||
log.debug("未检测到Webhook相关Controller");
|
||||
}
|
||||
|
||||
log.info("Web接口已完全就绪,共注册 {} 个Handler", handlerMethods.size());
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug("检查HandlerMapping时出错: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug("检查Web接口状态时出错: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待Web接口就绪
|
||||
*
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
* @param checkIntervalMs 检查间隔毫秒数
|
||||
* @return true如果Web接口在指定时间内就绪,false否则
|
||||
*/
|
||||
public boolean waitForWebInterfaceReady(int maxWaitSeconds, long checkIntervalMs) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
long maxWaitMs = maxWaitSeconds * 1000L;
|
||||
|
||||
while (System.currentTimeMillis() - startTime < maxWaitMs) {
|
||||
if (isWebInterfaceReady()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(checkIntervalMs);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步等待Web接口就绪后执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(Runnable task, ExecutorService executorService, int maxWaitSeconds) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
log.info("等待Web接口完全就绪...");
|
||||
|
||||
// 先等待一段时间让Web容器启动
|
||||
Thread.sleep(2000);
|
||||
|
||||
// 等待Web接口就绪
|
||||
boolean ready = waitForWebInterfaceReady(maxWaitSeconds, 1000);
|
||||
|
||||
if (ready) {
|
||||
log.info("Web接口已完全就绪,开始执行任务");
|
||||
task.run();
|
||||
} else {
|
||||
log.warn("Web接口就绪超时,强制执行任务");
|
||||
task.run();
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Web接口就绪失败", e);
|
||||
task.run();
|
||||
log.warn("等待Web接口就绪超时 ({}秒)", maxWaitSeconds);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步等待Web接口就绪后执行任务
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
* @param maxWaitSeconds 最大等待秒数
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(
|
||||
Runnable task, ExecutorService executorService, int maxWaitSeconds) {
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
try {
|
||||
log.info("等待Web接口完全就绪...");
|
||||
|
||||
// 先等待一段时间让Web容器启动
|
||||
Thread.sleep(2000);
|
||||
|
||||
// 等待Web接口就绪
|
||||
boolean ready = waitForWebInterfaceReady(maxWaitSeconds, 1000);
|
||||
|
||||
if (ready) {
|
||||
log.info("Web接口已完全就绪,开始执行任务");
|
||||
task.run();
|
||||
} else {
|
||||
log.warn("Web接口就绪超时,强制执行任务");
|
||||
task.run();
|
||||
}
|
||||
}, executorService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步等待Web接口就绪后执行任务(使用默认参数)
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(Runnable task, ExecutorService executorService) {
|
||||
executeAfterWebInterfaceReady(task, executorService, 15); // 默认等待15秒
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("等待Web接口就绪被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("等待Web接口就绪失败", e);
|
||||
task.run();
|
||||
}
|
||||
},
|
||||
executorService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步等待Web接口就绪后执行任务(使用默认参数)
|
||||
*
|
||||
* @param task 要执行的任务
|
||||
* @param executorService 执行器
|
||||
*/
|
||||
public void executeAfterWebInterfaceReady(Runnable task, ExecutorService executorService) {
|
||||
executeAfterWebInterfaceReady(task, executorService, 15); // 默认等待15秒
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-config</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-config</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,85 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-core</artifactId>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>cn-universal-core</artifactId>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-framework</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</dependency>
|
||||
<!-- GraalJS - 替代 Nashorn -->
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js</artifactId>
|
||||
<version>23.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js-scriptengine</artifactId>
|
||||
<version>23.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</dependency>
|
||||
<!-- GraalJS - 替代 Nashorn -->
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js</artifactId>
|
||||
<version>23.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js-scriptengine</artifactId>
|
||||
<version>23.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security Crypto - 提供 Base64Utils -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Security Crypto - 提供 Base64Utils -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!-- 日志 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+1
-2
@@ -20,8 +20,7 @@ import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 下行处理上下文
|
||||
* 贯穿整个下行处理流程,用于拦截器间传递数据
|
||||
* 下行处理上下文 贯穿整个下行处理流程,用于拦截器间传递数据
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/10/24
|
||||
|
||||
+8
-19
@@ -13,8 +13,7 @@
|
||||
package cn.universal.core.downlink;
|
||||
|
||||
/**
|
||||
* 下行拦截器接口
|
||||
* 所有下行拦截器都需要实现此接口
|
||||
* 下行拦截器接口 所有下行拦截器都需要实现此接口
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/10/24
|
||||
@@ -29,19 +28,15 @@ public interface DownlinkInterceptor {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* 执行顺序(数字越小越先执行)
|
||||
* 建议:
|
||||
* - 0-100: 系统级拦截器(鉴权、限流等)
|
||||
* - 100-500: 业务级拦截器(数据处理、编解码等)
|
||||
* - 500-1000: 辅助级拦截器(日志、监控等)
|
||||
* 执行顺序(数字越小越先执行) 建议: - 0-100: 系统级拦截器(鉴权、限流等) - 100-500: 业务级拦截器(数据处理、编解码等) - 500-1000:
|
||||
* 辅助级拦截器(日志、监控等)
|
||||
*
|
||||
* @return 执行顺序
|
||||
*/
|
||||
int getOrder();
|
||||
|
||||
/**
|
||||
* 是否启用此拦截器
|
||||
* 可通过配置动态控制拦截器的启用状态
|
||||
* 是否启用此拦截器 可通过配置动态控制拦截器的启用状态
|
||||
*
|
||||
* @return true-启用,false-禁用
|
||||
*/
|
||||
@@ -57,8 +52,7 @@ public interface DownlinkInterceptor {
|
||||
InterceptorPhase getPhase();
|
||||
|
||||
/**
|
||||
* 是否支持当前上下文
|
||||
* 可根据协议类型、产品类型等条件判断是否需要执行此拦截器
|
||||
* 是否支持当前上下文 可根据协议类型、产品类型等条件判断是否需要执行此拦截器
|
||||
*
|
||||
* @param context 下行上下文
|
||||
* @return true-支持,false-不支持
|
||||
@@ -68,8 +62,7 @@ public interface DownlinkInterceptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置处理(在主流程之前执行)
|
||||
* 此方法在拦截器链中按order顺序执行
|
||||
* 前置处理(在主流程之前执行) 此方法在拦截器链中按order顺序执行
|
||||
*
|
||||
* @param context 下行上下文
|
||||
* @return true-继续执行后续拦截器和主流程,false-中断执行
|
||||
@@ -78,9 +71,7 @@ public interface DownlinkInterceptor {
|
||||
boolean preHandle(DownlinkContext<?> context) throws Exception;
|
||||
|
||||
/**
|
||||
* 后置处理(在主流程之后执行)
|
||||
* 此方法在拦截器链中按order顺序执行
|
||||
* 注意:即使preHandle返回false,postHandle也不会被调用
|
||||
* 后置处理(在主流程之后执行) 此方法在拦截器链中按order顺序执行 注意:即使preHandle返回false,postHandle也不会被调用
|
||||
*
|
||||
* @param context 下行上下文
|
||||
* @throws Exception 处理异常
|
||||
@@ -88,9 +79,7 @@ public interface DownlinkInterceptor {
|
||||
void postHandle(DownlinkContext<?> context) throws Exception;
|
||||
|
||||
/**
|
||||
* 完成处理(无论成功失败都会执行)
|
||||
* 此方法在拦截器链中按order倒序执行(类似finally块)
|
||||
* 可用于资源清理、异常处理等
|
||||
* 完成处理(无论成功失败都会执行) 此方法在拦截器链中按order倒序执行(类似finally块) 可用于资源清理、异常处理等
|
||||
*
|
||||
* @param context 下行上下文
|
||||
* @param ex 执行过程中的异常(如果有)
|
||||
|
||||
+20
@@ -9,11 +9,13 @@
|
||||
### 步骤1:理解新架构
|
||||
|
||||
**改造前的流程**:
|
||||
|
||||
```
|
||||
IDown.doAction() → AbstractDownService.convert() → XxxDownProcessorChain.process()
|
||||
```
|
||||
|
||||
**改造后的流程**:
|
||||
|
||||
```
|
||||
IDown.doAction()
|
||||
→ createContext() // 创建上下文
|
||||
@@ -285,20 +287,24 @@ public class MQTTDownService extends AbstractDownService<MQTTDownRequest> implem
|
||||
## 🎯 改造要点总结
|
||||
|
||||
### ✅ 必须实现的方法
|
||||
|
||||
1. `createContext(Object msg)` - 创建下行上下文
|
||||
2. `doProcess(DownlinkContext<?> context)` - 核心处理逻辑
|
||||
3. `getInterceptorChain()` - 获取拦截器链
|
||||
|
||||
### ✅ 必须添加的依赖
|
||||
|
||||
```java
|
||||
@Resource private DownlinkInterceptorChain downlinkInterceptorChain;
|
||||
```
|
||||
|
||||
### ✅ 必须移除的方法
|
||||
|
||||
- `doAction(String msg)` - 使用接口默认实现
|
||||
- `doAction(JSONObject msg)` - 使用接口默认实现
|
||||
|
||||
### ✅ 保持不变的方法
|
||||
|
||||
- `code()` - 协议代码
|
||||
- `name()` - 协议名称
|
||||
- `convert()` - 消息转换逻辑
|
||||
@@ -306,11 +312,13 @@ public class MQTTDownService extends AbstractDownService<MQTTDownRequest> implem
|
||||
## 🔍 逐步迁移策略
|
||||
|
||||
### 方案1:一次性迁移(推荐用于新协议)
|
||||
|
||||
直接按照上述步骤改造,一次性启用拦截器模式。
|
||||
|
||||
### 方案2:渐进式迁移(推荐用于现有协议)
|
||||
|
||||
#### 阶段1:兼容模式(支持新旧两种方式)
|
||||
|
||||
```java
|
||||
@Override
|
||||
public R doAction(String msg) {
|
||||
@@ -334,6 +342,7 @@ private boolean enableInterceptor() {
|
||||
```
|
||||
|
||||
#### 阶段2:灰度发布
|
||||
|
||||
```yaml
|
||||
# application.yml
|
||||
downlink:
|
||||
@@ -345,11 +354,13 @@ downlink:
|
||||
```
|
||||
|
||||
#### 阶段3:全面启用
|
||||
|
||||
移除兼容代码,完全使用新模式。
|
||||
|
||||
## 🧪 测试清单
|
||||
|
||||
### 功能测试
|
||||
|
||||
- [ ] 下行消息能正常发送
|
||||
- [ ] 拦截器按顺序执行
|
||||
- [ ] 拦截器可以中断执行
|
||||
@@ -357,11 +368,13 @@ downlink:
|
||||
- [ ] 异常能正确处理
|
||||
|
||||
### 性能测试
|
||||
|
||||
- [ ] 拦截器对性能影响在可接受范围内(< 5%)
|
||||
- [ ] 并发场景下正常工作
|
||||
- [ ] 无内存泄漏
|
||||
|
||||
### 兼容性测试
|
||||
|
||||
- [ ] 现有业务功能不受影响
|
||||
- [ ] 各协议下行正常
|
||||
- [ ] 第三方平台下行正常
|
||||
@@ -369,6 +382,7 @@ downlink:
|
||||
## 📋 其他协议改造示例
|
||||
|
||||
### TCP 协议改造
|
||||
|
||||
```java
|
||||
@Service("tcpDownService")
|
||||
public class TcpDownService extends AbstractDownService<TcpDownRequest> implements IDown {
|
||||
@@ -392,6 +406,7 @@ public class TcpDownService extends AbstractDownService<TcpDownRequest> implemen
|
||||
```
|
||||
|
||||
### HTTP 协议改造
|
||||
|
||||
```java
|
||||
@Service("httpDownService")
|
||||
public class HttpDownService extends AbstractDownService<HttpDownRequest> implements IDown {
|
||||
@@ -417,18 +432,23 @@ public class HttpDownService extends AbstractDownService<HttpDownRequest> implem
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### Q1: 为什么不能直接在 IDown 接口中注入 DownlinkInterceptorChain?
|
||||
|
||||
A: 接口不能有成员变量,需要由实现类通过依赖注入提供。
|
||||
|
||||
### Q2: 拦截器链为 null 怎么办?
|
||||
|
||||
A: `doActionWithInterceptors()` 中已经做了 null 检查,即使拦截器链为 null,也能正常工作。
|
||||
|
||||
### Q3: 如何禁用某个拦截器?
|
||||
|
||||
A: 在拦截器中实现 `isEnabled()` 方法,返回 false。
|
||||
|
||||
### Q4: 拦截器执行顺序如何控制?
|
||||
|
||||
A: 通过 `getOrder()` 方法和 `@Order` 注解控制,数字越小越先执行。
|
||||
|
||||
### Q5: 如何只对特定协议应用拦截器?
|
||||
|
||||
A: 在拦截器的 `supports()` 方法中判断协议类型。
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
+3
-12
@@ -20,21 +20,12 @@ package cn.universal.core.downlink;
|
||||
*/
|
||||
public enum InterceptorPhase {
|
||||
|
||||
/**
|
||||
* 前置阶段:在消息转换(convert)之前执行
|
||||
* 适用场景:鉴权、限流、参数校验、日志记录等
|
||||
*/
|
||||
/** 前置阶段:在消息转换(convert)之前执行 适用场景:鉴权、限流、参数校验、日志记录等 */
|
||||
PRE,
|
||||
|
||||
/**
|
||||
* 中置阶段:在消息转换(convert)之后,具体处理器(processor)之前执行
|
||||
* 适用场景:数据增强、编解码、影子服务、业务规则检查等
|
||||
*/
|
||||
/** 中置阶段:在消息转换(convert)之后,具体处理器(processor)之前执行 适用场景:数据增强、编解码、影子服务、业务规则检查等 */
|
||||
MID,
|
||||
|
||||
/**
|
||||
* 后置阶段:在具体处理器(processor)执行之后
|
||||
* 适用场景:结果处理、通知推送、审计日志、清理资源等
|
||||
*/
|
||||
/** 后置阶段:在具体处理器(processor)执行之后 适用场景:结果处理、通知推送、审计日志、清理资源等 */
|
||||
POST
|
||||
}
|
||||
|
||||
+28
-14
@@ -125,34 +125,38 @@ public class CustomDownlinkInterceptor implements DownlinkInterceptor {
|
||||
|
||||
### 2. 在协议实现中使用
|
||||
|
||||
拦截器会自动应用到所有实现了 `IDown` 接口的协议服务中。无需额外配置,只要将拦截器注册为 Spring Bean 即可。
|
||||
拦截器会自动应用到所有实现了 `IDown` 接口的协议服务中。无需额外配置,只要将拦截器注册为 Spring Bean
|
||||
即可。
|
||||
|
||||
## 📋 拦截器执行阶段
|
||||
|
||||
### PRE 阶段(前置阶段)
|
||||
|
||||
- **执行时机**:在消息转换(convert)之前
|
||||
- **适用场景**:
|
||||
- ✅ 鉴权认证
|
||||
- ✅ 参数校验
|
||||
- ✅ 限流控制
|
||||
- ✅ 日志记录
|
||||
- ✅ 监控统计
|
||||
- ✅ 鉴权认证
|
||||
- ✅ 参数校验
|
||||
- ✅ 限流控制
|
||||
- ✅ 日志记录
|
||||
- ✅ 监控统计
|
||||
|
||||
### MID 阶段(中置阶段)
|
||||
|
||||
- **执行时机**:在消息转换(convert)之后,协议处理器(processor)之前
|
||||
- **适用场景**:
|
||||
- ✅ 数据增强
|
||||
- ✅ 编解码处理
|
||||
- ✅ 影子服务集成
|
||||
- ✅ 业务规则校验
|
||||
- ✅ 数据增强
|
||||
- ✅ 编解码处理
|
||||
- ✅ 影子服务集成
|
||||
- ✅ 业务规则校验
|
||||
|
||||
### POST 阶段(后置阶段)
|
||||
|
||||
- **执行时机**:在协议处理器(processor)执行之后
|
||||
- **适用场景**:
|
||||
- ✅ 结果处理
|
||||
- ✅ 通知推送
|
||||
- ✅ 审计日志
|
||||
- ✅ 数据持久化
|
||||
- ✅ 结果处理
|
||||
- ✅ 通知推送
|
||||
- ✅ 审计日志
|
||||
- ✅ 数据持久化
|
||||
|
||||
## 🔧 上下文对象
|
||||
|
||||
@@ -186,16 +190,19 @@ context.removeAttribute(key); // 移除属性
|
||||
## 🎯 内置拦截器
|
||||
|
||||
### 1. DownlinkLoggingInterceptor(日志拦截器)
|
||||
|
||||
- **Order**: 1000
|
||||
- **Phase**: PRE
|
||||
- **功能**: 记录下行消息的详细日志
|
||||
|
||||
### 2. DownlinkMetricsInterceptor(监控拦截器)
|
||||
|
||||
- **Order**: 10
|
||||
- **Phase**: PRE
|
||||
- **功能**: 收集性能指标(耗时、成功率等)
|
||||
|
||||
### 3. DownlinkValidationInterceptor(校验拦截器)
|
||||
|
||||
- **Order**: 50
|
||||
- **Phase**: PRE
|
||||
- **功能**: 校验必要参数
|
||||
@@ -203,6 +210,7 @@ context.removeAttribute(key); // 移除属性
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. 拦截器命名规范
|
||||
|
||||
```java
|
||||
// 建议使用:Downlink + 功能 + Interceptor
|
||||
DownlinkAuthenticationInterceptor // 鉴权拦截器
|
||||
@@ -211,6 +219,7 @@ DownlinkCodecInterceptor // 编解码拦截器
|
||||
```
|
||||
|
||||
### 2. Order 值建议
|
||||
|
||||
```
|
||||
0-100: 系统级拦截器(鉴权、限流、监控)
|
||||
100-500: 业务级拦截器(数据处理、编解码)
|
||||
@@ -218,6 +227,7 @@ DownlinkCodecInterceptor // 编解码拦截器
|
||||
```
|
||||
|
||||
### 3. 错误处理
|
||||
|
||||
```java
|
||||
@Override
|
||||
public boolean preHandle(DownlinkContext<?> context) throws Exception {
|
||||
@@ -233,6 +243,7 @@ public boolean preHandle(DownlinkContext<?> context) throws Exception {
|
||||
```
|
||||
|
||||
### 4. 使用上下文传递数据
|
||||
|
||||
```java
|
||||
// 在前置拦截器中存储数据
|
||||
@Override
|
||||
@@ -251,6 +262,7 @@ public void postHandle(DownlinkContext<?> context) {
|
||||
```
|
||||
|
||||
### 5. 条件执行
|
||||
|
||||
```java
|
||||
@Override
|
||||
public boolean supports(DownlinkContext<?> context) {
|
||||
@@ -369,6 +381,7 @@ public class DownlinkAuthenticationInterceptor implements DownlinkInterceptor {
|
||||
## 🐛 调试和测试
|
||||
|
||||
### 启用调试日志
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
@@ -376,6 +389,7 @@ logging:
|
||||
```
|
||||
|
||||
### 查看拦截器链信息
|
||||
|
||||
```java
|
||||
@Resource
|
||||
private DownlinkInterceptorChain interceptorChain;
|
||||
|
||||
+26
-21
@@ -17,18 +17,19 @@ import cn.universal.core.message.DownRequest;
|
||||
import cn.universal.core.message.UnifiedDownlinkCommand;
|
||||
|
||||
/**
|
||||
* 下行请求转换器接口
|
||||
* 负责将统一的UnifiedDownlinkCommand转换为协议特定的DownRequest对象
|
||||
* 下行请求转换器接口 负责将统一的UnifiedDownlinkCommand转换为协议特定的DownRequest对象
|
||||
*
|
||||
* <p>设计原则:
|
||||
*
|
||||
* <ul>
|
||||
* <li>单一职责:每个转换器只负责一种协议的转换</li>
|
||||
* <li>无状态:转换器应该是无状态的,可以被多次复用</li>
|
||||
* <li>向后兼容:转换器应该能处理旧格式的数据</li>
|
||||
* <li>异常处理:转换失败应该抛出明确的异常</li>
|
||||
* <li>单一职责:每个转换器只负责一种协议的转换
|
||||
* <li>无状态:转换器应该是无状态的,可以被多次复用
|
||||
* <li>向后兼容:转换器应该能处理旧格式的数据
|
||||
* <li>异常处理:转换失败应该抛出明确的异常
|
||||
* </ul>
|
||||
*
|
||||
* <p>实现示例:
|
||||
*
|
||||
* <pre>{@code
|
||||
* @Component("mqttConverter")
|
||||
* public class MQTTDownRequestConverter implements DownRequestConverter<MQTTDownRequest> {
|
||||
@@ -47,8 +48,8 @@ import cn.universal.core.message.UnifiedDownlinkCommand;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>注意:泛型参数T使用{@link DownRequest}作为上界,以保持核心模块的独立性。
|
||||
* 实际使用时,建议使用{@link cn.universal.persistence.base.BaseDownRequest}的子类。
|
||||
* <p>注意:泛型参数T使用{@link DownRequest}作为上界,以保持核心模块的独立性。 实际使用时,建议使用{@link
|
||||
* cn.universal.persistence.base.BaseDownRequest}的子类。
|
||||
*
|
||||
* @param <T> 协议特定的DownRequest类型(通常是BaseDownRequest的子类)
|
||||
* @version 1.0
|
||||
@@ -60,12 +61,13 @@ public interface DownRequestConverter<T extends DownRequest> {
|
||||
* 转换统一命令为协议特定请求
|
||||
*
|
||||
* <p>转换过程应该包括:
|
||||
*
|
||||
* <ol>
|
||||
* <li>复制通用字段(productKey、deviceId等)</li>
|
||||
* <li>设置协议特定字段</li>
|
||||
* <li>执行编解码(如果需要)</li>
|
||||
* <li>加载产品配置</li>
|
||||
* <li>加载设备信息</li>
|
||||
* <li>复制通用字段(productKey、deviceId等)
|
||||
* <li>设置协议特定字段
|
||||
* <li>执行编解码(如果需要)
|
||||
* <li>加载产品配置
|
||||
* <li>加载设备信息
|
||||
* </ol>
|
||||
*
|
||||
* @param command 统一下行命令对象
|
||||
@@ -89,6 +91,7 @@ public interface DownRequestConverter<T extends DownRequest> {
|
||||
* 获取转换器优先级
|
||||
*
|
||||
* <p>当多个转换器支持同一协议时,优先级高的会被优先使用
|
||||
*
|
||||
* <p>数字越小优先级越高,默认为100
|
||||
*
|
||||
* @return 优先级(0-最高,Integer.MAX_VALUE-最低)
|
||||
@@ -101,6 +104,7 @@ public interface DownRequestConverter<T extends DownRequest> {
|
||||
* 检查是否支持指定的命令
|
||||
*
|
||||
* <p>默认实现:支持所有命令
|
||||
*
|
||||
* <p>子类可以重写此方法以实现更细粒度的控制
|
||||
*
|
||||
* @param command 统一下行命令
|
||||
@@ -114,10 +118,11 @@ public interface DownRequestConverter<T extends DownRequest> {
|
||||
* 转换前的预处理钩子
|
||||
*
|
||||
* <p>在执行convert之前调用,可用于:
|
||||
*
|
||||
* <ul>
|
||||
* <li>参数验证</li>
|
||||
* <li>参数标准化</li>
|
||||
* <li>日志记录</li>
|
||||
* <li>参数验证
|
||||
* <li>参数标准化
|
||||
* <li>日志记录
|
||||
* </ul>
|
||||
*
|
||||
* @param command 统一下行命令
|
||||
@@ -131,18 +136,18 @@ public interface DownRequestConverter<T extends DownRequest> {
|
||||
* 转换后的后处理钩子
|
||||
*
|
||||
* <p>在执行convert之后调用,可用于:
|
||||
*
|
||||
* <ul>
|
||||
* <li>结果验证</li>
|
||||
* <li>数据增强</li>
|
||||
* <li>日志记录</li>
|
||||
* <li>结果验证
|
||||
* <li>数据增强
|
||||
* <li>日志记录
|
||||
* </ul>
|
||||
*
|
||||
* @param command 统一下行命令
|
||||
* @param request 转换后的请求对象
|
||||
* @param context 下行上下文
|
||||
*/
|
||||
default void postConvert(
|
||||
UnifiedDownlinkCommand command, T request, DownlinkContext<?> context) {
|
||||
default void postConvert(UnifiedDownlinkCommand command, T request, DownlinkContext<?> context) {
|
||||
// 默认空实现
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -30,19 +30,19 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 命令转换拦截器
|
||||
* 负责将UnifiedDownlinkCommand转换为协议特定的DownRequest对象
|
||||
* 命令转换拦截器 负责将UnifiedDownlinkCommand转换为协议特定的DownRequest对象
|
||||
*
|
||||
* <p>执行时机:MID阶段(在参数验证之后,业务处理之前)
|
||||
*
|
||||
* <p>转换流程:
|
||||
*
|
||||
* <ol>
|
||||
* <li>检查上下文中是否存在UnifiedDownlinkCommand</li>
|
||||
* <li>根据协议代码查找对应的转换器</li>
|
||||
* <li>执行转换前的预处理(preConvert)</li>
|
||||
* <li>执行转换(convert)</li>
|
||||
* <li>执行转换后的后处理(postConvert)</li>
|
||||
* <li>将转换结果设置到上下文中</li>
|
||||
* <li>检查上下文中是否存在UnifiedDownlinkCommand
|
||||
* <li>根据协议代码查找对应的转换器
|
||||
* <li>执行转换前的预处理(preConvert)
|
||||
* <li>执行转换(convert)
|
||||
* <li>执行转换后的后处理(postConvert)
|
||||
* <li>将转换结果设置到上下文中
|
||||
* </ol>
|
||||
*
|
||||
* @version 1.0
|
||||
@@ -80,10 +80,7 @@ public class CommandConversionInterceptor implements DownlinkInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
log.info(
|
||||
"命令转换拦截器初始化完成,加载转换器数量: {},支持协议: {}",
|
||||
converters.size(),
|
||||
converterCache.keySet());
|
||||
log.info("命令转换拦截器初始化完成,加载转换器数量: {},支持协议: {}", converters.size(), converterCache.keySet());
|
||||
} else {
|
||||
log.warn("未找到任何DownRequestConverter实现,命令转换功能将不可用");
|
||||
}
|
||||
@@ -142,7 +139,7 @@ public class CommandConversionInterceptor implements DownlinkInterceptor {
|
||||
|
||||
// 6. 执行转换(使用类型安全的方式)
|
||||
@SuppressWarnings("unchecked")
|
||||
DownRequestConverter<DownRequest> typedConverter =
|
||||
DownRequestConverter<DownRequest> typedConverter =
|
||||
(DownRequestConverter<DownRequest>) converter;
|
||||
DownRequest downRequest = typedConverter.convert(command, context);
|
||||
if (downRequest == null) {
|
||||
|
||||
+2
-4
@@ -20,8 +20,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 下行日志拦截器
|
||||
* 记录下行消息的详细日志信息
|
||||
* 下行日志拦截器 记录下行消息的详细日志信息
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/10/24
|
||||
@@ -68,8 +67,7 @@ public class DownlinkLoggingInterceptor implements DownlinkInterceptor {
|
||||
boolean success = context.getResult() != null && context.getResult().isSuccess();
|
||||
|
||||
log.info(
|
||||
"[下行日志] 下行消息处理完成: protocol={}, productKey={}, deviceId={}, "
|
||||
+ "success={}, duration={}ms",
|
||||
"[下行日志] 下行消息处理完成: protocol={}, productKey={}, deviceId={}, " + "success={}, duration={}ms",
|
||||
context.getProtocolCode(),
|
||||
context.getProductKey(),
|
||||
context.getDeviceId(),
|
||||
|
||||
+34
-31
@@ -20,16 +20,16 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 下行子设备拦截器
|
||||
* 用于处理网关下的子设备相关逻辑
|
||||
* 下行子设备拦截器 用于处理网关下的子设备相关逻辑
|
||||
*
|
||||
* <p>使用场景:
|
||||
*
|
||||
* <ul>
|
||||
* <li>子设备路由:判断消息是否需要转发到子设备</li>
|
||||
* <li>子设备鉴权:验证子设备是否有权接收指令</li>
|
||||
* <li>子设备映射:将网关设备ID映射到子设备ID</li>
|
||||
* <li>子设备状态检查:检查子设备是否在线</li>
|
||||
* <li>子设备协议转换:处理网关到子设备的协议差异</li>
|
||||
* <li>子设备路由:判断消息是否需要转发到子设备
|
||||
* <li>子设备鉴权:验证子设备是否有权接收指令
|
||||
* <li>子设备映射:将网关设备ID映射到子设备ID
|
||||
* <li>子设备状态检查:检查子设备是否在线
|
||||
* <li>子设备协议转换:处理网关到子设备的协议差异
|
||||
* </ul>
|
||||
*
|
||||
* @version 1.0
|
||||
@@ -43,7 +43,7 @@ public class DownlinkSubDeviceInterceptor implements DownlinkInterceptor {
|
||||
// TODO: 注入需要的服务
|
||||
// @Resource
|
||||
// private SubDeviceService subDeviceService;
|
||||
|
||||
|
||||
// @Resource
|
||||
// private GatewayService gatewayService;
|
||||
|
||||
@@ -70,37 +70,38 @@ public class DownlinkSubDeviceInterceptor implements DownlinkInterceptor {
|
||||
// 1. 检查上下文中是否有子设备标识
|
||||
// 2. 检查产品类型是否支持子设备
|
||||
// 3. 检查是否有网关设备信息
|
||||
|
||||
|
||||
// 示例代码:
|
||||
// String productKey = context.getProductKey();
|
||||
// Boolean isSubDevice = context.getAttribute("isSubDevice");
|
||||
// return Boolean.TRUE.equals(isSubDevice);
|
||||
|
||||
|
||||
// 默认返回 false,由你根据业务逻辑实现
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(DownlinkContext<?> context) throws Exception {
|
||||
log.debug("[子设备拦截器][PreHandle] 开始处理: productKey={}, deviceId={}",
|
||||
context.getProductKey(),
|
||||
log.debug(
|
||||
"[子设备拦截器][PreHandle] 开始处理: productKey={}, deviceId={}",
|
||||
context.getProductKey(),
|
||||
context.getDeviceId());
|
||||
|
||||
// TODO: 实现前置处理逻辑
|
||||
// 示例场景:
|
||||
|
||||
|
||||
// 1. 子设备路由判断
|
||||
// if (isSubDeviceMessage(context)) {
|
||||
// context.setAttribute("routeType", "subdevice");
|
||||
// log.info("[子设备拦截器] 检测到子设备消息");
|
||||
// }
|
||||
|
||||
|
||||
// 2. 子设备鉴权
|
||||
// if (!checkSubDevicePermission(context)) {
|
||||
// log.warn("[子设备拦截器] 子设备鉴权失败");
|
||||
// return false; // 中断执行
|
||||
// }
|
||||
|
||||
|
||||
// 3. 子设备状态检查
|
||||
// if (!isSubDeviceOnline(context)) {
|
||||
// log.warn("[子设备拦截器] 子设备离线");
|
||||
@@ -108,14 +109,14 @@ public class DownlinkSubDeviceInterceptor implements DownlinkInterceptor {
|
||||
// // 可以选择是否中断
|
||||
// // return false;
|
||||
// }
|
||||
|
||||
|
||||
// 4. 子设备ID映射
|
||||
// String subDeviceId = mapToSubDeviceId(context);
|
||||
// if (subDeviceId != null) {
|
||||
// context.setAttribute("subDeviceId", subDeviceId);
|
||||
// log.debug("[子设备拦截器] 映射到子设备ID: {}", subDeviceId);
|
||||
// }
|
||||
|
||||
|
||||
// 5. 网关信息填充
|
||||
// fillGatewayInfo(context);
|
||||
|
||||
@@ -124,24 +125,25 @@ public class DownlinkSubDeviceInterceptor implements DownlinkInterceptor {
|
||||
|
||||
@Override
|
||||
public void postHandle(DownlinkContext<?> context) throws Exception {
|
||||
log.debug("[子设备拦截器][PostHandle] 处理完成: productKey={}, deviceId={}",
|
||||
context.getProductKey(),
|
||||
log.debug(
|
||||
"[子设备拦截器][PostHandle] 处理完成: productKey={}, deviceId={}",
|
||||
context.getProductKey(),
|
||||
context.getDeviceId());
|
||||
|
||||
// TODO: 实现后置处理逻辑
|
||||
// 示例场景:
|
||||
|
||||
|
||||
// 1. 记录子设备下行日志
|
||||
// if (Boolean.TRUE.equals(context.getAttribute("isSubDevice"))) {
|
||||
// logSubDeviceDownlink(context);
|
||||
// }
|
||||
|
||||
|
||||
// 2. 更新子设备状态
|
||||
// updateSubDeviceStatus(context);
|
||||
|
||||
|
||||
// 3. 统计子设备指令数量
|
||||
// incrementSubDeviceCommandCount(context);
|
||||
|
||||
|
||||
// 4. 清理临时数据
|
||||
// cleanupTempData(context);
|
||||
}
|
||||
@@ -150,24 +152,25 @@ public class DownlinkSubDeviceInterceptor implements DownlinkInterceptor {
|
||||
public void afterCompletion(DownlinkContext<?> context, Exception ex) {
|
||||
// TODO: 实现完成处理逻辑(无论成功失败都会执行)
|
||||
// 示例场景:
|
||||
|
||||
|
||||
// 1. 异常处理
|
||||
if (ex != null) {
|
||||
log.error("[子设备拦截器][AfterCompletion] 处理异常: productKey={}, deviceId={}, error={}",
|
||||
context.getProductKey(),
|
||||
context.getDeviceId(),
|
||||
log.error(
|
||||
"[子设备拦截器][AfterCompletion] 处理异常: productKey={}, deviceId={}, error={}",
|
||||
context.getProductKey(),
|
||||
context.getDeviceId(),
|
||||
ex.getMessage());
|
||||
|
||||
|
||||
// TODO: 记录子设备错误
|
||||
// recordSubDeviceError(context, ex);
|
||||
}
|
||||
|
||||
|
||||
// 2. 资源清理
|
||||
// cleanupResources(context);
|
||||
|
||||
|
||||
// 3. 释放锁(如果有)
|
||||
// releaseLock(context);
|
||||
|
||||
|
||||
// 4. 记录处理耗时
|
||||
long duration = context.getDuration();
|
||||
if (duration > 1000) {
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 下行数据校验拦截器
|
||||
* 校验下行消息的必要参数
|
||||
* 下行数据校验拦截器 校验下行消息的必要参数
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/10/24
|
||||
|
||||
+25
-23
@@ -2,35 +2,37 @@
|
||||
|
||||
## 📖 概述
|
||||
|
||||
`DownlinkSubDeviceInterceptor` 是专门用于处理网关下子设备下行消息的拦截器。它在消息转换之后、业务处理之前执行(MID 阶段),order 值为 400。
|
||||
`DownlinkSubDeviceInterceptor` 是专门用于处理网关下子设备下行消息的拦截器。它在消息转换之后、业务处理之前执行(MID
|
||||
阶段),order 值为 400。
|
||||
|
||||
## 🎯 适用场景
|
||||
|
||||
### 典型使用场景
|
||||
|
||||
1. **子设备路由判断**
|
||||
- 判断消息是否需要转发到子设备
|
||||
- 区分网关直接处理还是转发给子设备
|
||||
- 判断消息是否需要转发到子设备
|
||||
- 区分网关直接处理还是转发给子设备
|
||||
|
||||
2. **子设备鉴权验证**
|
||||
- 验证子设备是否有权接收指令
|
||||
- 检查子设备是否绑定到对应网关
|
||||
- 验证子设备是否有权接收指令
|
||||
- 检查子设备是否绑定到对应网关
|
||||
|
||||
3. **子设备状态检查**
|
||||
- 检查子设备是否在线
|
||||
- 验证子设备健康状态
|
||||
- 检查子设备是否在线
|
||||
- 验证子设备健康状态
|
||||
|
||||
4. **子设备ID映射**
|
||||
- 将网关设备ID映射到子设备ID
|
||||
- 处理设备层级关系
|
||||
- 将网关设备ID映射到子设备ID
|
||||
- 处理设备层级关系
|
||||
|
||||
5. **协议转换准备**
|
||||
- 处理网关到子设备的协议差异
|
||||
- 准备子设备专用参数
|
||||
- 处理网关到子设备的协议差异
|
||||
- 准备子设备专用参数
|
||||
|
||||
## 🏗️ 架构设计
|
||||
|
||||
### 执行阶段
|
||||
|
||||
- **Phase**: `MID`(中置阶段)
|
||||
- **Order**: `400`
|
||||
- **时机**: 在消息转换之后,业务处理之前
|
||||
@@ -370,24 +372,24 @@ public boolean preHandle(DownlinkContext<?> context) throws Exception {
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **性能考虑**
|
||||
- 避免在拦截器中执行耗时操作
|
||||
- 使用缓存减少数据库查询
|
||||
- 异步处理非关键逻辑
|
||||
- 避免在拦截器中执行耗时操作
|
||||
- 使用缓存减少数据库查询
|
||||
- 异步处理非关键逻辑
|
||||
|
||||
2. **异常处理**
|
||||
- `preHandle` 中的异常会导致整个流程中断
|
||||
- 使用 `try-catch` 妥善处理异常
|
||||
- 在 `afterCompletion` 中记录错误
|
||||
- `preHandle` 中的异常会导致整个流程中断
|
||||
- 使用 `try-catch` 妥善处理异常
|
||||
- 在 `afterCompletion` 中记录错误
|
||||
|
||||
3. **上下文传递**
|
||||
- 使用 `context.setAttribute()` 传递数据
|
||||
- 注意数据的生命周期
|
||||
- 在 `afterCompletion` 中清理临时数据
|
||||
- 使用 `context.setAttribute()` 传递数据
|
||||
- 注意数据的生命周期
|
||||
- 在 `afterCompletion` 中清理临时数据
|
||||
|
||||
4. **条件判断**
|
||||
- `supports()` 方法要尽量轻量
|
||||
- 复杂判断放在 `preHandle()` 中
|
||||
- 避免重复查询
|
||||
- `supports()` 方法要尽量轻量
|
||||
- 复杂判断放在 `preHandle()` 中
|
||||
- 避免重复查询
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
|
||||
+32
-44
@@ -26,15 +26,15 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 统一下行指令模型
|
||||
* 作为所有下行指令的标准化入口,统一参数格式,避免String/JSONObject来回转换
|
||||
* 统一下行指令模型 作为所有下行指令的标准化入口,统一参数格式,避免String/JSONObject来回转换
|
||||
*
|
||||
* <p>设计目标:
|
||||
*
|
||||
* <ul>
|
||||
* <li>类型安全:通过强类型字段避免类型转换错误</li>
|
||||
* <li>参数统一:将分散的参数集中管理</li>
|
||||
* <li>易于扩展:通过extensions支持协议特定参数</li>
|
||||
* <li>向后兼容:支持从旧格式(String/JSONObject)构建</li>
|
||||
* <li>类型安全:通过强类型字段避免类型转换错误
|
||||
* <li>参数统一:将分散的参数集中管理
|
||||
* <li>易于扩展:通过extensions支持协议特定参数
|
||||
* <li>向后兼容:支持从旧格式(String/JSONObject)构建
|
||||
* </ul>
|
||||
*
|
||||
* @version 1.0
|
||||
@@ -110,8 +110,7 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
// ===== 扩展参数(协议特定) =====
|
||||
|
||||
/** 扩展字段(用于协议特定参数,避免污染核心字段) */
|
||||
@Builder.Default
|
||||
private Map<String, Object> extensions = new HashMap<>();
|
||||
@Builder.Default private Map<String, Object> extensions = new HashMap<>();
|
||||
|
||||
// ===== 控制标志 =====
|
||||
|
||||
@@ -127,12 +126,9 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
// ===== 元数据 =====
|
||||
|
||||
/** 指令元数据(用于记录来源、优先级等) */
|
||||
@Builder.Default
|
||||
private CommandMetadata metadata = new CommandMetadata();
|
||||
@Builder.Default private CommandMetadata metadata = new CommandMetadata();
|
||||
|
||||
/**
|
||||
* 指令元数据
|
||||
*/
|
||||
/** 指令元数据 */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -205,7 +201,7 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
|
||||
// 控制标志
|
||||
command.setDeviceReuse(json.getBool("deviceReuse", false));
|
||||
command.setAdmin(json.getBool("isAdmin", false)); // 使用setAdmin而非setIsAdmin
|
||||
command.setAdmin(json.getBool("isAdmin", false)); // 使用setAdmin而非setIsAdmin
|
||||
command.setAllowInsert(json.getBool("allowInsert", false));
|
||||
|
||||
// 保存原始数据(用于协议特定处理)
|
||||
@@ -250,49 +246,37 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
|
||||
// ===== 链式调用方法(便于构建) =====
|
||||
|
||||
/**
|
||||
* 设置产品Key(链式调用)
|
||||
*/
|
||||
/** 设置产品Key(链式调用) */
|
||||
public UnifiedDownlinkCommand withProductKey(String productKey) {
|
||||
this.productKey = productKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置设备ID(链式调用)
|
||||
*/
|
||||
/** 设置设备ID(链式调用) */
|
||||
public UnifiedDownlinkCommand withDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用ID(链式调用)
|
||||
*/
|
||||
/** 设置应用ID(链式调用) */
|
||||
public UnifiedDownlinkCommand withAppUnionId(String appUnionId) {
|
||||
this.appUnionId = appUnionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用实例ID(链式调用)
|
||||
*/
|
||||
/** 设置应用实例ID(链式调用) */
|
||||
public UnifiedDownlinkCommand withApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指令类型(链式调用)
|
||||
*/
|
||||
/** 设置指令类型(链式调用) */
|
||||
public UnifiedDownlinkCommand withCmd(DownCmd cmd) {
|
||||
this.cmd = cmd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加扩展字段(链式调用)
|
||||
*/
|
||||
/** 添加扩展字段(链式调用) */
|
||||
public UnifiedDownlinkCommand addExtension(String key, Object value) {
|
||||
if (this.extensions == null) {
|
||||
this.extensions = new HashMap<>();
|
||||
@@ -301,9 +285,7 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置元数据来源(链式调用)
|
||||
*/
|
||||
/** 设置元数据来源(链式调用) */
|
||||
public UnifiedDownlinkCommand withSource(String source) {
|
||||
this.metadata.setSource(source);
|
||||
return this;
|
||||
@@ -328,12 +310,11 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
// 根据不同指令类型验证特定参数
|
||||
switch (cmd) {
|
||||
case DEV_ADD:
|
||||
case DEV_DEL: // 使用DEV_DEL而非DEV_DELETE
|
||||
case DEV_DEL: // 使用DEV_DEL而非DEV_DELETE
|
||||
case DEV_UPDATE:
|
||||
case DEV_FUNCTION:
|
||||
if (StrUtil.isBlank(deviceId) && StrUtil.isBlank(iotId)) {
|
||||
throw new IllegalArgumentException(
|
||||
cmd.getValue() + "指令必须提供deviceId或iotId");
|
||||
throw new IllegalArgumentException(cmd.getValue() + "指令必须提供deviceId或iotId");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -433,11 +414,18 @@ public class UnifiedDownlinkCommand implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UnifiedDownlinkCommand{" +
|
||||
"productKey='" + productKey + '\'' +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
", cmd=" + cmd +
|
||||
", msgId='" + msgId + '\'' +
|
||||
'}';
|
||||
return "UnifiedDownlinkCommand{"
|
||||
+ "productKey='"
|
||||
+ productKey
|
||||
+ '\''
|
||||
+ ", deviceId='"
|
||||
+ deviceId
|
||||
+ '\''
|
||||
+ ", cmd="
|
||||
+ cmd
|
||||
+ ", msgId='"
|
||||
+ msgId
|
||||
+ '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -45,7 +45,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* @since 2023/05/19 19:28
|
||||
*/
|
||||
@Slf4j
|
||||
|
||||
public class ProtocolCodecMagic extends ProtocolCodecSupportWrapper
|
||||
implements ProtocolCodecLoader, ProtocolCodecSupport, ProtocolCodecWrapper {
|
||||
|
||||
|
||||
+5
-4
@@ -28,8 +28,10 @@ public interface IDown {
|
||||
|
||||
/**
|
||||
* 下行处理(统一命令对象)
|
||||
* <p>这是唯一的下行入口方法,提供类型安全的参数传递</p>
|
||||
* <p>此方法会自动应用拦截器链(如果已配置)</p>
|
||||
*
|
||||
* <p>这是唯一的下行入口方法,提供类型安全的参数传递
|
||||
*
|
||||
* <p>此方法会自动应用拦截器链(如果已配置)
|
||||
*
|
||||
* @param command 统一下行命令对象
|
||||
* @return 处理结果
|
||||
@@ -57,8 +59,7 @@ public interface IDown {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建下行上下文
|
||||
* 基于UnifiedDownlinkCommand创建上下文对象
|
||||
* 创建下行上下文 基于UnifiedDownlinkCommand创建上下文对象
|
||||
*
|
||||
* @param command 统一下行命令对象
|
||||
* @return 下行上下文
|
||||
|
||||
+16
-14
@@ -1,4 +1,4 @@
|
||||
///*
|
||||
/// *
|
||||
// *
|
||||
// * Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
// *
|
||||
@@ -10,25 +10,25 @@
|
||||
// *
|
||||
// */
|
||||
//
|
||||
//package cn.universal.core.message;
|
||||
// package cn.universal.core.message;
|
||||
//
|
||||
//import cn.hutool.json.JSONObject;
|
||||
//import cn.hutool.json.JSONUtil;
|
||||
//import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
//import org.junit.Test;
|
||||
// import cn.hutool.json.JSONObject;
|
||||
// import cn.hutool.json.JSONUtil;
|
||||
// import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
// import org.junit.Test;
|
||||
//
|
||||
//import java.util.HashMap;
|
||||
//import java.util.Map;
|
||||
// import java.util.HashMap;
|
||||
// import java.util.Map;
|
||||
//
|
||||
//import static org.junit.Assert.*;
|
||||
// import static org.junit.Assert.*;
|
||||
//
|
||||
///**
|
||||
/// **
|
||||
// * UnifiedDownlinkCommand 单元测试
|
||||
// *
|
||||
// * @version 1.0
|
||||
// * @since 2025/10/25
|
||||
// */
|
||||
//public class UnifiedDownlinkCommandTest {
|
||||
// public class UnifiedDownlinkCommandTest {
|
||||
//
|
||||
// @Test
|
||||
// public void testFromJson() {
|
||||
@@ -54,7 +54,8 @@
|
||||
// public void testFromString() {
|
||||
// // 准备JSON字符串
|
||||
// String jsonString =
|
||||
// "{\"productKey\":\"test-product-002\",\"deviceId\":\"test-device-002\",\"cmd\":\"DEV_FUNCTION\",\"function\":{\"messageType\":\"FUNCTIONS\",\"function\":\"turnOn\",\"data\":{\"switch\":true}}}";
|
||||
//
|
||||
// "{\"productKey\":\"test-product-002\",\"deviceId\":\"test-device-002\",\"cmd\":\"DEV_FUNCTION\",\"function\":{\"messageType\":\"FUNCTIONS\",\"function\":\"turnOn\",\"data\":{\"switch\":true}}}";
|
||||
//
|
||||
// // 执行转换
|
||||
// UnifiedDownlinkCommand command = UnifiedDownlinkCommand.fromString(jsonString);
|
||||
@@ -266,7 +267,8 @@
|
||||
// public void testFromJson_WithExtensions() {
|
||||
// // 测试包含扩展字段的JSON转换
|
||||
// String jsonString =
|
||||
// "{\"productKey\":\"test-product-013\",\"deviceId\":\"test-device-013\",\"cmd\":\"DEV_ADD\",\"data\":{\"imei\":\"123456789\",\"location\":\"Beijing\"}}";
|
||||
//
|
||||
// "{\"productKey\":\"test-product-013\",\"deviceId\":\"test-device-013\",\"cmd\":\"DEV_ADD\",\"data\":{\"imei\":\"123456789\",\"location\":\"Beijing\"}}";
|
||||
//
|
||||
// UnifiedDownlinkCommand command = UnifiedDownlinkCommand.fromString(jsonString);
|
||||
//
|
||||
@@ -347,4 +349,4 @@
|
||||
// assertTrue(str.contains("DEV_ADD"));
|
||||
// assertTrue(str.contains("msg-001"));
|
||||
// }
|
||||
//}
|
||||
// }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user