2026/10/3 9:56:26

Spring Boot+Vue智慧草莓基地:物联网环境监控与告警联动系统设计

Spring Boot+Vue智慧草莓基地:物联网环境监控与告警联动系统设计 做了几年全栈开发也帮不少人审过毕业设计发现这类“物联网管理系统”的项目有一个通病前端页面做得很漂亮后端 CRUD 也能跑但一落到传感器数据采集、实时展示、告警联动这些核心链路就漏洞百出。今天借“Spring Boot Vue 智慧草莓基地管理系统”这个典型的毕业设计题把整条线的设计思路、代码实现、论文写作到答辩准备一次性讲透。这个题目特别适合计算机相关专业做毕设因为既有管理系统的常规操作又有物联网数据接入和可视化展示的加分项难度刚好能达到本科毕设的深度要求。1. 开题前先想清楚这套系统到底要解决什么问题1.1 毕业设计的选型逻辑选技术栈是最先要做的事。Spring Boot Vue 的组合在招聘市场和毕设评审里都是最不容易出错的搭配但很多人只背下来了“前后端分离”这个词实际连为什么这么选都说不清。这门题目的核心价值不是做一个增删改查后台而是把种植大棚里的环境因子变成可管理、可预警、可分析的数据资产。先用 Spring Boot 做后端原因很简单它对 RESTful 接口的支持是最成熟的内置 Tomcat、Spring MVC、MyBatis 或 JPA 的集成几乎零成本非常适合快速搭建业务系统。再加上现在 Spring Boot 3.x 已经普及JDK 17 是标配做毕设不需要追求最新版本稳定优先。前端用 Vue因为 Vue 的响应式数据绑定和组件化开发很适合做监控大屏这类界面。Vue 2 还是 Vue 3我的建议是直接上 Vue 3 Vite Element Plus因为很多学校评审老师已经见惯了 Vue 2 Element UI你用 Vue 3 反而能体现出对技术演进的关注。另一个原因Vue 3 的 Composition API 写图表联动、WebSocket 推送这类逻辑时代码组织更清晰答辩也能多讲出一些东西。1.2 核心业务流程与技术链路先别急着写代码把业务链路理顺。草莓基地的智能管理通常包括这四部分环境感知、设备控制、生长记录、告警联动。环境感知是温湿度、光照强度、二氧化碳浓度、土壤湿度传感器设备控制是风机、卷帘、滴灌、补光灯生长记录是草莓从定植到开花的阶段照片和农事操作告警联动则是温度和湿度超限之后系统自动推送消息并触发设备动作。整个数据流可以这样描述传感器节点定时上报数据通过 MQTT 协议或 HTTP 接口发到后端后端解析、清洗、校验后存入 MySQL同时通过 WebSocket 推送到前端页面实时刷新。规则引擎判断当前数值是否超过阈值是则生成告警记录并可以给设备下发控制指令。这条链路是系统的灵魂论文里一定要把它讲清楚评审老师最看重的也是这个闭环是否完整。1.3 范围控制不要让毕设膨胀成产品很多学生做这个题目最容易犯的错误是想着把“智慧农业平台”做成大而全的 SaaS连商城、社区、在线支付都往里塞。这没必要。毕业设计考察的是你完整走通一个项目的思路而不是功能堆砌。建议把范围控制在用户管理、基地信息管理、传感器数据采集与查询、环境实时监控、设备管理、农事记录、告警管理、数据可视化分析这八个模块足够拿高分。如果时间富余再增加一个简单的移动端适配页面但不要碰 App 和小程序成本和答辩风险都高。2. 功能模块划分与数据模型设计2.1 模块边界别把系统做成“大杂烩”功能模块划分直接决定你数据库有多少张表也决定你后面写论文的章节结构。我在实际项目中推荐这样拆模块核心功能关键实体用户管理登录、角色权限、个人信息用户、角色、权限基地管理大棚信息、区域划分、作物品种基地、大棚、作物环境监测传感器数据采集、历史查询、曲线图传感器、环境数据设备控制设备列表、启停控制、运行日志设备、设备指令农事管理种植批次、农事操作记录批次、农事记录告警管理阈值设定、告警记录、消息通知阈值规则、告警记录注意这里没有把“统计分析”单列成模块而是挂在环境监测下面因为数据分析依赖环境数据表这样做代码耦合度更低。2.2 数据库表设计与关键字段以环境数据表为例我建议这样设计字段CREATE TABLE env_data ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sensor_id VARCHAR(32) NOT NULL COMMENT 传感器编号, base_id BIGINT NOT NULL COMMENT 基地ID, greenhouse_id BIGINT NOT NULL COMMENT 大棚ID, air_temperature DECIMAL(5,2) COMMENT 空气温度(℃), air_humidity DECIMAL(5,2) COMMENT 空气湿度(%RH), soil_moisture DECIMAL(5,2) COMMENT 土壤湿度(%), co2_concentration DECIMAL(6,2) COMMENT CO2浓度(ppm), light_intensity DECIMAL(10,2) COMMENT 光照强度(lux), collect_time DATETIME NOT NULL COMMENT 采集时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_base_time (base_id, collect_time) ) COMMENT 环境监测数据表;这里有个经验采集时间和系统接收时间一定要分开因为网络传输可能造成延迟如果只存一个时间后续做曲线图时会出现顺序错乱。另外DECIMAL 类型比 FLOAT 更适合存传感器数值避免二进制浮点带来的精度误差。告警规则表可以设计成阈值型规则具体如下CREATE TABLE alarm_rule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, greenhouse_id BIGINT NOT NULL, sensor_type VARCHAR(20) NOT NULL, min_value DECIMAL(10,2), max_value DECIMAL(10,2), enabled TINYINT DEFAULT 1, alarm_level VARCHAR(10) DEFAULT WARN, update_time DATETIME );规则表单独建的好处是后期调整告警阈值不需要改代码在前端写一个规则配置页面直接更新数据库。2.3 数据流转中的状态管理在系统里要处理好几个状态机设备状态有在线、离线、运行、停止订单状态如果有应该是待处理、进行中、已完成告警状态有未处理、已确认、已关闭。这些状态建议在数据库中用 TINYINT 存储数字再用前端配置映射文本。不要直接用中文存状态否则统计查询和状态流转判断都很难写。我在项目中还专门给传感器建了一张元数据表记录传感器型号、量程、精度、安装时间因为不同供应商的传感器量程不一样统一做阈值判断时如果不考虑量程很容易误报。这个细节哪怕论文里不展开答辩被问到时你能答上来就非常加分。3. Spring Boot 后端实现的关键环节3.1 项目工程结构与依赖选择后端工程建议用 Maven 构建依赖尽量精简。核心依赖有spring-boot-starter-web、spring-boot-starter-validation、mybatis-plus、mysql-connector-j、spring-boot-starter-data-redis做验证码和 Token 缓存、spring-boot-starter-websocket、spring-boot-starter-security或者用 Sa-Token 也行。工程结构我习惯这样分层com.strawberry ├── controller // 接口层 ├── service // 业务层 ├── mapper // 数据访问层使用MyBatis-Plus ├── entity // 实体类 ├── dto // 前端交互对象 ├── mqtt // 传感器接入 ├── websocket // 实时推送 ├── config // 配置类 ├── common // 统一返回结构、异常、常量 └── utils // 时间、JSON等工具这里的重点不是包结构而是一定要遵循“单向依赖”原则controller 只依赖 serviceservice 只依赖 mapper不能在 controller 里直接写 SQL。毕设答辩时老师会随机抽查一个方法你如果写成三层结构解释起来会很顺。3.2 传感器数据接入从模拟数据到 MQTT硬件传感器不是每个人都买得到所以毕设里做模拟数据接入是完全可接受的。但你需要把方案讲得有层次第一层用定时任务生成模拟数据用于演示和测试第二层预留 MQTT 接口方便真实设备接入。用定时任务模拟数据时需要注意不要用Scheduled(fixedRate...)写死间隔应该把采集频率设为可配置项。我建议通过application.yml配置strawberry: sensor: simulate: true interval-ms: 5000然后在配置类里读取这个值注册一个ScheduledTaskRegistrar动态调整定时任务。这样做的好处是论文里可以写“本文设计了可配置的数据采集策略以适应不同传感器的上报频率”这是很好的亮点。真实 MQTT 接入的核心代码大致是添加org.eclipse.paho.client.mqttv3依赖然后写一个 MqttCallback 实现Component public class SensorMessageHandler implements MqttCallback { Override public void messageArrived(String topic, MqttMessage message) { String payload new String(message.getPayload()); SensorDataDTO dto JSON.parseObject(payload, SensorDataDTO.class); // 校验数据合法性 if (dto.getAirTemperature() null) { log.warn(收到非法数据: {}, payload); return; } // 写入数据库并推送WebSocket envDataService.saveFromSensor(dto); websocketService.pushSensorData(dto); // 触发告警规则 alarmService.checkAndFire(dto); } }这段代码把“接收 - 解析 - 持久化 - 推送 - 告警”一条线串起来了。注意messageArrived 是回调线程执行不要在中间做耗时的数据库事务否则高频数据下会积压。实际项目中我做过压测单线程处理 MQTT 消息会有瓶颈所以建议将消息先丢进线程池或消息队列消费者再落库。3.3 告警规则怎么设计才不误报农业环境数据有个特点传感器偶尔会出现瞬时抖动比如人从旁边走过导致温度值突变。如果每个采集点都判断是否超阈值一个下午能告警几百次用户直接就把通知关掉了。更好的做法是“连续 N 次超限才告警”。在代码里实现一个带计数器的规则检测器public class RuleEvaluator { private final int thresholdCount 3; private final MapLong, Integer consecutiveCount new ConcurrentHashMap(); public synchronized boolean shouldAlarm(AlarmRule rule, BigDecimal value) { boolean over value.compareTo(rule.getMaxValue()) 0; Long key rule.getId(); int count over ? consecutiveCount.getOrDefault(key, 0) 1 : 0; consecutiveCount.put(key, count); return count thresholdCount; } }另外告警产生后还需要做去重同一个大棚同类型的告警在没有恢复之前不重复生成新记录。如果支持告警恢复功能当环境数值回到安全区时把之前的告警记录关掉生成一条恢复信息。这种设计比单纯“告警了就存一条”要专业得多论文里能单独写一节。3.4 接口设计与 Token 认证如果你用了 Spring Security为了让毕设源码不至于太复杂可以只开启登录认证和接口拦截不用引入 OAuth2。登录接口返回一个 UUID Token存储在 Redis设置过期时间。拦截器里校验 Tokenpublic class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !redisTemplate.hasKey(login:token: token)) { response.setStatus(401); return false; } return true; } }前端每次请求在 axios 拦截器中带上 Token如果返回 401跳转登录页。这里有一个常见坑不同浏览器的跨域配置Spring Boot 需要实现WebMvcConfigurer的addCorsMappings同时allowCredentials和allowedOriginPatterns要配对使用否则前端请求会被拦。返回值建议统一封装public class ResultT { private Integer code; private String message; private T data; // 静态方法 success() / error() }这种统一返回结构写论文时特别好描述前后端联调也不容易出分歧。4. Vue 前端的工程化落地4.1 搭建 Vue 项目与路由规划前端我用 Vue 3 Vite创建项目就一行命令npm create vitelatest strawberry-admin -- --template vue接下来安装路由、状态管理、UI 库npm install vue-router4 pinia element-plus axios echarts路由规划要贴合页面层级比如/login登录页/layout主框架嵌套路由/layout/dashboard监控大屏、/layout/base基地管理、/layout/env环境历史数据、/layout/device设备控制、/layout/alarm告警中心权限控制可以先用最简单的方案在路由守卫beforeEach中判断localStorage是否存有 token没有则跳登录页。如果需要区分管理员和普通用户再在 meta 里标记roles与登录用户角色比对。路由懒加载用import()就足够不用单独优化。需要注意的是Vite 打包后的资源路径如果放到 Spring Boot 的静态目录下访问可能会出现图片加载不出来这个在 6.2 节里会详细说。4.2 ECharts 可视化大屏智慧农业项目如果没有一个像样的数据大屏验收效果会大打折扣。ECharts 是首选。最核心的是折线图用实时数据展示温湿度变化。启动时从后端拉取最近一小时数据用echarts.init初始化图表WebSocket 每收到一条数据就setOption添加一个点const chart echarts.init(document.getElementById(tempChart)) const initOption { title: { text: 空气温度趋势 }, xAxis: { type: time }, yAxis: { type: value, name: ℃ }, series: [{ name: 温度, type: line, data: [] }] } chart.setOption(initOption) socket.onmessage (event) { const data JSON.parse(event.data) chart.appendData({ seriesIndex: 0, data: [[data.collectTime, data.airTemperature]] }) }这里有一个优化点时间轴如果一直 append 数据旧的还在图表会变得很卡。需要在每次 append 前把超过一定数量的旧数据shift掉或者设置dataZoom只展示最近 30 分钟。另外一个坑ECharts 的 time 轴需要的时间格式是时间戳或标准时间字符串后端返回的 LocalDateTime 默认格式带T你可以让后端返回字符串也可以在前端用dayjs格式化建议前端统一处理后端只返回 ISO 格式。4.3 WebSocket 实时刷新别用定时器轮询很多毕设项目偷懒用setInterval每隔 5 秒调一次接口刷新数据。这在答辩现场可能看不出问题但如果被问“为什么用轮询而不是长连接”会是一个很大的扣分点。既然学了 WebSocket就一定要用起来。后端可以直接用 Spring 的WebSocketHandler写入实时数据。不过 Spring Boot 的 WebSocket API 写起来偏底层。建议使用spring-boot-starter-websocket前端用原生 WebSocket 即可不需要额外库。关键代码是连接管理Component public class SensorWebSocketHandler extends TextWebSocketHandler { private static final CopyOnWriteArraySetWebSocketSession SESSIONS new CopyOnWriteArraySet(); Override public void afterConnectionEstablished(WebSocketSession session) { SESSIONS.add(session); } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { SESSIONS.remove(session); } public void broadcast(String message) throws IOException { for (WebSocketSession session : SESSIONS) { if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } } }用CopyOnWriteArraySet是为了并发安全。广播时注意如果消息队列堆积可能出现发送超时这时可以捕获 IOException 后关闭 session避免阻塞其他连接。前端在App.vue中创建全局 socket 连接页面销毁时close()避免内存泄漏。4.4 接口封装、环境变量和登录状态axios 封装不复杂但要有统一拦截器。创建src/utils/request.jsimport axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers.Authorization token return config }) request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.message || 请求失败) return Promise.reject(error) } )注意VITE_API_BASE_URL要在.env.development和.env.production中分别配置。开发环境用http://localhost:8080/api生产环境可以用相对地址/api然后在 Spring Boot 里统一加前缀。登录状态除了看 token 是否存在特别要注意 token 过期。可以做一个响应拦截401 时跳转登录页并清除本地数据。有的学生会把用户信息存到 Vuex 或 Pinia刷新页面后状态丢失需要再从后端拉一次这个要提前处理。我习惯在每次路由守卫里根据 token 拉取最新用户信息而不是直接信任本地存储。5. 毕业论文怎么写才能拿到高分5.1 论文结构排布论文结构大概是绪论背景、意义、国内外现状、相关技术介绍、系统需求分析、系统设计、系统实现、系统测试、总结展望。最容易出问题的地方是“相关技术介绍”写成了名词解释大全。评审老师最反感大段抄百度百科的话。正确做法是把技术特点和项目场景结合比如写 Vue 时一句话概括它是前端渐进式框架然后马上说“本系统中Vue 的组件化开发方式用于将监控大屏拆分为多个独立图表组件数据请求统一通过 axios 封装模块管理”。这样技术介绍就变成了服务于你的系统设计而不是孤立背景。需求分析部分最好用用例图和用例描述表。用例图不需要画太复杂重点是“用户”和“管理员”两个角色的核心操作。用例表要包含用例名称、优先级、主流程、异常流程这部分是得分点认真写能拉开差距。5.2 图表、数据与测试部分论文中的图不要只有截图要让每一张图都承担说明作用。建议包含系统总体架构图、功能结构图、数据库 E-R 图、关键表关系图、系统运行时序图比如传感器数据上报到告警产生的时序。不要画 Mermaid直接用 Visio、Draw.io 或者 PlantUML 画得规范一点。测试部分很多学生只写“测试通过”四个字。要写具体的测试用例至少包括登录测试、传感器数据上传接口测试、阈值告警测试、WebSocket 消息推送测试。再补一个接口测试表格列出接口路径、参数、预期结果、实际结果。如果做过分批简单压测也可以写进去。5.3 答辩现场应对答辩高频问题我列几个为什么选 Spring Boot传感器数据怎么接入的如果同时有上万个设备上报怎么处理告警规则能实时修改吗前端访问慢怎么排查这些问题在本文前面都有覆盖回答时不要只背概念要用自己项目中的实现细节去印证。答辩时如果老师问到超出你实现范围的功能不要说“我不会”而是说“我在设计中预留了该方案但由于时间和硬件资源限制当前使用模拟数据验证后续可以将传感器模块替换为实际设备”。这样比掩饰更稳妥。6. 从开发到部署的踩坑记录6.1 Spring Boot 版本与 JDK 版本匹配很多人一上来就装了最新的 Spring Boot 3.3但本地 JDK 还是 8跑不起来。Spring Boot 3.x 强制要求 JDK 17如果你的电脑还停留在 Java 8建议用 Spring Boot 2.7.x对应 JDK 8/11。不要为了追求新版本折腾半天环境毕设重点是完成业务不是版本追逐。如果在 IDE 里配置启动端口可以在application.yml中写server: port: 8080然后在 Spring Boot 启动类的SpringApplication.run中可以用new SpringApplicationBuilder(Application.class).properties(server.port8080)覆盖但通常情况下配置文件就够了。6.2 Vue 打包后放进 Spring Boot这是毕设演示最常用的部署方式把前端打包后的dist目录放到 Spring Boot 的src/main/resources/static下然后直接访问后端端口就同时提供前端页面和接口服务。需要注意几点首先是 Vite 的base配置在vite.config.js中设置base: /。其次是前端路由要用history模式否则刷新页面会 404。解决方法是给 Spring Boot 加一个转发规则把非/api的路径都转发到index.htmlController public class ViewController { RequestMapping(value {/, /{path:[^\\.]*}}) public String forward() { return forward:/index.html; } }注意正则[^\\.]*表示不包含点的路径都转发这样静态资源如.js、.css不会被错误转发。最后接口统一前缀建议为/api因为 Vue 打包后接口请求地址如果在开发环境是localhost:8080生产环境就需要改成/api所以统一用.env文件配置避免改代码。6.3 长连接断开、时区、JSON 序列化等低级错误这类坑在开发中非常常见记录下来防止大家踩一是 WebSocket 长时间不通信会被网关断开。建议前端每 30 秒发送一个 ping 消息后端回复 pong保持长连接活跃。如果服务端部署在 Nginx 后面还要调整proxy_read_timeout。二是 MySQL 时区问题。jdbc:mysql://localhost:3306/db?serverTimezoneAsia/Shanghai必须加上否则时间比正常时间早 8 小时或者直接报错。三是 LocalDateTime 序列化。Spring Boot 默认对 LocalDateTime 序列化成数组或 ISO 字符串前端拿到格式不统一。建议在配置文件里统一spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果是 LocalDateTime 字段还需要在实体类上标注JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。四是 JSON 循环引用。实体类之间的双向关联比如用户和角色会导致序列化抛异常要在任意一端加JsonIgnoreProperties或拆成 DTO。6.4 部署与演示环境的稳定化答辩演示最怕现场翻车。建议提前准备几个稳定性的保底手段开题之前就把种子数据写好包括几个大棚、不同类型的传感器和足够的历史数据这样演示时不用现场等数据产生。环境数据采集的开关做成前端按钮演示时如果要展示实时变化可以手动触发一次上报模拟数据生成器输出随机但合理的数值。设备控制功能写一个假的控制响应点击开启之后状态立即变化并在页面给出“指令已下发”的提示不要让设备真的依赖硬件。最后还有一个心态上的建议不要等到最后两周才开始写论文。系统开发完了之后至少留三周给论文写作和查重修改因为测试数据整理、画图、排版非常耗时很多人就是因为最后没时间论文质量断崖式下跌。我个人体会是这类毕设能不能做好的关键不在代码量有多大而在于你有没有把“环境感知 - 数据流转 - 规则判警 - 前端展示 - 设备控制”这条链路完整跑通。哪怕每个环节都做得简单一点只要闭环是通的答辩老师一眼就能看出你真正理解了系统。反过来如果你只实现了 CRUD再多的页面都是空壳。先把核心链路打通再往里面加所谓的亮点会比一开始就想着堆功能稳妥得多。希望这篇文章能让你少踩几个坑也祝你论文和答辩顺利。