基于Spring Boot的冷链监控平台开发指南:物联网数据采集与实时告警实现

📅 2026/7/5 11:18:15 👁️ 阅读次数 📝 编程学习
基于Spring Boot的冷链监控平台开发指南:物联网数据采集与实时告警实现

在实际计算机专业毕业设计开发过程中,很多同学面临的第一个难题不是写代码,而是选题和寻找一个结构清晰、技术栈主流、能跑通、有参考价值的项目源码。一个名为“冷链监控平台温控系统”的题目,结合了物联网数据采集、实时监控、数据可视化等热门技术点,是典型的软硬件结合或纯软件模拟的优秀毕设选题。本文将围绕如何从零开始理解和实现一个简化版的冷链监控平台,重点阐述其核心架构、技术选型(Java/Spring Boot)、关键模块实现,并提供一套可复现的开发、部署与排错指南。无论你是需要完成Java、Python、PHP或Node.js等不同技术栈的毕设,还是想深入理解物联网平台后端开发,本文提供的设计思路和工程实践都能为你提供清晰的路径。

1. 理解冷链监控平台的核心业务与架构

冷链监控平台的核心目标是确保特定环境(如冷库、冷藏车、医药冰箱)的温度、湿度等参数维持在设定的安全范围内,并在异常时及时告警。这本质上是一个数据采集 -> 传输 -> 存储 -> 分析与展示 -> 预警的完整物联网数据管道。

1.1 核心业务流程拆解

对于一个毕业设计级别的系统,我们可以将其核心流程简化为以下几步:

  1. 数据模拟与采集:在无真实硬件的情况下,通过程序模拟传感器设备,定期生成符合业务逻辑的温湿度数据。
  2. 数据传输:模拟设备通过HTTP、MQTT等协议将数据上报至平台后端服务。
  3. 数据接收与校验:后端服务接收数据,进行格式校验和合法性检查(如数值范围)。
  4. 数据存储:将有效数据持久化到数据库,通常需要存储设备信息、监测点信息、历史数据记录和报警记录。
  5. 实时监控与展示:前端页面以图表(如折线图、仪表盘)形式实时或近实时展示各监测点的数据变化。
  6. 阈值告警:系统根据预设的阈值规则(如温度 > 8°C)判断数据是否异常,并触发告警(记录日志、发送通知)。

1.2 系统技术架构选型建议

毕业设计应选择生态成熟、资料丰富、易于部署的技术栈。以下是一个基于Spring Boot的经典分层架构,其他语言栈可参考类似思想。

  • 前端展示层:Vue.js / React + ECharts / Ant Design。负责数据可视化、设备管理界面。
  • 后端服务层:Spring Boot。提供RESTful API供前端调用,并处理设备上报的数据。
  • 数据持久层:MyBatis-Plus / JPA。操作关系型数据库。
  • 数据库:MySQL。存储设备、监测数据、用户、告警等信息。
  • 消息中间件(可选,用于解耦与实时性):RabbitMQ / Kafka。用于缓冲设备上报的高频数据。
  • 缓存(可选,提升性能):Redis。缓存设备实时状态、热点数据。
  • 设备模拟:使用一个独立的Java/Python程序,模拟多个设备按规则上报数据。

对于Python、PHP或Node.js实现,可将Spring Boot替换为Flask/Django、Laravel/ThinkPHP、Express/Nest.js等对应框架,核心业务流程保持一致。

2. 环境准备与项目初始化

我们以Java/Spring Boot技术栈为例,演示如何初始化一个基础项目。请确保你的开发环境满足以下要求:

组件推荐版本说明
JDK8, 11 或 17需与Spring Boot版本匹配。本文使用JDK 17。
Maven3.6+项目管理与构建工具。
IDEIntelliJ IDEA 或 Eclipse集成开发环境。
MySQL5.7+ 或 8.0数据库服务器。
Redis (可选)6.x缓存服务器。
RabbitMQ (可选)3.8+消息队列服务器。

