浏览代码

项目初始化

Yumin 6 年之前
当前提交
9fe43e50f9

+ 29 - 0
.gitignore

@@ -0,0 +1,29 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
+
+### VS Code ###
+.vscode/

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+## IoT
+
+基于 MQTT 协议通讯的物联网后端服务

+ 128 - 0
pom.xml

@@ -0,0 +1,128 @@
+<?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.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.1.3.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>cn.minbb</groupId>
+    <artifactId>IoT</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>IoT</name>
+    <description>Iot project for Spring Boot</description>
+    <packaging>war</packaging>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <!-- Tomcat -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-tomcat</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        
+        <!-- Web -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- MySQL -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- JPA -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+
+        <!-- 模板引擎 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <!-- 避免 layout:fragment 失效 -->
+        <dependency>
+            <groupId>nz.net.ultraq.thymeleaf</groupId>
+            <artifactId>thymeleaf-layout-dialect</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+
+        <!-- Swagger2 -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.7.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.7.0</version>
+        </dependency>
+
+        <!-- FastJSON -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.38</version>
+        </dependency>
+
+        <!-- MQTT -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-integration</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-stream</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.integration</groupId>
+            <artifactId>spring-integration-mqtt</artifactId>
+        </dependency>
+
+        <!-- 热部署-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 24 - 0
src/main/java/cn/minbb/iot/IoTApplication.java

@@ -0,0 +1,24 @@
+package cn.minbb.iot;
+
+import org.springframework.boot.Banner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableScheduling
+@SpringBootApplication
+public class IoTApplication extends SpringBootServletInitializer {
+
+    public static void main(String[] args) {
+        SpringApplication application = new SpringApplication(IoTApplication.class);
+        application.setBannerMode(Banner.Mode.CONSOLE);
+        application.run(args);
+    }
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+        return builder.sources(IoTApplication.class);
+    }
+}

+ 37 - 0
src/main/java/cn/minbb/iot/Swagger2.java

@@ -0,0 +1,37 @@
+package cn.minbb.iot;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class Swagger2 {
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("cn.minbb.iot.controller.rest"))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("Spring Boot 中使用 Swagger2 构建RESTful APIs")
+                .description("主页:https://www.minbb.cn/")
+                .termsOfServiceUrl("https://www.minbb.cn/")
+                .contact(new Contact("Yumin", "https://www.minbb.cn/", "yumin@minbb.cn"))
+                .version("1.0")
+                .build();
+    }
+}

+ 6 - 0
src/main/java/cn/minbb/iot/config/Const.java

@@ -0,0 +1,6 @@
+package cn.minbb.iot.config;
+
+public class Const {
+    public static final String MQTT_TOPIC_ALL = "/iot/all";
+    public static final String MQTT_TOPIC_CAR = "/iot/car";
+}

+ 113 - 0
src/main/java/cn/minbb/iot/config/MqttSenderConfig.java

