first commit
This commit is contained in:
113
system/README.md
Normal file
113
system/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
## 快速开始
|
||||
|
||||
**example** 中有 `mqtt` 服务端和客户端演示代码。
|
||||
|
||||
也可以只启动**Server**端,**Client**端用客户端工具测试。
|
||||
|
||||
### 1. 启动 Server 端
|
||||
|
||||
运行 `example/mica-mqtt-server-spring-boot-example/src/main/java/net/dreamlu/iot/mqtt/server/MqttServerApplication.java` 的 `main` 方法
|
||||
|
||||
控制台打印如下内容:
|
||||
|
||||
```text
|
||||
2021-07-05 20:42:36,869 INFO server.TioServer -
|
||||
|----------------------------------------------------------------------------------------|
|
||||
| t-io site | https://www.tiocloud.com |
|
||||
| t-io on gitee | https://gitee.com/tywo45/t-io |
|
||||
| t-io on github | https://github.com/tywo45/t-io |
|
||||
| t-io version | 3.7.3.v20210706-RELEASE |
|
||||
| ---------------------------------------------------------------------------------------|
|
||||
| TioConfig name | Mica-Mqtt-Server |
|
||||
| Started at | 2021-07-05 20:42:36 |
|
||||
| Listen on | 127.0.0.1:1883 |
|
||||
| Main Class | org.dromara.mica.mqtt.server.MqttServerTest |
|
||||
| Jvm start time | 2715ms |
|
||||
| Tio start time | 16ms |
|
||||
| Pid | 3588 |
|
||||
|----------------------------------------------------------------------------------------|
|
||||
|
||||
2021-07-05 20:42:37,884 WARN server.MqttServer - Mqtt publish to all ChannelContext is empty.
|
||||
```
|
||||
|
||||
`Mqtt publish to all ChannelContext is empty.` 通道上下文为空,即没有客户端。
|
||||
|
||||
```text
|
||||
Mica-Mqtt-Server
|
||||
├ 当前时间:1625489086843
|
||||
├ 连接统计
|
||||
│ ├ 共接受过连接数 :0
|
||||
│ ├ 当前连接数 :0
|
||||
│ ├ 异IP连接数 :0
|
||||
│ └ 关闭过的连接数 :0
|
||||
├ 消息统计
|
||||
│ ├ 已处理消息 :0
|
||||
│ ├ 已接收消息(packet/byte):0/0
|
||||
│ ├ 已发送消息(packet/byte):0/0b
|
||||
│ ├ 平均每次TCP包接收的字节数 :0.0
|
||||
│ └ 平均每次TCP包接收的业务包 :0.0
|
||||
└ IP统计时段
|
||||
└ 没有设置ip统计时间
|
||||
├ 节点统计
|
||||
│ ├ clientNodes :0
|
||||
│ ├ 所有连接 :0
|
||||
│ ├ 绑定user数 :0
|
||||
│ ├ 绑定token数 :0
|
||||
│ └ 等待同步消息响应 :0
|
||||
├ 群组
|
||||
│ └ groupmap:0
|
||||
└ 拉黑IP
|
||||
└ []
|
||||
2021-07-05 20:44:46,925 WARN server.ServerTioConfig - Mica-Mqtt-Server, 检查心跳, 共0个连接, 取锁耗时0ms, 循环耗时71ms, 心跳超时时间:120000ms
|
||||
```
|
||||
|
||||
### 2. 启动 Client 端
|
||||
|
||||
运行 `example/mica-mqtt-client-spring-boot-example/src/main/java/net/dreamlu/iot/mqtt/client/MqttClientApplication.java` 的 `main` 方法
|
||||
|
||||
控制台打印如下内容,表示客户端连接成功:
|
||||
```text
|
||||
2021-07-05 20:46:10,972 ERROR client.TioClient - closeds:0, connections:0
|
||||
2021-07-05 20:46:10,972 INFO client.TioClient - [1]: curr:0, closed:0, received:(0p)(0b), handled:0, sent:(0p)(0b)
|
||||
2021-07-05 20:46:12,566 INFO client.ConnectionCompletionHandler - connected to 127.0.0.1:1883
|
||||
2021-07-05 20:46:12,586 INFO client.MqttClient - MqttClient reconnect send connect result:true
|
||||
2021-07-05 20:46:12,630 INFO client.DefaultMqttClientProcessor - MqttClient connection succeeded!
|
||||
2021-07-05 20:46:13,932 INFO client.MqttClientTest - /test/123 mica最牛皮
|
||||
```
|
||||
|
||||
此时的 Server 端会打印出如下内容:
|
||||
|
||||
```text
|
||||
2021-07-05 20:46:45,654 INFO server.MqttServerTest - subscribe: /test/client mica最牛皮
|
||||
2021-07-05 20:46:46,926 WARN server.ServerTioConfig -
|
||||
Mica-Mqtt-Server
|
||||
├ 当前时间:1625489206923
|
||||
├ 连接统计
|
||||
│ ├ 共接受过连接数 :1
|
||||
│ ├ 当前连接数 :1
|
||||
│ ├ 异IP连接数 :1
|
||||
│ └ 关闭过的连接数 :0
|
||||
├ 消息统计
|
||||
│ ├ 已处理消息 :20
|
||||
│ ├ 已接收消息(packet/byte):20/584
|
||||
│ ├ 已发送消息(packet/byte):37/935b
|
||||
│ ├ 平均每次TCP包接收的字节数 :29.2
|
||||
│ └ 平均每次TCP包接收的业务包 :1.0
|
||||
└ IP统计时段
|
||||
└ 没有设置ip统计时间
|
||||
├ 节点统计
|
||||
│ ├ clientNodes :1
|
||||
│ ├ 所有连接 :1
|
||||
│ ├ 绑定user数 :0
|
||||
│ ├ 绑定token数 :0
|
||||
│ └ 等待同步消息响应 :0
|
||||
├ 群组
|
||||
│ └ groupmap:0
|
||||
└ 拉黑IP
|
||||
└ []
|
||||
2021-07-05 20:46:46,926 WARN server.ServerTioConfig - Mica-Mqtt-Server, 检查心跳, 共1个连接, 取锁耗时0ms, 循环耗时0ms, 心跳超时时间:120000ms
|
||||
```
|
||||
|
||||
### 3. Client 接入 Aliyun MQTT 服务(示例)
|
||||
|
||||
详见 `example/mica-mqtt-example/src/main/java/net/dreamlu/iot/mqtt/aliyun/MqttClientTest.java`
|
||||
14
system/mqtt-xf-receiver/README.md
Normal file
14
system/mqtt-xf-receiver/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## SpringBoot + mica-mqtt-server 应用演示
|
||||
|
||||
## 启动步骤
|
||||
1. 先启动 mica-mqtt-server-spring-boot-example
|
||||
|
||||
2. 再启动 mica-mqtt-client-spring-boot-example
|
||||
|
||||
3. 查看控制器 swagger 地址:http://localhost:30012/doc.html
|
||||
|
||||
4. 可开启 prometheus 指标收集,详见: http://localhost:30012/actuator/prometheus
|
||||
|
||||
## 连接
|
||||
|
||||
mica Spring boot 开发组件集文档:https://www.dreamlu.net/components/mica-swagger.html
|
||||
130
system/mqtt-xf-receiver/pom.xml
Normal file
130
system/mqtt-xf-receiver/pom.xml
Normal file
@@ -0,0 +1,130 @@
|
||||
<?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>
|
||||
<artifactId>mqtt-xf-receiver</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>system</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt-server-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-lite</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-openapi</artifactId>
|
||||
</dependency>
|
||||
<!-- 开启 prometheus 指标收集,详见: http://localhost:30012/actuator/prometheus -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
|
||||
<version>3.5.14</version>
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>com.baomidou</groupId>-->
|
||||
<!-- <artifactId>mybatis-plus-spring-boot-autoconfigure</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>1.2.20</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Boot Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON 解析器和生成器 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.23</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool工具包 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.40</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.dromara.mica.mqtt.server;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@MapperScan("org.dromara.mica.mqtt.server.mapper")
|
||||
public class MqttServerApplication {
|
||||
|
||||
/**
|
||||
* 先启动本项目,再启动 mica-mqtt-client-spring-boot-example 进行测试
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MqttServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerAuthHandler;
|
||||
import org.dromara.mica.mqtt.spring.server.config.MqttServerProperties;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例 mqtt tcp、websocket 认证,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttAuthHandler implements IMqttServerAuthHandler {
|
||||
|
||||
@Value("${mqtt.server.auth.enable}")
|
||||
private boolean enable;
|
||||
|
||||
@Value("${mqtt.server.auth.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${mqtt.server.auth.password}")
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public boolean authenticate(ChannelContext context, String uniqueId, String clientId, String username, String password) {
|
||||
// 客户端认证逻辑实现
|
||||
if (enable) {
|
||||
if (username.equals(this.username) && password.equals(this.password)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.http.api.code.ResultCode;
|
||||
import org.dromara.mica.mqtt.core.server.http.api.result.Result;
|
||||
import org.dromara.mica.mqtt.core.server.http.handler.HttpFilter;
|
||||
import org.dromara.mica.mqtt.core.server.http.handler.MqttHttpRoutes;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.http.common.HttpRequest;
|
||||
import org.tio.http.common.HttpResponse;
|
||||
|
||||
/**
|
||||
* 示例自定义 mqtt http 接口认证,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttHttpAuthFilter implements HttpFilter, InitializingBean {
|
||||
|
||||
@Override
|
||||
public boolean filter(HttpRequest request) throws Exception {
|
||||
// 自行实现逻辑
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse response(HttpRequest request) {
|
||||
// 认证不通过时的响应
|
||||
return Result.fail(request, ResultCode.E103);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
MqttHttpRoutes.addFilter(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerSubscribeValidator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例自定义订阅校验,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttSubscribeValidator implements IMqttServerSubscribeValidator {
|
||||
|
||||
@Override
|
||||
public boolean isValid(ChannelContext context, String clientId, String topicFilter, MqttQoS qoS) {
|
||||
// 校验客户端订阅的 topic,校验成功返回 true,失败返回 false
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.mica.mqtt.server.auth;
|
||||
|
||||
import org.dromara.mica.mqtt.core.server.auth.IMqttServerUniqueIdService;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* 示例自定义 clientId,请按照自己的需求和业务进行扩展
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MqttUniqueIdService implements IMqttServerUniqueIdService {
|
||||
|
||||
@Override
|
||||
public String getUniqueId(ChannelContext context, String clientId, String userName, String password) {
|
||||
// 返回的 uniqueId 会替代 mqtt client 传过来的 clientId,请保证返回的 uniqueId 唯一。
|
||||
return clientId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.dromara.mica.mqtt.server.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* mybatis-plus配置
|
||||
*
|
||||
* @author Mark sunlightcs@gmail.com
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* 配置分页等
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
|
||||
// 乐观锁
|
||||
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
|
||||
return mybatisPlusInterceptor;
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
|
||||
// MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
|
||||
// sqlSessionFactoryBean.setDataSource(dataSource);
|
||||
// sqlSessionFactoryBean.setMapperLocations(
|
||||
// new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/**/*.xml")
|
||||
// );
|
||||
// return sqlSessionFactoryBean.getObject();
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.dromara.mica.mqtt.server.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(factory);
|
||||
//使用jackson进行序列化
|
||||
Jackson2JsonRedisSerializer jsonRedisSerializer =
|
||||
new Jackson2JsonRedisSerializer(Object.class);
|
||||
//规定序列化规则
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
/**
|
||||
* 第一个参数指的是序列化的域,ALL指的是字段、get和set方法、构造方法
|
||||
* 第二个参数指的是序列化哪些访问修饰符,默认是public,ANY指任何访问修饰符
|
||||
*/
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
//指定序列化输入的类型,类必须是非final修饰的类
|
||||
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jsonRedisSerializer.setObjectMapper(objectMapper);
|
||||
//序列化key value
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setValueSerializer(jsonRedisSerializer);
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dromara.mica.mqtt.server.constant;
|
||||
|
||||
/**
|
||||
* 缓存常量信息
|
||||
*/
|
||||
public class CacheConstants
|
||||
{
|
||||
|
||||
/**
|
||||
* 设备心跳缓存key
|
||||
*/
|
||||
public static final String EQUIPMENT_HEARTBEAT = "equipment:heartbeat:";
|
||||
|
||||
/**
|
||||
* 设备心跳缓存过期时间
|
||||
*/
|
||||
public static final long OFFLINE_THRESHOLD = 10 * 1000;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.mica.mqtt.server.controller;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.dromara.mica.mqtt.server.service.impl.ServerService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RequestMapping("/mqtt/server")
|
||||
@RestController
|
||||
public class ServerController {
|
||||
@Autowired
|
||||
private ServerService service;
|
||||
|
||||
@PostMapping("publish")
|
||||
public JSONObject publish(@RequestBody JSONObject js) {
|
||||
boolean publish = service.publish(js);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
if (publish) {
|
||||
jsonObject.put("code", 200);
|
||||
} else {
|
||||
jsonObject.put("code", 500);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.dromara.mica.mqtt.server.entity;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@TableName("sys_equipment")
|
||||
public class Equipment implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/** 设备Id */
|
||||
private Long id;
|
||||
|
||||
/** 所属产品Id */
|
||||
private Long productId;
|
||||
|
||||
/** 设备名称 */
|
||||
private String name;
|
||||
|
||||
/** 设备序列号 */
|
||||
private String sequence;
|
||||
|
||||
/** 设备Ip */
|
||||
private String ip;
|
||||
|
||||
/** 设备密码 */
|
||||
private String password;
|
||||
|
||||
/** 设备区域 */
|
||||
private Long spaceId;
|
||||
|
||||
/** 设备位置 */
|
||||
private Long pointId;
|
||||
|
||||
/** 对接状态(0未对接 1对接成功) */
|
||||
private Long state;
|
||||
|
||||
/** 设备状态(0在线 1离线) */
|
||||
private String flag;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.mica.mqtt.server.enums;
|
||||
|
||||
/**
|
||||
* 设备是否在线
|
||||
*
|
||||
*/
|
||||
public enum FlagEnums {
|
||||
ONLINE("0", "在线"),
|
||||
OFFLINE("1", "离线")
|
||||
;
|
||||
|
||||
FlagEnums(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private String code;
|
||||
private String name;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.dromara.mica.mqtt.server.listener;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.core.annotation.MqttServerFunction;
|
||||
import org.dromara.mica.mqtt.server.constant.CacheConstants;
|
||||
import org.dromara.mica.mqtt.server.enums.FlagEnums;
|
||||
import org.dromara.mica.mqtt.server.pojo.WhiteListOperatorPO;
|
||||
import org.dromara.mica.mqtt.server.redis.RedisService;
|
||||
import org.dromara.mica.mqtt.server.utils.AESUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.tio.utils.hutool.StrUtil;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 消息监听器
|
||||
*
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class CarMessageListener {
|
||||
|
||||
@Autowired
|
||||
RedisService redisService;
|
||||
|
||||
/**
|
||||
* 心跳
|
||||
* @param topic
|
||||
* @param topicVars
|
||||
* @param message
|
||||
*/
|
||||
@MqttServerFunction("${sn}")
|
||||
public void onKeepAliveMessage(String topic, Map<String, String> topicVars, byte[] message) {
|
||||
String sn = topicVars.get("sn");
|
||||
log.info("接收到来自客户端 [{}] 的心跳消息 -> Topic: {}, TopicVars: {}, Message: {}", sn, topic,topicVars,new String(message, StandardCharsets.UTF_8));
|
||||
|
||||
// 更新客户端的最后心跳
|
||||
redisService.setCacheObject(CacheConstants.EQUIPMENT_HEARTBEAT + sn, FlagEnums.ONLINE.getCode(), CacheConstants.OFFLINE_THRESHOLD, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.mica.mqtt.server.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.spring.server.event.MqttClientOfflineEvent;
|
||||
import org.dromara.mica.mqtt.spring.server.event.MqttClientOnlineEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态,使用 spring boot event 方式,性能有损耗
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
//@Service
|
||||
public class MqttConnectStatusListener1 {
|
||||
|
||||
@EventListener
|
||||
public void online(MqttClientOnlineEvent event) {
|
||||
log.info("MqttClientOnlineEvent:{}", event);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void offline(MqttClientOfflineEvent event) {
|
||||
log.info("MqttClientOfflineEvent:{}", event);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.dromara.mica.mqtt.server.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.core.server.event.IMqttConnectStatusListener;
|
||||
import org.dromara.mica.mqtt.server.constant.CacheConstants;
|
||||
import org.dromara.mica.mqtt.server.enums.FlagEnums;
|
||||
import org.dromara.mica.mqtt.server.redis.RedisService;
|
||||
import org.dromara.mica.mqtt.server.service.IEquipmentService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
/**
|
||||
* mqtt 连接状态
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MqttConnectStatusListener2 implements IMqttConnectStatusListener {
|
||||
|
||||
@Autowired
|
||||
IEquipmentService equipmentService;
|
||||
|
||||
@Autowired
|
||||
RedisService redisService;
|
||||
|
||||
@Override
|
||||
public void online(ChannelContext context, String clientId, String username) {
|
||||
//设备上线不做任何处理,只有心跳报文做处理
|
||||
log.info("online-context: {}", context);
|
||||
log.info("设备:{}上线", clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void offline(ChannelContext context, String clientId, String username, String reason) {
|
||||
log.info("offline-context: {}", context);
|
||||
redisService.deleteObject(CacheConstants.EQUIPMENT_HEARTBEAT + clientId);
|
||||
log.info("设备:{}离线,offline reason:{}.", clientId, reason);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.mica.mqtt.server.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
|
||||
import org.dromara.mica.mqtt.codec.MqttQoS;
|
||||
import org.dromara.mica.mqtt.core.server.event.IMqttMessageListener;
|
||||
import org.dromara.mica.mqtt.spring.server.MqttServerTemplate;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.tio.core.ChannelContext;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 消息监听器示例1,直接实现 IMqttMessageListener,注意:如果实现了 IMqttMessageListener,MqttServerFunction 注解就不生效了。
|
||||
*
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
public class MqttServerMessageListener1 implements IMqttMessageListener, SmartInitializingSingleton {
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private MqttServerTemplate mqttServerTemplate;
|
||||
|
||||
@Override
|
||||
public void onMessage(ChannelContext context, String clientId, String topic, MqttQoS qos, MqttPublishMessage message) {
|
||||
log.info("context:{} clientId:{} message:{} payload:{}", context, clientId, message, new String(message.payload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// 单利 bean 初始化完成之后从 ApplicationContext 中获取 bean
|
||||
mqttServerTemplate = applicationContext.getBean(MqttServerTemplate.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dromara.mica.mqtt.server.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.dromara.mica.mqtt.server.entity.Equipment;
|
||||
|
||||
@Mapper
|
||||
public interface EquipmentMapper extends BaseMapper<Equipment> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.dromara.mica.mqtt.server.pojo;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备报文数据实体
|
||||
*/
|
||||
@Data
|
||||
public class WhiteListOperatorPO {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 回执的code
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 设备编码
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 报文名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version = "1.0";
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private JSONObject payload;
|
||||
|
||||
/**
|
||||
* 时间戳(精确到秒)
|
||||
*/
|
||||
private Long timestamp = System.currentTimeMillis() / 1000;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package org.dromara.mica.mqtt.server.redis;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RedisService {
|
||||
|
||||
|
||||
@Autowired
|
||||
public RedisTemplate redisTemplate;
|
||||
|
||||
private static final long DEFAULT_TIMEOUT = 60 * 60 * 24 * 7;
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value)
|
||||
{
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 缓存是否存在,存在返回false,不存在返回true并存储缓存值
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public Boolean setIfAbsent(final String key, final String value, long timeout, TimeUnit timeUnit)
|
||||
{
|
||||
try {
|
||||
return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
|
||||
} catch (Exception e) {
|
||||
log.error("redis error:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分布式加锁
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public Boolean lock(final String key, final String value, long timeout, TimeUnit timeUnit)
|
||||
{
|
||||
try {
|
||||
if (timeout <= 0) {
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
timeUnit = TimeUnit.SECONDS;
|
||||
}
|
||||
return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
|
||||
} catch (Exception e) {
|
||||
log.error("redis error:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @param timeout 时间
|
||||
* @param timeUnit 时间颗粒度
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
|
||||
{
|
||||
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param timeout 超时时间
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public boolean expire(final String key, final long timeout)
|
||||
{
|
||||
return expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param timeout 超时时间
|
||||
* @param unit 时间单位
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public boolean expire(final String key, final long timeout, final TimeUnit unit)
|
||||
{
|
||||
return redisTemplate.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @return 有效时间
|
||||
*/
|
||||
public long getExpire(final String key)
|
||||
{
|
||||
return redisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 key是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hasKey(String key)
|
||||
{
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象。
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> T getCacheObject(final String key)
|
||||
{
|
||||
ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
return operation.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个对象
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public boolean deleteObject(final String key)
|
||||
{
|
||||
return redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除集合对象
|
||||
*
|
||||
* @param collection 多个对象
|
||||
* @return
|
||||
*/
|
||||
public boolean deleteObject(final Collection collection)
|
||||
{
|
||||
return redisTemplate.delete(collection) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存List数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param dataList 待缓存的List数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public <T> long setCacheList(final String key, final List<T> dataList)
|
||||
{
|
||||
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
|
||||
return count == null ? 0 : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> List<T> getCacheList(final String key)
|
||||
{
|
||||
return redisTemplate.opsForList().range(key, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Set
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @param dataSet 缓存的数据
|
||||
* @return 缓存数据的对象
|
||||
*/
|
||||
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
|
||||
{
|
||||
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
|
||||
Iterator<T> it = dataSet.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
setOperation.add(it.next());
|
||||
}
|
||||
return setOperation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的set
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Set<T> getCacheSet(final String key)
|
||||
{
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Map
|
||||
*
|
||||
* @param key
|
||||
* @param dataMap
|
||||
*/
|
||||
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
|
||||
{
|
||||
if (dataMap != null) {
|
||||
redisTemplate.opsForHash().putAll(key, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的Map
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Map<String, T> getCacheMap(final String key)
|
||||
{
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 往Hash中存入数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @param value 值
|
||||
*/
|
||||
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
|
||||
{
|
||||
redisTemplate.opsForHash().put(key, hKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return Hash中的对象
|
||||
*/
|
||||
public <T> T getCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
|
||||
return opsForHash.get(key, hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKeys Hash键集合
|
||||
* @return Hash对象集合
|
||||
*/
|
||||
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
|
||||
{
|
||||
return redisTemplate.opsForHash().multiGet(key, hKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的某条数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean deleteCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
return redisTemplate.opsForHash().delete(key, hKey) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象列表
|
||||
*
|
||||
* @param pattern 字符串前缀
|
||||
* @return 对象列表
|
||||
*/
|
||||
public Collection<String> keys(final String pattern)
|
||||
{
|
||||
return redisTemplate.keys(pattern);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.dromara.mica.mqtt.server.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.dromara.mica.mqtt.server.entity.Equipment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IEquipmentService extends IService<Equipment> {
|
||||
|
||||
Equipment selectEquipmentBySn(String sn);
|
||||
|
||||
List<Equipment> selectAllSnFlag();
|
||||
|
||||
void updateFlag(String sn, String flag);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.mica.mqtt.server.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.dromara.mica.mqtt.server.entity.Equipment;
|
||||
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
|
||||
import org.dromara.mica.mqtt.server.service.IEquipmentService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment> implements IEquipmentService {
|
||||
|
||||
@Autowired
|
||||
EquipmentMapper equipmentMapper;
|
||||
|
||||
@Override
|
||||
public Equipment selectEquipmentBySn(String sn) {
|
||||
return equipmentMapper.selectOne(new QueryWrapper<Equipment>().eq("sequence", sn).eq("product_id", 4L).last("limit 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Equipment> selectAllSnFlag() {
|
||||
return equipmentMapper.selectList(new QueryWrapper<Equipment>().eq("product_id", 4L).select("sequence", "flag"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFlag(String sn, String flag) {
|
||||
Equipment equipment = new Equipment();
|
||||
equipment.setFlag(flag);
|
||||
equipmentMapper.update(equipment, new QueryWrapper<Equipment>().eq("sequence", sn).eq("product_id", 4L));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.mica.mqtt.server.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.spring.server.MqttServerTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author wsq
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ServerService {
|
||||
@Autowired
|
||||
private MqttServerTemplate server;
|
||||
|
||||
public boolean publish(JSONObject js) {
|
||||
String sn = js.getString("sn");
|
||||
String topic = js.getString("topic");
|
||||
String body = js.getString("body");
|
||||
boolean result = server.publish(sn,topic, body.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("publish-topic:{},body:{},result:{}", topic, body, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.dromara.mica.mqtt.server.task;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.mica.mqtt.server.constant.CacheConstants;
|
||||
import org.dromara.mica.mqtt.server.entity.Equipment;
|
||||
import org.dromara.mica.mqtt.server.enums.FlagEnums;
|
||||
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
|
||||
import org.dromara.mica.mqtt.server.redis.RedisService;
|
||||
import org.dromara.mica.mqtt.server.service.IEquipmentService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.tio.utils.hutool.StrUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 检查设备心跳,判断设备是否在线
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HeartbeatOnLineTask {
|
||||
|
||||
@Autowired
|
||||
RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
IEquipmentService equipmentService;
|
||||
|
||||
@Scheduled(fixedRate = 10 * 1000)
|
||||
public void run() {
|
||||
log.info("===========心跳检测=============");
|
||||
//缓存中有该设备心跳key
|
||||
if (redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT)) {
|
||||
|
||||
} else {
|
||||
//没有心跳上传,且设备在线,将设备置为离线
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package org.dromara.mica.mqtt.server.utils;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* AES加密工具类
|
||||
*/
|
||||
public class AESUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 加密Key 需要16位 可用数字与字母组成
|
||||
*/
|
||||
private static String key = "1234567898765432";
|
||||
/**
|
||||
* 偏移量 需要16位
|
||||
*/
|
||||
private static String iv = "4w2Df1xSj5ff662d";
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(AESUtil.class);
|
||||
|
||||
private static Base64.Decoder decoder;
|
||||
|
||||
private static Base64.Encoder encoder;
|
||||
|
||||
|
||||
|
||||
static {
|
||||
decoder = Base64.getDecoder();
|
||||
encoder = Base64.getEncoder();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String getSixteenBitString(){
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String[] chars = new String[]{
|
||||
"1","2","3","4","5","6","7","8","9","a","b",
|
||||
"c","d","e","f","g","h","i","j","k","l","m",
|
||||
"n","o","p","q","r","s","t","u","v","w","x",
|
||||
"y","z","A","B","C","D","E","F","G","H","I",
|
||||
"J","K","L","M","N","O","P","Q","R","S","T",
|
||||
"U","V","W","X","Y","Z",
|
||||
};
|
||||
int len = chars.length;
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
sb.append(chars[random.nextInt(len-1)]);
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
* @param data
|
||||
* @param key
|
||||
* @param iv
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encryptAES_CBC(String data,String key,String iv) {
|
||||
Cipher cipher = null;
|
||||
try {
|
||||
cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
|
||||
int blockSize = cipher.getBlockSize();
|
||||
byte[] dataBytes = data.getBytes();
|
||||
int dataLength = dataBytes.length;
|
||||
if (dataLength % blockSize != 0) {
|
||||
dataLength = dataLength + (blockSize - (dataLength % blockSize));
|
||||
}
|
||||
byte[] plaintext = new byte[dataLength];
|
||||
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
|
||||
byte[] bytes = cipher.doFinal(plaintext);
|
||||
return encoder.encodeToString(bytes);
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error("AES加密失败");
|
||||
log.error(e.getMessage());
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
/**
|
||||
* AES解密
|
||||
* @param data
|
||||
* @param key
|
||||
* @param iv
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decrptyAES_CBC(String data,String key,String iv){
|
||||
try {
|
||||
byte[] bytes = decoder.decode(data);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
|
||||
byte[] plainByte = cipher.doFinal(bytes);
|
||||
return new String(plainByte).trim();
|
||||
}catch (Exception e){
|
||||
log.error("AES解密失败");
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
* @param data
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encryptAES_ECB(String data,String key) throws Exception{
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
int blockSize = cipher.getBlockSize();
|
||||
byte[] dataBytes = data.getBytes();
|
||||
int dataLength = dataBytes.length;
|
||||
if(dataLength % blockSize != 0){
|
||||
dataLength = dataLength + (blockSize - (dataLength % blockSize));
|
||||
}
|
||||
byte [] plaintext = new byte[dataLength];
|
||||
System.arraycopy(dataBytes,0,plaintext,0,dataBytes.length);
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(),"AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE,secretKey);
|
||||
byte[] bytes = cipher.doFinal(plaintext);
|
||||
return encoder.encodeToString(bytes);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
* @param data
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decrptyAES_ECB(String data,String key) throws Exception{
|
||||
// byte[] bytes = decoder.decode(data);
|
||||
byte[] bytes =Base64.getDecoder().decode(data);
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(),"AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE,secretKey);
|
||||
byte[] plainByte = cipher.doFinal(bytes);
|
||||
return bytesToHex(plainByte);
|
||||
}
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gb2312编码
|
||||
*/
|
||||
public static String gb2312decode( String string) throws UnsupportedEncodingException{
|
||||
byte[] bytes = new byte[string.length() / 2];
|
||||
for(int i = 0; i < bytes.length; i ++){
|
||||
byte high = Byte.parseByte(string.substring(i * 2, i * 2 + 1), 16);
|
||||
byte low = Byte.parseByte(string.substring(i * 2 + 1, i * 2 + 2), 16);
|
||||
bytes[i] = (byte) (high << 4 | low);
|
||||
}
|
||||
return new String(bytes, "gb2312");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UTF8编码
|
||||
*/
|
||||
public static String UTF8decode( String string) throws UnsupportedEncodingException{
|
||||
byte[] bytes = new byte[string.length() / 2];
|
||||
for(int i = 0; i < bytes.length; i ++){
|
||||
byte high = Byte.parseByte(string.substring(i * 2, i * 2 + 1), 16);
|
||||
byte low = Byte.parseByte(string.substring(i * 2 + 1, i * 2 + 2), 16);
|
||||
bytes[i] = (byte) (high << 4 | low);
|
||||
}
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception{
|
||||
String encrypt = "IcDSUR8fdtJ8gLYlZX9qLw==";
|
||||
String str = decrptyAES_ECB(encrypt, key);
|
||||
// String str1 = str.substring(0, 16);
|
||||
String license = AESUtil.UTF8decode(str);
|
||||
// System.out.println(gb2312decode(str1));
|
||||
System.out.println(license);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.dromara.mica.mqtt.server.utils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
|
||||
public class Base64MultipartFile implements MultipartFile {
|
||||
|
||||
private final byte[] fileContent;
|
||||
private final String fileName;
|
||||
private final String contentType;
|
||||
|
||||
public Base64MultipartFile(String base64Data, String fileName) {
|
||||
// 清理Base64数据
|
||||
String cleanedBase64 = base64Data.contains(",")
|
||||
? base64Data.substring(base64Data.indexOf(",") + 1)
|
||||
: base64Data;
|
||||
|
||||
this.fileContent = Base64.getDecoder().decode(cleanedBase64);
|
||||
this.fileName = fileName;
|
||||
this.contentType = extractContentType(base64Data);
|
||||
}
|
||||
|
||||
private String extractContentType(String base64Data) {
|
||||
if (base64Data.startsWith("data:image/jpeg")) {
|
||||
return "image/jpeg";
|
||||
} else if (base64Data.startsWith("data:image/png")) {
|
||||
return "image/png";
|
||||
} else if (base64Data.startsWith("data:image/gif")) {
|
||||
return "image/gif";
|
||||
}
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "file";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalFilename() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return fileContent == null || fileContent.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return fileContent.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws IOException {
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(fileContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||
try (FileOutputStream fos = new FileOutputStream(dest)) {
|
||||
fos.write(fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.mica.mqtt.server.utils;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public class Base64ToMultipartFileUtil {
|
||||
|
||||
public static MultipartFile convertToMultipartFile(String base64Data, String fileName) {
|
||||
return new Base64MultipartFile(base64Data, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成文件名
|
||||
*/
|
||||
public static MultipartFile convertToMultipartFile(String base64Data) {
|
||||
String fileName = generateFileName(base64Data);
|
||||
return convertToMultipartFile(base64Data, fileName);
|
||||
}
|
||||
|
||||
private static String generateFileName(String base64Data) {
|
||||
String extension = ".jpg";
|
||||
if (base64Data.startsWith("data:image/png")) {
|
||||
extension = ".png";
|
||||
} else if (base64Data.startsWith("data:image/gif")) {
|
||||
extension = ".gif";
|
||||
}
|
||||
return "image_" + System.currentTimeMillis() + extension;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.mica.mqtt.server.utils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UuidUtil {
|
||||
|
||||
public static String[] chars = new String[] { "a", "b", "c", "d", "e", "f",
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
|
||||
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
|
||||
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
|
||||
"W", "X", "Y", "Z" };
|
||||
|
||||
|
||||
/**
|
||||
* 获取短UUID
|
||||
* @return
|
||||
*/
|
||||
public static String getShortUuid() {
|
||||
StringBuffer shortBuffer = new StringBuffer();
|
||||
String uuid = UuidUtil.getUuid();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
String str = uuid.substring(i * 4, i * 4 + 4);
|
||||
int x = Integer.parseInt(str, 16);
|
||||
shortBuffer.append(chars[x % 0x3E]); // 对62取余
|
||||
}
|
||||
return shortBuffer.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得32位UUID
|
||||
*/
|
||||
public static String getUuid(){
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
//去掉“-”符号
|
||||
return uuid.replaceAll("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: root
|
||||
data:
|
||||
redis:
|
||||
host: 192.168.2.30
|
||||
password: redis2025
|
||||
database: 5
|
||||
port: 6379
|
||||
@@ -0,0 +1,28 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
#xa
|
||||
# url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: root
|
||||
# password: Xahg2024.
|
||||
#jl
|
||||
# url: jdbc:mysql://127.0.0.1:3306/jl_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: root
|
||||
# password: JL202509jj
|
||||
#td
|
||||
# url: jdbc:mysql://127.0.0.1:3306/td_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: root
|
||||
# password: td@JJ2024
|
||||
#zr
|
||||
url: jdbc:mysql://192.168.155.42:3306/zr_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: zr202407.J
|
||||
data:
|
||||
redis:
|
||||
#zr
|
||||
host: 192.168.155.42
|
||||
#xa、jl、td
|
||||
# host: 127.0.0.1
|
||||
port: 6379
|
||||
password:
|
||||
database: 1
|
||||
129
system/mqtt-xf-receiver/src/main/resources/application.yml
Normal file
129
system/mqtt-xf-receiver/src/main/resources/application.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
server:
|
||||
port: 30013
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: mica-mqtt-server
|
||||
# 环境 dev|test|prod
|
||||
profiles:
|
||||
active: dev
|
||||
# active: prod
|
||||
messages:
|
||||
encoding: UTF-8
|
||||
basename: i18n/messages
|
||||
# jackson时间格式化
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ANT_PATH_MATCHER
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
enabled: true
|
||||
|
||||
#mybatis
|
||||
#mybatis-plus:
|
||||
# mapper-locations: classpath*:/mapper/**/*.xml
|
||||
# #实体扫描,多个package用逗号或者分号分隔
|
||||
# typeAliasesPackage: io.renren.entity
|
||||
# global-config:
|
||||
# #数据库相关配置
|
||||
# db-config:
|
||||
# #主键类型
|
||||
# id-type: ASSIGN_ID
|
||||
# banner: false
|
||||
# #原生配置
|
||||
# configuration:
|
||||
# map-underscore-to-camel-case: true
|
||||
# cache-enabled: false
|
||||
# call-setters-on-nulls: true
|
||||
# jdbc-type-for-null: 'null'
|
||||
# configuration-properties:
|
||||
# prefix:
|
||||
# blobType: BLOB
|
||||
# boolValue: TRUE
|
||||
|
||||
|
||||
|
||||
# mqtt 服务端配置
|
||||
mqtt:
|
||||
server:
|
||||
enabled: true # 是否开启服务端,默认:true
|
||||
name: Mica-Mqtt-Server # 名称,默认:Mica-Mqtt-Server
|
||||
heartbeat-timeout: 120000 # 心跳超时,单位毫秒,默认: 1000 * 120
|
||||
read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k
|
||||
max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M
|
||||
auth:
|
||||
enable: true # 是否开启 mqtt 认证
|
||||
username: admin # mqtt 认证用户名
|
||||
password: admin@123 # mqtt 认证密码
|
||||
debug: true # 如果开启 prometheus 指标收集建议关闭
|
||||
stat-enable: true # 开启指标收集,debug 和 prometheus 开启时需要打开,默认开启,关闭节省内存
|
||||
mqtt-listener: # mqtt 监听器
|
||||
enable: true # 是否开启,默认:false
|
||||
# ip: "0.0.0.0" # 服务端 ip 默认为空,0.0.0.0,建议不要设置
|
||||
port: 1883 # 端口,默认:1883
|
||||
mqtt-ssl-listener: # mqtt ssl 监听器
|
||||
enable: false # 是否开启,默认:false
|
||||
port: 8883 # 端口,默认:8883
|
||||
ssl: # ssl 配置,必须
|
||||
keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 必选参数:ssl keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要)
|
||||
ws-listener: # websocket mqtt 监听器
|
||||
enable: true # 是否开启,默认:false
|
||||
port: 8083 # websocket 端口,默认:8083
|
||||
wss-listener: # websocket ssl mqtt 监听器
|
||||
enable: false # 是否开启,默认:false
|
||||
port: 8084 # 端口,默认:8084
|
||||
ssl: # ssl 配置,必须
|
||||
keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。
|
||||
keystore-pass: # 必选参数:ssl keystore 密码
|
||||
truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。
|
||||
truststore-pass: # 可选参数:ssl 双向认证 truststore 密码
|
||||
client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要)
|
||||
http-listener:
|
||||
enable: true
|
||||
port: 18083
|
||||
basic-auth: # 基础认证
|
||||
enable: true
|
||||
username: mica
|
||||
password: mica
|
||||
mcp-server: # 大模型 mcp
|
||||
enable: true
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
urls:
|
||||
- name: swagger
|
||||
url: /v3/api-docs
|
||||
|
||||
# actuator management
|
||||
management:
|
||||
info:
|
||||
defaults:
|
||||
enabled: true
|
||||
metrics:
|
||||
tags:
|
||||
application: ${spring.application.name}
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ALWAYS
|
||||
prometheus:
|
||||
enabled: true
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
server: info # t-io 服务端默认日志
|
||||
org.tio: info # t-io 服务端默认日志
|
||||
org.dromara.mica.mqtt: info # mica-mqtt 日志
|
||||
12
system/mqtt-xf-receiver/src/main/resources/banner.txt
Normal file
12
system/mqtt-xf-receiver/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
${AnsiColor.BRIGHT_BLUE}## ## #### ###### ### ${AnsiColor.RED} ## ## ####### ######## ########
|
||||
${AnsiColor.BRIGHT_BLUE}### ### ## ## ## ## ## ${AnsiColor.RED} ### ### ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}#### #### ## ## ## ## ${AnsiColor.RED} #### #### ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ### ## ## ## ## ##${AnsiColor.RED} ## ### ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## ## ## #########${AnsiColor.RED} ## ## ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## ## ## ## ## ##${AnsiColor.RED} ## ## ## ## ## ##
|
||||
${AnsiColor.BRIGHT_BLUE}## ## #### ###### ## ##${AnsiColor.RED} ## ## ##### ## ## ##
|
||||
|
||||
https://www.dreamlu.net
|
||||
|
||||
${AnsiColor.BRIGHT_BLUE}:: ${spring.application.name} :: Running Spring Boot ${spring-boot.version} 🏃🏃🏃 ${AnsiColor.DEFAULT}
|
||||
63
system/pom.xml
Normal file
63
system/pom.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>org.dromara.mica-mqtt</groupId>
|
||||
<artifactId>mica-mqtt</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>system</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<!-- Spring Boot 3 需要 Java 17 -->
|
||||
<java.version>17</java.version>
|
||||
<!-- 覆盖父项目的 Spring Boot 版本 -->
|
||||
<spring.boot.version>4.0.0</spring.boot.version>
|
||||
<!-- mica 版本 -->
|
||||
<mica.version>4.0.0</mica.version>
|
||||
<!-- gson 版本 -->
|
||||
<gson.version>2.13.2</gson.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 使用 Spring Boot 3 的依赖管理 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- mica 相关依赖 -->
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-bom</artifactId>
|
||||
<version>${mica.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user