2.1 使用Spring Initializr创建项目

通过 start.spring.io 或IDE内置的Spring Initializr创建项目。

依赖选择

  • Spring Web:提供Web MVC能力,用于编写API接口。
  • Spring Data JPAMyBatis Framework:数据库持久化。本文示例使用JPA,更简洁。
  • MySQL Driver:MySQL数据库连接驱动。
  • Lombok:简化Java Bean代码编写(推荐)。
  • (可选)Spring Data Redis:集成Redis。
  • (可选)Spring for RabbitMQ:集成RabbitMQ。

生成项目后,用IDE打开,目录结构应类似:

cold-chain-monitor/ ├── src/ │ ├── main/ │ │ ├── java/com/example/coldchain/ │ │ │ ├── ColdChainApplication.java // 启动类 │ │ │ ├── controller/ // 控制器层 │ │ │ ├── service/ // 业务逻辑层 │ │ │ ├── repository/ // 数据访问层 (JPA) │ │ │ ├── entity/ // 实体类 (对应数据库表) │ │ │ └── dto/ // 数据传输对象 │ │ └── resources/ │ │ ├── application.properties // 配置文件 │ │ └── static/ // 静态资源 (可放前端打包文件) │ └── test/ // 测试代码 └── pom.xml // Maven依赖管理

2.2 基础配置与数据库初始化

src/main/resources/application.properties中配置数据库连接等基本信息。

# 应用端口 server.port=8080 # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/cold_chain_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA配置 spring.jpa.hibernate.ddl-auto=update # 开发环境可用update,生产环境应使用validate或none,配合SQL脚本 spring.jpa.show-sql=true # 控制台显示SQL,便于调试 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect # 日志级别 logging.level.com.example.coldchain=debug

在MySQL中创建数据库:

CREATE DATABASE IF NOT EXISTS cold_chain_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

3. 核心数据模型与实体类设计

良好的数据模型是系统稳定的基石。冷链监控系统至少需要以下几张核心表。

3.1 实体类定义

entity包下创建对应的Java类,并使用JPA注解进行映射。

1. 设备表 (Device)存储监控设备的基本信息。