@@ -0,0 +1,113 @@
+package cn.minbb.iot.config;
+
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.core.MessageProducer;
+import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
+import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
+import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
+import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
+import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+/**
+ * MQTT消息推送配置
+ */
+@Configuration
+@IntegrationComponentScan
+public class MqttSenderConfig {
+
+    private Logger logger = LoggerFactory.getLogger(MqttSenderConfig.class);
+
+    @Value("${spring.mqtt.username}")
+    private String username;
+
+    @Value("${spring.mqtt.password}")
+    private String password;
+
+    @Value("${spring.mqtt.url}")
+    private String hostUrl;
+
+    @Value("${spring.mqtt.client.id}")
+    private String clientId;
+
+    @Value("${spring.mqtt.default.topic}")
+    private String defaultTopic;
+
+    @Value("${spring.mqtt.completionTimeout}")
+    private int completionTimeout;
+
+    @Bean
+    public MqttConnectOptions getMqttConnectOptions() {
+        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
+        mqttConnectOptions.setUserName(username);
+        mqttConnectOptions.setPassword(password.toCharArray());
+        mqttConnectOptions.setServerURIs(new String[]{hostUrl});
+        mqttConnectOptions.setKeepAliveInterval(2);
+        return mqttConnectOptions;
+    }
+
+    @Bean
+    public MqttPahoClientFactory mqttClientFactory() {
+        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
+        factory.setConnectionOptions(getMqttConnectOptions());
+        return factory;
+    }
+
+    @Bean
+    @ServiceActivator(inputChannel = "mqttOutboundChannel")
+    public MessageHandler mqttOutbound() {
+        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, mqttClientFactory());
+        messageHandler.setAsync(true);
+        messageHandler.setDefaultTopic(defaultTopic);
+        return messageHandler;
+    }
+
+    @Bean
+    public MessageChannel mqttOutboundChannel() {
+        return new DirectChannel();
+    }
+
+    /**
+     * 接收通道
+     */
+    @Bean
+    public MessageChannel mqttInputChannel() {
+        return new DirectChannel();
+    }
+
+    // 配置 client,监听 topic
+    @Bean
+    public MessageProducer inbound() {
+        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
+                clientId + "_inbound", mqttClientFactory(),
+                defaultTopic,
+                Const.MQTT_TOPIC_ALL, Const.MQTT_TOPIC_CAR);
+        adapter.setCompletionTimeout(completionTimeout);
+        adapter.setConverter(new DefaultPahoMessageConverter());
+        adapter.setQos(1);
+        adapter.setOutputChannel(mqttInputChannel());
+        return adapter;
+    }
+
+    // 通过通道获取数据
+    @Bean
+    @ServiceActivator(inputChannel = "mqttInputChannel")
+    public MessageHandler handler() {
+        return message -> {
+            Object object = message.getHeaders().get("mqtt_receivedTopic");
+            String topic = null != object ? object.toString() : "";
+            String type = topic.substring(topic.lastIndexOf("/") + 1, topic.length());
+            String order = message.getPayload().toString();
+            logger.info("收到消息 = 主题 = {}  信息 = {}", topic, order);
+        };
+    }
+}

+ 16 - 0
src/main/java/cn/minbb/iot/controller/rest/IoTController.java

@@ -0,0 +1,16 @@
+package cn.minbb.iot.controller.rest;
+
+import cn.minbb.iot.service.MqttGateway;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping
+public class IoTController {
+
+    private MqttGateway mqttGateway;
+
+    public IoTController(MqttGateway mqttGateway) {
+        this.mqttGateway = mqttGateway;
+    }
+}

+ 17 - 0
src/main/java/cn/minbb/iot/controller/web/MapController.java

@@ -0,0 +1,17 @@
+package cn.minbb.iot.controller.web;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+@RequestMapping
+public class MapController {
+
+    @GetMapping
+    public ModelAndView map(ModelAndView modelAndView) {
+        modelAndView.setViewName("map");
+        return modelAndView;
+    }
+}

+ 16 - 0
src/main/java/cn/minbb/iot/service/MqttGateway.java

@@ -0,0 +1,16 @@
+package cn.minbb.iot.service;
+
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+@Component
+@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
+public interface MqttGateway {
+    void sendToMqtt(String data);
+
+    void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic);
+
+    void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos);
+}

+ 24 - 0
src/main/java/cn/minbb/iot/task/AfterServiceStarted.java

@@ -0,0 +1,24 @@
+package cn.minbb.iot.task;
+
+import cn.minbb.iot.util.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AfterServiceStarted implements ApplicationRunner {
+
+    @Value("${server.port}")
+    private int port;
+
+    private Logger logger = LoggerFactory.getLogger(AfterServiceStarted.class);
+
+    @Override
+    public void run(ApplicationArguments args) {
+        // 服务启动完成后立即执行
+        logger.info("{} --- 启动成功", Application.getCurrentStringTime());
+    }
+}

