做在线智能 IoT 管理系统,最耗时间的往往不是设备接入协议,而是后台管理那套“通用功能”:用户登录、菜单权限、操作日志、数据字典、部门管理。这些功能每个项目都要重复写一遍,写起来又不能直接产生业务价值。如果你正在做物联网设备管理平台,或者准备用 Java 技术栈做毕业设计 / 课程设计 / 企业内部设备监控系统,本文会给你一条完整的实现路线:基于 RuoYi 框架 + Spring Boot + MySQL,配合 Idea 开发和 HTML/CSS 前端页面,快速搭出一个能用的在线智能 IoT 管理系统。
先说判断:RuoYi 这类快速开发平台,真正降低的是通用后台功能的开发成本,而不是 IoT 业务本身的复杂度。设备接入协议、数据上报链路、告警规则这些核心逻辑,依然需要你亲手设计。所以这篇文章会分成两条线推进:一条线讲怎么用 RuoYi 把用户、权限、菜单这些底座跑起来;另一条线讲怎么把 IoT 设备管理、数据上报、设备告警做成真正可落地的业务模块。两条线合在一起,才是一个完整的在线智能 IoT 管理系统。
1. 这篇文章真正要解决的问题
很多新手拿到 IoT 项目需求时,第一反应是:先研究 MQTT 协议,先写设备端代码,先做通信模块。这个顺序其实反了。
设备端只解决“数据怎么传上来”的问题,而一个完整的管理系统还要解决“数据传上来之后怎么办”。以我接触过的设备管理类项目为例,最常见的状态是:设备数据已经通过 MQTT 或 HTTP 上报到服务端,但后台没有用户体系,没有设备台账,没有历史数据查询页面,告警只能靠人工看日志。这就像家里装了一堆智能传感器,却没有一个统一控制面板,数据都在,但用不起来。
RuoYi 出现在这里,是为了解决“控制面板”的问题。它自带了一套成熟的后台管理基础功能:
- 用户管理、角色管理、菜单管理、部门管理
- 操作日志、登录日志、数据字典
- 代码生成器,可以根据数据库表自动生成 CRUD 代码
- 统一的前端页面框架和权限控制
而 IoT 业务本身,还需要你做四件事:
- 设计设备信息表、设备上报数据表、设备告警表
- 开放一个设备数据上报接口,接收设备端推上来的数据
- 提供设备管理页面,让运维人员看到所有设备的在线状态、基本信息、最新数据
- 设计告警规则,当温度、电压等指标超过阈值时,产生告警记录
这篇文章适合以下几类读者:准备用 Java 技术栈做 IoT 管理系统但不知道如何起步的人;想做毕设选题“在线智能物联网管理系统”的大学生;以及公司内部需要一个轻量级设备管理后台,但不想花三个月从零写权限系统的开发人员。
读完这篇文章,你会跑通这样一条完整链路:初始化 RuoYi 项目 → 设计设备相关数据表 → 用代码生成器生成业务 CRUD → 编写设备数据上报接口 → 开发设备管理前端页面 → 用模拟数据验证设备上报和在线状态更新。
2. 核心概念:RuoYi、Spring Boot、MySQL 在 IoT 系统中的分工
在动手之前,先把几个概念讲清楚,不然代码写完了可能还是不知道每一层在做什么。
2.1 RuoYi 到底是什么
RuoYi(若依)是一个基于 Spring Boot + Spring Security + MyBatis 的快速开发平台。你从官网下载项目后,默认就有用户管理、部门管理、菜单权限、登录认证这些功能。它的核心价值不是“多了一个框架要学”,而是帮你省掉了业务系统里最通用的那 30% 代码。
RuoYi 有两个常见版本:单体版本(后端 + Thymeleaf 模板页面)和前后端分离版本(RuoYi-Vue)。本文以单体版本为例来说明,因为项目标题里明确提到了 html/css,单体版本的页面能够直接用 Thymeleaf 模板和 HTML 渲染,理解起来更直观。前后端分离版本的思路完全一致,只是后端输出 JSON,前端用 Vue 做页面,业务表的 CRUD 设计没有本质区别。
有一个容易踩坑的理解误区:以为用了 RuoYi 就不用写代码了。实际上不是。RuoYi 提供的是基础底座和代码生成器,它可以生成实体类、Mapper、Service、Controller 的标准 CRUD 代码,但设备上报接口、告警判断逻辑、在线状态维护这些 IoT 业务逻辑,仍然需要自己写。
2.2 Spring Boot 在系统里的角色
Spring Boot 是这套系统的运行时骨架。它负责把数据库连接、Web 接口、事务管理、日志等组件组合起来,让开发人员少写大量 XML 配置。在 IoT 管理系统里,Spring Boot 的职责主要是:
- 提供 HTTP 接口,接收前端页面的请求
- 提供设备数据上报接口,接收设备端或模拟器推送的数据
- 协调 Service 层和 Mapper 层,完成数据库读写
- 集成 Spring Security,由 RuoYi 默认配置完成登录认证和权限控制
如果你之前只用 Spring Boot 写过传统 CRUD,那 IoT 系统在接口层并没有多出多少复杂性。真正的复杂性在数据模型设计:设备和数据是“一对多”的关系,每台设备会持续产生大量上报记录,这种数据形态和普通的订单表、用户表是不一样的。
2.3 MySQL 在 IoT 系统中的使用边界
MySQL 作为这套系统的核心存储,负责保存三类数据:RuoYi 框架自身的管理数据(用户、角色、菜单等)、业务基础数据(设备台账)、时序型数据(设备上报记录)。
这里有一个重要判断:如果设备数量少、上报频率低,比如家庭场景或小型工厂,MySQL 完全够用。但如果设备量达到几万台,每台设备每十秒上报一条数据,MySQL 很快会遇到写入压力和查询瓶颈。更合理的方案是引入时序数据库(如 TDengine、InfluxDB),或者对 MySQL 做分表归档。本文作为入门方案,先用 MySQL 把完整链路跑通,到了生产环境再根据数据量做演进。
2.4 一个典型 IoT 管理系统的分层架构
从整体架构看,这套在线智能 IoT 管理系统可以分成四层:
| 层级 | 技术选型 | 主要职责 |
|---|---|---|
| 设备接入层 | HTTP / MQTT | 接收设备上报的数据 |
| 应用服务层 | Spring Boot + RuoYi | 业务处理、权限控制、数据存储 |
| 数据存储层 | MySQL | 保存设备信息、上报数据、告警记录 |
| Web 展示层 | HTML / CSS / Thymeleaf | 设备列表、状态监控、历史数据展示 |
这个架构对初学者最友好的地方在于:每一层都可以单独调试。设备接入层先用 Postman 或模拟器测,应用服务层直接用浏览器访问接口,数据存储层用 MySQL 客户端查询,Web 展示层最后对接接口。分层清晰,排错也会容易很多。
3. 环境准备与前置条件
在开始搭建之前,先把开发环境准备好。以下版本以常见的稳定组合为例,如果你是直接使用 RuoYi 官网最新代码,版本号请以你下载的版本为准。
3.1 开发工具与运行环境
| 组件 | 建议版本 | 说明 |
|---|---|---|
| JDK | 1.8 或 17 | 老版本 RuoYi 建议 1.8,新版本建议 17 |
| IntelliJ IDEA | 2023 及以上 | 导入 Maven 项目更方便 |
| Maven | 3.6+ | 管理项目依赖 |
| MySQL | 5.7 或 8.0 | 建议 8.0,性能更好 |
| Redis | 5.0+ | RuoYi 部分配置可能需要,实际不强依赖 |
安装顺序建议:JDK → MySQL → Maven → IDEA。JDK 安装后,需要配置 JAVA_HOME 环境变量。MySQL 安装完成后,要记住 root 密码,并确保能通过命令行或客户端连接。
3.2 获取 RuoYi 项目
RuoYi 的下载和使用方式比较简单。你可以从项目官网获取源码,也可以通过 Maven 方式引入相关模块。项目是 Maven 多模块结构,典型的模块包括:
- ruoyi-admin:启动模块,包含启动类和 controller
- ruoyi-framework:框架核心,包括安全配置、拦截器
- ruoyi-system:系统管理模块,用户、角色、菜单等
- ruoyi-common:公共工具类
使用 IDEA 导入时,直接选择根目录的 pom.xml,让它以 Maven 项目方式加载。首次导入会下载大量依赖,国内网络环境下建议把 Maven 仓库地址配置为阿里云镜像,否则等待时间会很长。
3.3 数据库初始化
RuoYi 项目自带 SQL 脚本,包含了用户表、角色表、菜单表等基础数据。你需要先创建一个新的数据库,例如ry_iot,然后执行 RuoYi 项目的ry_xxx.sql脚本。执行完成后,表示 RuoYi 的基础表已经就绪。
有一点要特别提醒:RuoYi 的 SQL 脚本里默认带有字符集配置,建议统一使用utf8mb4,避免设备名称、位置描述等字段出现中文乱码。后面新增 IoT 业务表时,也保持这个习惯。
4. 核心数据表设计:设备、数据、告警
在线智能 IoT 管理系统的核心,不是页面,也不是接口,而是数据表设计。表设计一旦定下来,后端代码和前端页面基本就是围绕表结构展开的。
先明确实体关系:一台设备可以有多条上报数据、多条告警记录,所以设备表是主表,数据表和告警表是子表。
4.1 设备信息表 iot_device
设备表保存每台设备的静态信息。这里要注意区分“业务状态”和“在线状态”这两个字段:
- status(业务状态):表示设备是否被停用,与是否在线无关。
- online_status(在线状态):表示设备当前是否连接到服务器并保持上报。
这两个状态在实际项目里经常被混在一起,导致后续排错困难。设计时明确区分,后面写在线状态更新逻辑时会清晰很多。
CREATE TABLE iot_device ( device_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '设备ID', device_code varchar(64) NOT NULL COMMENT '设备唯一编码', device_name varchar(100) NOT NULL COMMENT '设备名称', device_type varchar(50) DEFAULT NULL COMMENT '设备类型,如温湿度传感器、电表', status char(1) DEFAULT '0' COMMENT '业务状态(0正常 1停用)', online_status char(1) DEFAULT '0' COMMENT '在线状态(0离线 1在线)', location varchar(200) DEFAULT NULL COMMENT '安装位置', last_report_time datetime DEFAULT NULL COMMENT '最后上报时间', create_by varchar(64) DEFAULT '' COMMENT '创建者', create_time datetime DEFAULT NULL COMMENT '创建时间', update_by varchar(64) DEFAULT '' COMMENT '更新者', update_time datetime DEFAULT NULL COMMENT '更新时间', remark varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (device_id), UNIQUE KEY uk_device_code (device_code) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='IoT设备信息表';device_code是设备唯一编码,在真实项目里一般是设备出厂时烧录的编号,或者是平台分配给设备的标识。这里设置了唯一索引,上报接口会根据这个编码定位设备。
4.2 设备上报数据表 iot_device_data
设备上报数据是 IoT 系统里增长最快的表。这类数据有几个特点:只插入、不修改、按时间查询。表设计上要特别关注索引,否则数据量一大,按设备和时间查历史数据会非常慢。
CREATE TABLE iot_device_data ( data_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '数据ID', device_id bigint(20) NOT NULL COMMENT '设备ID', temperature decimal(8,2) DEFAULT NULL COMMENT '温度值', humidity decimal(8,2) DEFAULT NULL COMMENT '湿度值', voltage decimal(8,2) DEFAULT NULL COMMENT '电压值', report_time datetime NOT NULL COMMENT '上报时间', PRIMARY KEY (data_id), KEY idx_device_time (device_id, report_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备上报数据表';这里使用idx_device_time复合索引,是为了适配最常见的查询场景:查看某台设备在一段时间内的历史数据。如果业务需要按设备类型维度的汇总统计,可以考虑增加设备类型冗余字段,或者定期做聚合表,不要在原始数据表上做太复杂的统计查询。
4.3 设备告警表 iot_alarm
告警表用来记录设备指标超出阈值的情况。实际项目中,告警触发逻辑可以在设备端判断,也可以在服务端判断。对于智能 IoT 管理系统,推荐在服务端统一判断,因为这样规则可以动态调整,不用每次更新设备固件。
CREATE TABLE iot_alarm ( alarm_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '告警ID', device_id bigint(20) NOT NULL COMMENT '设备ID', alarm_type varchar(50) DEFAULT NULL COMMENT '告警类型,如超温、欠压', alarm_content varchar(500) DEFAULT NULL COMMENT '告警内容', status char(1) DEFAULT '0' COMMENT '处理状态(0未处理 1已处理)', alarm_time datetime DEFAULT NULL COMMENT '告警时间', PRIMARY KEY (alarm_id), KEY idx_alarm_status (status) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备告警表';告警表和上报数据表本质上是两种类型的记录:数据表是设备“说了什么”,告警表是系统“发现了什么问题”。在实际开发中,上报数据每一条都会插入,但只有满足条件的上报才会插入告警记录,所以告警表的数据量会小很多。
5. 基于 RuoYi 的后端业务代码实现
数据表设计完成后,开始写后端代码。RuoYi 自带代码生成器,但对于 IoT 业务表,最稳妥的方式还是先手动写一遍核心链路,理解清楚以后再用生成器提高后续效率。
5.1 在 RuoYi 项目中添加设备模块的 Maven 依赖
如果你的 RuoYi 项目是多模块结构,可以在 ruoyi-admin 模块的 pom.xml 中添加快 JSON 解析库,用于处理上报接口的 JSON 数据。RuoYi 本身已经集成了 fastjson,通常不用额外添加,但如果版本较老,可以使用 fastjson2:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson2</artifactId> <version>2.0.36</version> </dependency>实际开发中,这个依赖是否添加取决于项目里已有的依赖树。如果代码里用到了 fastjson 的 JSONObject,但没有相关依赖,就会出现编译报错。建议先检查依赖树,再决定是否添加。
5.2 配置数据库连接
在 ruoyi-admin 模块的 application.yml 中修改数据源配置,指向刚创建的ry_iot数据库:
server: port: 8080 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ry_iot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 123456这里最容易出的问题是serverTimezone配置错误。MySQL 8.0 连接时,如果 URL 不指定时区,JDBC 驱动会报时区错误。另外,characterEncoding=utf8是避免中文乱码的关键配置,不要漏掉。
5.3 编写设备实体类
在业务模块下新建com.iot.device.domain.IotDevice实体类。RuoYi 的实体类继承BaseEntity,可以复用创建时间、更新时间、备注等公共字段:
package com.iot.device.domain; import com.ruoyi.common.core.domain.BaseEntity; public class IotDevice extends BaseEntity { private Long deviceId; private String deviceCode; private String deviceName; private String deviceType; private String status; private String onlineStatus; private String location; private String lastReportTime; public Long getDeviceId() { return deviceId; } public void setDeviceId(Long deviceId) { this.deviceId = deviceId; } public String getDeviceCode() { return deviceCode; } public void setDeviceCode(String deviceCode) { this.deviceCode = deviceCode; } // 其他 getter / setter 省略,由 IDEA 快捷生成即可 }这里的lastReportTime虽然是数据库的 datetime 类型,在实体类中既可以用Date也可以用String,RuoYi 的代码生成器默认会处理类型转换。为了减少前端格式转换的麻烦,这里示例使用 String,实际项目中建议统一用 Date,在返回 JSON 时配置格式化规则。
5.4 编写设备数据上报接口
设备数据上报是 IoT 系统的关键接口。它要做三件事:
- 根据 deviceCode 找到设备。
- 插入一条设备上报数据。
- 更新设备的最后上报时间和在线状态。
另外,还要判断温度、电压是否超过阈值,如果超过则写入告警表,方便运维人员处理。
package com.iot.device.controller; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.alibaba.fastjson2.JSONObject; import com.iot.device.domain.IotDevice; import com.iot.device.service.IotDeviceService; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; @RestController @RequestMapping("/iot/device") public class IotDeviceReportController extends BaseController { @Autowired private IotDeviceService iotDeviceService; @PostMapping("/report") public AjaxResult report(@RequestBody JSONObject body) { String deviceCode = body.getString("deviceCode"); // 1. 根据设备编码查找设备 IotDevice device = iotDeviceService.selectIotDeviceByCode(deviceCode); if (device == null) { return AjaxResult.error("设备不存在:" + deviceCode); } // 2. 记录上报数据 double temperature = body.getDoubleValue("temperature"); double humidity = body.getDoubleValue("humidity"); double voltage = body.getDoubleValue("voltage"); iotDeviceService.insertDeviceData(device.getDeviceId(), temperature, humidity, voltage); // 3. 更新设备在线状态和最后上报时间 iotDeviceService.updateOnlineStatus(device.getDeviceId(), new Date()); // 4. 判断阈值并写入告警 if (temperature > 60) { iotDeviceService.insertAlarm(device.getDeviceId(), "超温", "当前温度:" + temperature); } if (voltage < 180) { iotDeviceService.insertAlarm(device.getDeviceId(), "欠压", "当前电压:" + voltage); } return AjaxResult.success("上报成功"); } }这里把阈值写死在代码里,只是为了演示完整链路。在实际工程项目中,阈值应该放到配置中心、数据库或 Redis 中,方便运行时动态修改。告警判断逻辑也应该独立成告警服务,避免上报接口过于臃肿。
5.5 编写 Service 和 Mapper 方法
Service 层需要实现几个核心方法:按编码查设备、插入数据、更新在线状态、插入告警。以插入上报数据为例,Mapper 层的 SQL 是这样写的:
<insert id="insertDeviceData"> INSERT INTO iot_device_data(device_id, temperature, humidity, voltage, report_time) VALUES(#{deviceId}, #{temperature}, #{humidity}, #{voltage}, #{reportTime}) </insert>更新在线状态和最后上报时间:
<update id="updateOnlineStatus"> UPDATE iot_device SET online_status = '1', last_report_time = #{reportTime} WHERE device_id = #{deviceId} </update>这里涉及一个很关键的业务判断:什么时候把设备状态置为离线?如果设备异常断电,它可能不会再上报数据,最后一次上报时间是 10 分钟前,如何判定它已经离线?
最常见的做法是增加一个定时任务,比如每分钟扫描一次设备表,把“最后上报时间早于当前时间 5 分钟”的设备置为离线。RuoYi 自带定时任务框架,可以在系统监控里配置一个定时任务,调用离线检测的 Service 方法,这是一个完整 IoT 系统必须考虑的问题。
5.6 设备管理列表接口
设备管理页面需要一个分页列表接口。RuoYi 的 BaseController 提供了startPage()方法,配合 MyBatis 的分页插件,可以很简洁地实现:
@GetMapping("/list") public TableDataInfo list(IotDevice device) { startPage(); List<IotDevice> list = iotDeviceService.selectIotDeviceList(device); return getDataTable(list); }这个接口返回的是 RuoYi 约定的分页格式,前端表格组件可以直接拿到total和rows,不需要自己拼接 JSON。
6. 前端页面:设备管理列表与状态展示
RuoYi 单体版本的前端页面使用 Thymeleaf 模板引擎,HTML 文件放在resources/templates/iot/device.html目录下。页面本身使用 Layui 或简单 JavaScript 渲染都可以,这里给出一个简洁的例子,方便你理解页面如何与后端接口交互。
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>IoT 设备管理</title> <link rel="stylesheet" href="/static/css/layui.css"> </head> <body> <div class="layui-container"> <h2>在线智能 IoT 设备列表</h2> <table class="layui-table" id="deviceTable"> <thead> <tr> <th>设备编码</th> <th>设备名称</th> <th>设备类型</th> <th>在线状态</th> <th>最后上报时间</th> <th>安装位置</th> </tr> </thead> <tbody id="deviceTbody"></tbody> </table> </div> <script src="/static/js/jquery.min.js"></script> <script> $(function() { $.get('/iot/device/list', function(res) { if (res.code === 0) { var rows = res.rows; var html = ''; for (var i = 0; i < rows.length; i++) { var row = rows[i]; var status = row.onlineStatus === '1' ? '在线' : '离线'; html += '<tr>'; html += '<td>' + row.deviceCode + '</td>'; html += '<td>' + row.deviceName + '</td>'; html += '<td>' + row.deviceType + '</td>'; html += '<td>' + status + '</td>'; html += '<td>' + row.lastReportTime + '</td>'; html += '<td>' + (row.location || '-') + '</td>'; html += '</tr>'; } $('#deviceTbody').html(html); } }); }); </script> </body> </html>这里有一个新手经常踩的坑:RuoYi 默认情况下,所有接口都需要登录后才能访问。前端页面发起$.get('/iot/device/list')时,如果当前会话没有登录态,请求会被拦截并重定向到登录页,返回的数据就不是 JSON。
解决办法有两种:第一,在浏览器里先访问 RuoYi 的登录页,登录成功后再打开设备管理页面;第二,参考 RuoYi 的权限配置,给/iot/**的访问接口配置对应的权限标识,再在用户管理中为账号分配权限菜单。无论哪种方式,都要理解 RuoYi “先过后端权限认证,再访问业务接口”的基本逻辑。
7. 运行结果与效果验证
后端和前端代码完成后,进入验证阶段。验证的目标是:跑通“模拟设备上报 → 更新设备状态 → 页面看到结果”的完整链路。
7.1 启动项目
在 IDEA 中启动 ruoyi-admin 模块的启动类,控制台出现Started RuoYiApplication时,表示后端启动成功。启动过程中如果出现端口被占用,可以修改 application.yml 中的server.port。
7.2 准备一条测试设备
在 iot_device 表中手动插入一台设备:
INSERT INTO iot_device(device_code, device_name, device_type, status, online_status, location) VALUES('DEV001', '车间温度传感器', '温湿度传感器', '0', '0', '一号车间');这一步非常重要:设备表里如果没有这条记录,上报接口会直接返回“设备不存在”。
7.3 使用 curl 或工具模拟设备上报
先用最简单的 curl 命令模拟一次设备上报:
curl -X POST http://localhost:8080/iot/device/report \ -H "Content-Type: application/json" \ -d '{"deviceCode":"DEV001","temperature":25.6,"humidity":58.2,"voltage":220.3}'如果返回结果包含"msg": "上报成功",说明接口逻辑已经走通。此时查询 iot_device 表,会看到online_status变为 1,last_report_time变成了当前时间。再查 iot_device_data 表,会多出一条上报记录。
7.4 验证告警触发
再执行一次模拟上报,把温度改成 80:
curl -X POST http://localhost:8080/iot/device/report \ -H "Content-Type: application/json" \ -d '{"deviceCode":"DEV001","temperature":80,"humidity":58.2,"voltage":220.3}'查看 iot_alarm 表,应该能看到一条“超温”告警记录。
7.5 验证页面展示
打开设备管理页面,如果页面能正常显示设备列表,并且看到 DEV001 的在线状态是“在线”,说明后端接口、前端渲染、数据库更新这条链路全部跑通。
如果页面出现数据为空,优先打开浏览器开发者工具的 Network 面板,查看/iot/device/list这个请求返回了什么。如果返回的是 401 或跳转登录页,说明权限没有配置好,先走登录流程再测试。
8. 常见问题与排查思路
在基于 RuoYi 开发 IoT 管理系统的过程中,以下问题出现频率很高:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 页面调用接口返回 401 | 未登录或接口没有配置权限 | 打开浏览器 Network 查看请求状态 | 先登录,或为接口配置权限标识 |
| 设备上报接口返回“设备不存在” | device_code 不匹配 | 查询 iot_device 表确认编码 | 检查上报 JSON 中的 deviceCode |
| 中文乱码 | 数据库连接 URL 缺少 characterEncoding | 检查 application.yml | 添加characterEncoding=utf8 |
| 时间字段相差 8 小时 | JDBC 时区配置不对 | 查询数据库时间与本地时间 | URL 添加serverTimezone=GMT%2B8 |
| 启动时报数据源错误 | MySQL 未启动或密码错误 | 查看完整堆栈日志 | 确认 MySQL 服务和账号密码 |
| 设备在线状态一直不变 | 缺少离线扫描定时任务 | 观察 last_report_time | 增加定时任务做超时离线判断 |
这里单独解释一下 401 的问题。RuoYi 自带 Spring Security,默认所有接口都受保护。很多人刚接触时,在浏览器地址栏输入接口地址,被重定向到登录页,就以为后端写错了。实际这是正常现象,只要登录后携带会话访问即可。
另一个容易忽略的问题是分页参数。RuoYi 的startPage()默认读取pageNum和pageSize参数,如果你的前端请求没有传这两个参数,接口会返回全部数据。虽然功能没错,但设备数量多了以后会拖慢接口响应,建议在开发阶段就统一使用 RuoYi 的分页组件。
9. 最佳实践与工程建议
当整个 IoT 管理系统跑通以后,不要急着加新功能,先考虑下面这些工程层面的建议。它们决定了这个系统能不能从“demo”变成“能用的后台”。
9.1 设备编码规范
设备编码(device_code)是设备在系统中的唯一身份证,设计时要有全局视角。建议采用分段编码方式,例如:
- 前 2 位代表设备类型:WD 表示温度、DL 表示电力
- 中间 3 位代表区域编码
- 最后几位代表设备序号
这样查日志、做报表、定位设备都能节省不少时间。
9.2 上报接口要做幂等和校验
设备端可能因为网络抖动而重复上报数据,上报接口如果每次都直接 insert,会导致数据量膨胀。可以设计一个去重字段,比如“设备编码 + 上报时间戳”,在插入前先查重。同时,接口参数要做合法性校验,防止设备异常时上报负数温度或空值,污染历史数据。
9.3 在线状态不要依赖设备主动断线
IoT 设备不具备稳定的“断线通知”能力,设备异常断电后,可能永远不会告知服务器。所以在线状态必须依靠超时判断维护。建议设计一个定时任务,周期扫描在线设备,将最后上报时间超过 5 分钟的设备置为离线。在 RuoYi 中,可以直接使用系统自带的定时任务功能,不需要额外引入 Quartz。
9.4 告警规则要可配置
不要在代码里写死阈值。阈值应该保存在配置表里,通过管理页面维护。比如温度超过多少触发“超温告警”,电压低于多少触发“欠压告警”。这样运维人员调整阈值时,不需要重新开发和发布服务。这就是在线智能 IoT 系统里“智能”二字的实际含义。
9.5 历史数据归档
设备上报数据会持续增长,建议从一开始就规划清理策略。最简单的方式是保留近三个月的明细数据,更早的数据定期迁移到归档表;如果业务允许,可以用定时任务删除。对于更复杂的统计需求,可以按天汇总设备数据的平均值、最大值、最小值,需要查询汇总趋势时直接读汇总表。
9.6 权限与安全边界
设备管理后台虽然内部使用,也要注意权限边界。RuoYi 默认提供了角色和菜单权限,建议按角色划分数据权限,比如普通运维人员只能查看设备列表和告警,管理员才能删除设备、调整告警阈值。涉及到生产环境的设备操作指令下发,不要直接暴露给前端,必须增加二次确认和操作日志记录。
9.7 代码生成器的使用时机
当你理解了实体、Mapper、Service、Controller 的写法之后,后续新加一张业务表,就可以使用 RuoYi 的代码生成器,从表结构直接生成标准 CRUD 代码。生成后再根据业务手动改造,效率会高很多。但第一张表,我仍然建议手写一遍,因为你只有理解了底层代码结构,才能在生成器生成的代码上做定制,否则出了问题都不知道从哪查起。
10. 总结与后续学习方向
到这里,一个基于 RuoYi + Spring Boot + MySQL 的在线智能 IoT 管理系统已经完整跑通了。你掌握了几个关键设计:设备和上报数据的一对多关系、在线状态的超时维护思路、上报接口的告警判断逻辑,以及 RuoYi 权限体系对业务接口的影响。
下一步,建议你从两个方向继续深入:一是把模拟上报替换成真实设备接入,可以了解 MQTT 协议与 EMQX 这类消息中间件,让设备通过 MQTT 上报数据,服务端订阅并写库;二是完善告警通知,当前告警只是写进了数据库,如果业务需要,可以扩展成短信、邮件或企业微信通知,这部分和 Spring Boot 的异步任务、消息队列结合得很紧密。
如果你是在做毕业设计或课程设计,基于这套系统,还可以继续扩展大屏数据可视化、设备地图定位、历史曲线报表等功能,技术栈不会变,核心还是在数据模型和接口设计上想清楚。建议先打开 IDEA,把这一套流程跑一遍,再回来看本文的设备表设计部分,会更有体会。