package com.example.coldchain.entity; import lombok.Data; import javax.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "device") @Data public class Device { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String deviceId; // 设备唯一标识,如SN码 @Column(nullable = false) private String deviceName; // 设备名称 @Column private String location; // 设备安装位置,如“1号冷库A区” @Column private String deviceType; // 设备类型,如“温湿度传感器” @Column(nullable = false) private Integer status = 1; // 状态:1-在线,0-离线 @Column(updatable = false) private LocalDateTime createTime = LocalDateTime.now(); private LocalDateTime updateTime; }

2. 监测数据表 (SensorData)存储设备上报的实时监测数据。

package com.example.coldchain.entity; import lombok.Data; import javax.persistence.*; import java.math.BigDecimal; import java.time.LocalDateTime; @Entity @Table(name = "sensor_data") @Data public class SensorData { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String deviceId; // 关联设备ID @Column(precision = 5, scale = 2) private BigDecimal temperature; // 温度,单位:℃ @Column(precision = 5, scale = 2) private BigDecimal humidity; // 湿度,单位:%RH @Column(nullable = false) private LocalDateTime reportTime; // 数据上报时间 @Column(updatable = false) private LocalDateTime createTime = LocalDateTime.now(); }

3. 报警规则表 (AlertRule)定义触发报警的条件。

package com.example.coldchain.entity; import lombok.Data; import javax.persistence.*; import java.math.BigDecimal; @Entity @Table(name = "alert_rule") @Data public class AlertRule { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String deviceId; // 关联设备,为空则表示全局规则 @Column(nullable = false) private String ruleName; // 规则名称,如“高温预警” @Column(nullable = false) private String metric; // 指标:temperature, humidity @Column(nullable = false) private String operator; // 操作符:>, <, >=, <=, == @Column(precision = 5, scale = 2) private BigDecimal threshold; // 阈值 @Column private String alertLevel; // 报警级别:INFO, WARNING, CRITICAL @Column(nullable = false) private Boolean enabled = true; // 是否启用 }

4. 报警记录表 (AlertLog)记录每次报警触发的详细信息。

package com.example.coldchain.entity; import lombok.Data; import javax.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "alert_log") @Data public class AlertLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String deviceId; @Column(nullable = false) private String ruleId; // 触发的规则ID @Column(nullable = false) private String alertContent; // 报警内容,如“温度超过8℃” @Column(nullable = false) private String alertLevel; @Column(nullable = false) private Integer status = 0; // 状态:0-未处理,1-已处理 @Column(nullable = false) private LocalDateTime alertTime = LocalDateTime.now(); private LocalDateTime handleTime; private String handler; }

启动应用,JPA的ddl-auto=update会根据实体类自动在数据库中创建表结构。生产环境务必使用Flyway或Liquibase进行版本化的SQL脚本管理。

4. 核心业务逻辑实现

4.1 设备数据上报接口

这是平台接收数据的入口。创建一个DataCollectController来处理HTTP POST请求。

package com.example.coldchain.controller; import com.example.coldchain.entity.SensorData; import com.example.coldchain.service.DataCollectService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Map; @RestController @RequestMapping("/api/v1/data") @Slf4j public class DataCollectController { @Autowired private DataCollectService dataCollectService; /** * 模拟设备上报数据 * 请求体示例:{"deviceId":"SN001", "temperature": 5.2, "humidity": 65.0} */ @PostMapping("/report") public Map<String, Object> reportData(@RequestBody Map<String, Object> payload) { log.info("接收到设备上报数据: {}", payload); try { String deviceId = (String) payload.get("deviceId"); // 处理可能为整数或浮点的温度值 Number tempNum = (Number) payload.get("temperature"); Number humNum = (Number) payload.get("humidity"); BigDecimal temperature = tempNum != null ? new BigDecimal(tempNum.toString()) : null; BigDecimal humidity = humNum != null ? new BigDecimal(humNum.toString()) : null; SensorData sensorData = new SensorData(); sensorData.setDeviceId(deviceId); sensorData.setTemperature(temperature); sensorData.setHumidity(humidity); sensorData.setReportTime(LocalDateTime.now()); // 调用服务层处理数据(存储、检查报警) dataCollectService.processSensorData(sensorData); return Map.of("code", 200, "message", "数据接收成功"); } catch (Exception e) { log.error("处理上报数据异常", e); return Map.of("code", 500, "message", "服务器内部错误: " + e.getMessage()); } } }

4.2 数据接收与报警检查服务

DataCollectService中,我们实现数据存储和报警判断的核心逻辑。

package com.example.coldchain.service; import com.example.coldchain.entity.AlertLog; import com.example.coldchain.entity.AlertRule; import com.example.coldchain.entity.SensorData; import com.example.coldchain.repository.AlertLogRepository; import com.example.coldchain.repository.AlertRuleRepository; import com.example.coldchain.repository.SensorDataRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @Service @Slf4j public class DataCollectService { @Autowired private SensorDataRepository sensorDataRepository; @Autowired private AlertRuleRepository alertRuleRepository; @Autowired private AlertLogRepository alertLogRepository; @Transactional public void processSensorData(SensorData sensorData) { // 1. 保存传感器数据 sensorDataRepository.save(sensorData); log.debug("传感器数据已保存: {}", sensorData); // 2. 检查报警规则 checkAlertRules(sensorData); } private void checkAlertRules(SensorData data) { // 查询该设备相关的所有启用规则,以及全局规则(deviceId为null) List<AlertRule> rules = alertRuleRepository.findByDeviceIdOrDeviceIdIsNullAndEnabledTrue(data.getDeviceId()); for (AlertRule rule : rules) { boolean isTriggered = false; BigDecimal value = null; // 根据指标获取当前数据值 if ("temperature".equals(rule.getMetric())) { value = data.getTemperature(); } else if ("humidity".equals(rule.getMetric())) { value = data.getHumidity(); } if (value == null) { continue; // 该指标无数据,跳过检查 } // 根据操作符判断是否触发 switch (rule.getOperator()) { case ">": isTriggered = value.compareTo(rule.getThreshold()) > 0; break; case ">=": isTriggered = value.compareTo(rule.getThreshold()) >= 0; break; case "<": isTriggered = value.compareTo(rule.getThreshold()) < 0; break; case "<=": isTriggered = value.compareTo(rule.getThreshold()) <= 0; break; case "==": isTriggered = value.compareTo(rule.getThreshold()) == 0; break; default: log.warn("未知的操作符: {}", rule.getOperator()); continue; } if (isTriggered) { // 触发报警,记录日志 AlertLog alertLog = new AlertLog(); alertLog.setDeviceId(data.getDeviceId()); alertLog.setRuleId(rule.getId().toString()); alertLog.setAlertContent(String.format("设备[%s]的%s[%s] %s 阈值[%s]", data.getDeviceId(), rule.getMetric(), value, rule.getOperator(), rule.getThreshold())); alertLog.setAlertLevel(rule.getAlertLevel()); alertLog.setAlertTime(LocalDateTime.now()); alertLogRepository.save(alertLog); log.warn("报警已触发: {}", alertLog.getAlertContent()); // 此处可扩展:调用短信、邮件、WebHook等通知服务 // notifyService.sendAlert(alertLog); } } } }

对应的Repository接口(JPA)非常简单:

package com.example.coldchain.repository; import com.example.coldchain.entity.AlertRule; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface AlertRuleRepository extends JpaRepository<AlertRule, Long> { @Query("SELECT r FROM AlertRule r WHERE (r.deviceId = :deviceId OR r.deviceId IS NULL) AND r.enabled = true") List<AlertRule> findByDeviceIdOrDeviceIdIsNullAndEnabledTrue(@Param("deviceId") String deviceId); }

4.3 数据查询与设备管理接口

为前端提供数据查询和设备管理的API。

package com.example.coldchain.controller; import com.example.coldchain.entity.Device; import com.example.coldchain.entity.SensorData; import com.example.coldchain.service.DeviceService; import com.example.coldchain.service.MonitorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.util.List; @RestController @RequestMapping("/api/v1/monitor") public class MonitorController { @Autowired private MonitorService monitorService; @Autowired private DeviceService deviceService; // 获取设备最新数据 @GetMapping("/latest/{deviceId}") public SensorData getLatestData(@PathVariable String deviceId) { return monitorService.getLatestData(deviceId); } // 获取设备历史数据(用于图表) @GetMapping("/history/{deviceId}") public List<SensorData> getHistoryData(@PathVariable String deviceId, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end) { return monitorService.getHistoryData(deviceId, start, end); } // 获取所有设备列表 @GetMapping("/devices") public List<Device> getAllDevices() { return deviceService.getAllDevices(); } // 添加新设备 @PostMapping("/device") public Device addDevice(@RequestBody Device device) { return deviceService.addDevice(device); } }

5. 模拟设备与系统运行验证

5.1 编写设备模拟器

为了在没有真实硬件的情况下测试系统,我们编写一个简单的Java程序来模拟设备定时上报数据。

package com.example.coldchain.simulator; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Slf4j public class DeviceSimulator { private static final String SERVER_URL = "http://localhost:8080/api/v1/data/report"; private static final String[] DEVICE_IDS = {"SN001", "SN002", "SN003"}; private static final Random random = new Random(); private static final RestTemplate restTemplate = new RestTemplate(); private static final ObjectMapper objectMapper = new ObjectMapper(); public static void main(String[] args) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(DEVICE_IDS.length); for (String deviceId : DEVICE_IDS) { // 每个设备一个任务,每10秒上报一次数据 scheduler.scheduleAtFixedRate(() -> reportData(deviceId), 0, 10, TimeUnit.SECONDS); } log.info("设备模拟器已启动,模拟 {} 个设备。", DEVICE_IDS.length); } private static void reportData(String deviceId) { try { // 模拟温度在 -5°C 到 10°C 之间波动,湿度在 50% 到 80% 之间波动 double tempBase = 2.5; // 基准温度 double tempFluctuation = random.nextDouble() * 15 - 5; // -5 ~ 10 BigDecimal temperature = BigDecimal.valueOf(tempBase + tempFluctuation).setScale(2, RoundingMode.HALF_UP); double humBase = 65.0; double humFluctuation = random.nextDouble() * 30 - 15; // -15 ~ 15 BigDecimal humidity = BigDecimal.valueOf(Math.max(30, Math.min(90, humBase + humFluctuation))).setScale(2, RoundingMode.HALF_UP); Map<String, Object> data = new HashMap<>(); data.put("deviceId", deviceId); data.put("temperature", temperature); data.put("humidity", humidity); data.put("timestamp", System.currentTimeMillis()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(data), headers); String response = restTemplate.postForObject(SERVER_URL, request, String.class); log.debug("设备 {} 上报数据: 温度={}°C, 湿度={}%RH, 响应: {}", deviceId, temperature, humidity, response); } catch (Exception e) { log.error("设备 {} 上报数据失败", deviceId, e); } } }

这是一个独立的Java程序,需要引入spring-webjackson等依赖。可以将其放在一个单独的模块中,或直接在主项目的测试目录下运行。

5.2 启动与验证

  1. 启动后端服务:运行ColdChainApplicationmain方法,确保控制台无报错,并看到Tomcat启动在8080端口。
  2. 启动设备模拟器:运行DeviceSimulatormain方法,观察控制台日志,确认数据在上报。
  3. 验证数据入库:使用MySQL客户端连接cold_chain_db数据库,查询sensor_data表,应能看到不断新增的记录。
  4. 验证API接口:使用Postman或浏览器测试API。
    • GET http://localhost:8080/api/v1/monitor/devices:应返回空数组或你预先插入的设备列表。
    • GET http://localhost:8080/api/v1/monitor/latest/SN001:返回设备SN001的最新一条数据。
    • POST http://localhost:8080/api/v1/data/report:手动发送一条JSON数据,测试接收和报警。
      { "deviceId": "SN001", "temperature": 12.5, "humidity": 70.0 }
  5. 验证报警:在alert_rule表中插入一条规则。
    INSERT INTO alert_rule (device_id, rule_name, metric, operator, threshold, alert_level, enabled) VALUES ('SN001', '高温报警', 'temperature', '>', 8.00, 'WARNING', 1);
    当模拟器上报的温度超过8°C,或手动上报的温度超过8°C时,检查alert_log表,应能看到新增的报警记录。

6. 常见问题排查与优化实践

在开发此类系统时,以下几个问题是高频出现的。

6.1 数据库连接与JPA配置问题

问题现象可能原因检查与解决
java.sql.SQLException: Access denied for user数据库用户名/密码错误,或用户无权限访问该数据库。1. 检查application.properties中的用户名密码。
2. 登录MySQL,执行GRANT ALL PRIVILEGES ON cold_chain_db.* TO 'username'@'%'; FLUSH PRIVILEGES;
java.net.ConnectException: Connection refusedMySQL服务未启动,或端口不对。1. 执行systemctl status mysql或 `netstat -an
Hibernate: drop table if exists device等DDL语句频繁执行spring.jpa.hibernate.ddl-auto被设置为createcreate-drop开发环境可设为update,生产环境务必设为validatenone,并通过SQL脚本管理表结构。
实体类字段修改后,数据库表未更新ddl-auto=update在某些复杂修改(如删除字段、修改字段类型)时可能失效。1. 直接修改数据库表。
2. 或使用spring.jpa.hibernate.ddl-auto=create启动一次(会清空所有数据),再改回update

6.2 接口请求与数据处理问题

问题现象可能原因检查与解决
模拟器上报数据,但后端控制台无日志,数据库无记录。1. 模拟器请求URL错误。
2. 后端服务未启动或端口不对。
3. 网络防火墙阻止。
1. 在模拟器中打印完整的请求URL和响应。
2. 用Postman直接调用接口,确认后端服务正常。
3. 检查后端控制台是否有请求接入日志。
上报数据接口返回400错误。1. 请求头Content-Type不是application/json
2. JSON格式错误或字段类型不匹配。
1. 确保模拟器或Postman设置了正确的Content-Type
2. 检查JSON格式,确保temperaturehumidity是数字类型。
报警规则未触发。1. 规则未启用 (enabled=0)。
2. 规则中的deviceId与上报数据的deviceId不匹配,且无全局规则。
3. 阈值比较逻辑有误。
1. 检查alert_rule表数据。
2. 在checkAlertRules方法中增加调试日志,打印当前数据值和匹配到的规则详情。
3. 检查BigDecimal.compareTo的逻辑是否符合预期。

6.3 性能与生产环境考量

毕业设计项目虽然对性能要求不高,但了解生产环境的考量是重要的加分项。

  1. 数据库优化
    • 索引:为sensor_data表的device_idreport_time字段添加复合索引,加速按设备和时间范围查询。
    CREATE INDEX idx_device_time ON sensor_data(device_id, report_time);
    • 数据归档:监控数据增长极快,需定期将历史数据迁移到历史表或冷存储,保证主表查询性能。
  2. 服务解耦
    • 当前数据接收和报警检查在同一个事务中,如果报警逻辑复杂或通知服务慢,会阻塞数据接收。可引入消息队列(如RabbitMQ)。设备数据先入库,然后发送一个消息到队列,由独立的消费者服务进行报警判断和通知,实现异步处理。
  3. 缓存应用
    • 设备最新状态、报警规则等不常变化的数据可以缓存在Redis中,减少数据库查询压力。
  4. 接口安全
    • 生产环境的数据上报接口必须增加认证机制,如简单的API Key验证,或更复杂的OAuth2.0 Client Credentials模式,防止非法数据注入。
  5. 监控与日志
    • 集成Spring Boot Actuator暴露健康检查、指标端点。
    • 使用Logback或Log4j2配置日志滚动策略,将错误日志单独输出到文件,便于排查。

7. 扩展方向与毕设深化建议

一个基础的监控平台已经完成,但要让毕设脱颖而出,可以考虑以下扩展方向:

  1. 前端可视化:使用Vue.js + ECharts 构建一个实时数据看板,展示温度/湿度曲线图、设备状态地图、报警列表等。
  2. 多协议支持:除了HTTP,实现MQTT协议接入,这是物联网更常用的轻量级协议。可以使用Eclipse PahoSpring Integration
  3. 报警通知多元化:集成邮件(Spring Boot Mail)、短信(阿里云/腾讯云SDK)、钉钉/企业微信机器人WebHook等通知渠道。
  4. 设备管理:实现设备的增删改查、远程配置下发(如修改上报频率)、固件升级管理等功能。
  5. 数据报表:增加按日、周、月生成温湿度统计报表的功能,并支持导出Excel或PDF。
  6. 多租户与权限:引入Spring Security,实现不同仓库管理员只能查看和管理自己权限范围内的设备。
  7. 容器化部署:编写Dockerfile和docker-compose.yml,将MySQL、Redis、后端服务、前端服务一键部署,体现运维能力。
  8. 引入时序数据库:对于海量监控数据,可以调研并集成InfluxDB或TDengine,专门优化时间序列数据的存储和查询。

在撰写毕业论文时,重点阐述你选择的技术栈的对比(如为什么选Spring Boot而非Node.js)、系统架构设计(分层、模块划分)、核心业务流程的实现、遇到的挑战及解决方案(即上面的排错过程)、以及系统的测试(单元测试、接口测试)和未来展望。将本文提供的代码和思路作为你的工程实践部分,结合理论分析,便能构成一份扎实的计算机毕业设计。