+ 30 - 0
src/main/java/cn/minbb/iot/task/AutoConfig.java

@@ -0,0 +1,30 @@
+package cn.minbb.iot.task;
+
+import cn.minbb.iot.config.Const;
+import cn.minbb.iot.service.MqttGateway;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AutoConfig {
+
+    private Logger logger = LoggerFactory.getLogger(AutoConfig.class);
+
+    private MqttGateway mqttGateway;
+
+    public AutoConfig(MqttGateway mqttGateway) {
+        this.mqttGateway = mqttGateway;
+    }
+
+    /**
+     * 从程序启动开始,每间隔 60s 执行一次
+     * 服务端心跳广播,延时 1 秒
+     */
+    @Scheduled(fixedRate = 60000, initialDelay = 1000)
+    public void scheduled() {
+        logger.info("Scheduled");
+        mqttGateway.sendToMqtt("*", Const.MQTT_TOPIC_ALL, 2);
+    }
+}

+ 31 - 0
src/main/java/cn/minbb/iot/util/Application.java

@@ -0,0 +1,31 @@
+package cn.minbb.iot.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class Application {
+
+    private Application() {
+    }
+
+    private static Application application;
+
+    public static Application getApplication() {
+        if (application == null) {
+            // 线程安全
+            synchronized (Application.class) {
+                application = new Application();
+            }
+        }
+        return application;
+    }
+
+    // 获取系统当前时间
+    public static String getCurrentStringTime() {
+        return SimpleDateFormat.getDateTimeInstance().format(getCurrentDateTime());
+    }
+
+    public static Date getCurrentDateTime() {
+        return new Date();
+    }
+}

+ 6 - 0
src/main/resources/application-dev.properties

@@ -0,0 +1,6 @@
+# ¿ª·¢»·¾³
+server.servlet.context-path=
+# MySQL
+spring.datasource.url=jdbc:mysql://127.0.0.1:3306/iot?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT
+spring.datasource.username=Yumin
+spring.datasource.password=Wang19970305

+ 6 - 0
src/main/resources/application-pro.properties

@@ -0,0 +1,6 @@
+# Éú²ú»·¾³
+server.servlet.context-path=
+# MySQL
+spring.datasource.url=jdbc:mysql://www.minbb.cn:3306/iot?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT
+spring.datasource.username=Yumin
+spring.datasource.password=Wang19970305

+ 38 - 0
src/main/resources/application.properties

@@ -0,0 +1,38 @@
+server.port=80
+# 指定运行环境
+spring.profiles.active=dev
+server.servlet.context-path=
+spring.mvc.favicon.enabled=true
+spring.http.encoding.charset=UTF-8
+spring.jmx.default-domain=iot
+spring.main.allow-bean-definition-overriding=true
+# MySQL
+spring.datasource.url=
+spring.datasource.username=
+spring.datasource.password=
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+spring.datasource.sql-script-encoding=UTF-8
+# JPA
+spring.jpa.database=mysql
+spring.jpa.show-sql=false
+spring.jpa.properties.hibernate.hbm2ddl.auto=update
+spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
+# 开启懒加载
+spring.jpa.open-in-view=true
+# 模板引擎
+spring.thymeleaf.mode=HTML
+spring.thymeleaf.cache=false
+spring.thymeleaf.encoding=UTF-8
+# MQTT 配置信息
+# MQTT - 用户名
+spring.mqtt.username=yumin
+# MQTT - 密码
+spring.mqtt.password=minbb.cn
+# MQTT - 服务器连接地址,如果有多个,用逗号隔开,如:tcp://127.0.0.1:61613,tcp://192.168.2.133:61613
+spring.mqtt.url=tcp://123.207.151.92:1600
+# MQTT - 连接服务器默认客户端 ID
+spring.mqtt.client.id=IoTServer
+# MQTT - 默认的消息推送主题,实际可在调用接口时指定
+spring.mqtt.default.topic=topic
+# MQTT - 最大连接超时时间
+spring.mqtt.completionTimeout=3000

+ 4 - 0
src/main/resources/banner.txt

@@ -0,0 +1,4 @@
+
+
+IoT - 物联网后端服务
+

+ 74 - 0
src/main/resources/templates/map.html

@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8"/>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
+    <title>设备位置信息</title>
+
+    <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=Z2045g3IUDzxUdIWhvjRi2IqMQTYXWrX"></script>
+    <script type="text/javascript" src="http://api.map.baidu.com/library/CurveLine/1.5/src/CurveLine.min.js"></script>
+    <style type="text/css">
+        body, html {
+            width: 100%;
+            height: 100%;
+            margin: 0;
+            font-family: "微软雅黑";
+        }
+
+        #map {
+            width: 100%;
+            height: 100%;
+        }
+
+        p {
+            margin-left: 5px;
+            font-size: 14px;
+        }
+
+        .BMapLabel {
+            border-radius: 20px 20px 20px 20px;
+        }
+    </style>
+</head>
+<body>
+<div id="map"></div>
+
+<script type="text/javascript">
+    var top_left_control = new BMap.ScaleControl({anchor: BMAP_ANCHOR_TOP_LEFT});// 左上角,添加比例尺
+    var top_left_navigation = new BMap.NavigationControl();  //左上角,添加默认缩放平移控件
+    var top_right_navigation = new BMap.NavigationControl({anchor: BMAP_ANCHOR_TOP_RIGHT, type: BMAP_NAVIGATION_CONTROL_SMALL}); //右上角,仅包含平移和缩放按钮
+    /*缩放控件type有四种类型:
+    BMAP_NAVIGATION_CONTROL_SMALL:仅包含平移和缩放按钮;BMAP_NAVIGATION_CONTROL_PAN:仅包含平移按钮;BMAP_NAVIGATION_CONTROL_ZOOM:仅包含缩放按钮*/
+    // 百度地图 API 功能
+    var map = new BMap.Map("map");
+    // 初始化地图,设置中心点坐标和地图级别
+    var point = new BMap.Point(116.404, 39.915);
+    map.centerAndZoom(point, 18);
+    // 添加地图类型控件
+    map.addControl(new BMap.MapTypeControl({mapTypes: [BMAP_NORMAL_MAP, BMAP_HYBRID_MAP]}));
+    map.setCurrentCity("郑州");          // 设置地图显示的城市 此项是必须设置的
+    map.enableScrollWheelZoom(true);     // 开启鼠标滚轮缩放
+    map.addControl(top_left_control);
+    map.addControl(top_left_navigation);
+    map.addControl(top_right_navigation);
+
+    var icon = new BMap.Icon("https://www.easyicon.net/api/resizeApi.php?id=1223994&size=32", new BMap.Size(64, 64));
+    var marker = new BMap.Marker(point, {icon: icon});  // 创建标注
+    var label = new BMap.Label("物联网汽车:" + new Date(), {offset: new BMap.Size(40, 10)});
+    label.setStyle({
+        color: "#2b2b2b",
+        fontSize: "14px",
+        backgroundColor: "rgba(255,255,255,1.0)",
+        border: "1px solid rgb(43, 43, 43)",
+        fontWeight: "normal",
+        padding: "2px 8px 2px 8px"
+    });
+
+    marker.setLabel(label);                 // 创建标注的文字标签
+    map.addOverlay(marker);                 // 将标注添加到地图中
+    var circle = new BMap.Circle(point, 160, {fillColor: "gray", strokeWeight: 1, fillOpacity: 0.3, strokeOpacity: 0.3});
+    map.addOverlay(circle);
+</script>
+</body>
+</html>

+ 16 - 0
src/test/java/cn/minbb/iot/IotApplicationTests.java

@@ -0,0 +1,16 @@
+package cn.minbb.iot;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class IotApplicationTests {
+
+    @Test
+    public void contextLoads() {
+    }
+
+}