做毕业设计选“精密温室监控小程序(SpringBoot+微信小程序)”这个方向,很多人的第一反应是:这不就是做一个能在手机上查温度和湿度的小系统吗?真等动手写代码,才会发现它和图书管理、商城这类纯信息管理系统有一个本质区别。温室监控的核心不是“记录数据”,而是“建立一条数据链路”:传感器采集环境参数,通过网络上报给SpringBoot后端,后端解析、存储、计算,微信小程序再去读取并展示,必要时还要下发控制指令。这三段链路只要有一段没打通,整个项目就只是界面好看却不能用的演示壳。
所以我给这个题目的定位很清楚:它不是一个纯前端项目,也不是纯后端项目,而是一个前后端联动、带有真实物联网特征的完整系统。SpringBoot负责稳定地接收、处理和输出数据,微信小程序负责把数据变成用户能理解和操作的东西。理解了这条链路,后面所有设计都顺了。
1. 先搞清楚这个毕业设计真正要解决的是什么
1.1 它不只是一个“手机上的管理系统”
很多同学看到“监控”两个字,就自动把它归类成管理系统。如果是图书管理系统,核心是“增删改查”,把书、读者、借阅记录管理好就行。但温室监控不一样。
温室里的环境不是静态的。温度会随着太阳照射和通风变化,湿度会因为灌溉和蒸腾上下波动,土壤墒情会从湿润慢慢变干。如果系统只能“录入一条数据”或者“查看一条记录”,那就完全没有解决真实问题。真实问题是:环境参数一直在变,人不可能二十四小时进大棚看,怎么让一个系统替人盯着?
所以,这个项目真正要做的,是“状态感知”和“异常发现”。状态感知指的是传感器定时上报数据,系统能把实时数据和历史数据保存下来;异常发现指的是当温度超过上限、湿度过低时,系统能提醒人处理,而不是等人自己发现。
这也是为什么它和普通管理系统的技术方案会有很大差别。管理系统通常是一次请求一次响应,数据是用户主动产生的;监控系统则是设备不断上报数据,用户偶尔查看或操作。前者更像“填表格”,后者更像“看电视信号”。
1.2 核心链路:采集、上报、存储、展示、控制
把整个项目拆开看,一条完整的数据链路包含五个环节:
- 采集:传感器读取空气温度、湿度、光照强度、土壤湿度等参数。
- 上报:设备端把采集到的数据通过网络发送给后端接口。
- 存储:SpringBoot后端接收数据,校验后写入数据库。
- 展示:微信小程序通过接口读取实时数据和历史数据,用图表和卡片呈现。
- 控制:用户在小程序里操作设备开关,后端生成控制指令,下发到设备或模拟设备。
在毕业设计答辩时,最容易讲清楚的就是这条链路。你不需要强调自己会多少新技术,只要能把“数据从哪来、经过哪、存到哪、最后怎么用”讲明白,评委就能立刻判断你确实理解了项目,而不是背了一个项目。
反过来,如果一个系统只做了页面和后端CRUD,没有数据来源说明,也没有上报机制,那它和“网页版Excel”没有区别。这也是很多同类毕业设计看起来很完整、细看却很空的原因。
2. 后端不是写CRUD,而是把数据链路稳定地串起来
2.1 用SpringBoot搭出的分层骨架
SpringBoot在这个项目里承担的是数据中枢的角色。它不负责让页面好看,也不负责曲线图画得多炫,它要做的事情是:接收设备上报、处理业务规则、提供查询接口、保存操作记录。
常见的分层结构可以这样设计:
- Controller 层:负责接收HTTP请求,校验参数,调用Service层。
- Service 层:负责业务逻辑,比如判断是否触发告警、控制设备开关。
- Mapper/Repository 层:负责数据库访问,查询、插入、更新。
- Entity 层:对应数据库表结构。
- DTO 层:用于接口返回值,不直接把数据库实体暴露给前端。
很多同学会问,毕业设计需要这么分层吗?我的建议是,哪怕项目很小,也尽量把分层写清楚。原因不是“这样可以加分”,而是当你调试问题的时候,分层能帮你快速定位。小程序请求超时了?先看Controller有没有收到请求。数据查不出来?去Mapper层看SQL。告警没触发?去Service层看判断条件。没有分层,所有代码挤在一起,排查起来会非常痛苦。
2.2 传感器数据表该怎么设计
监控类系统的数据表设计和普通业务系统有一个非常大的区别:历史数据量会持续增长,而且比用户数据增长快得多。
假设一个大棚里有10个传感器,每5秒上报一次,一天就会产生十几万条记录。虽然毕业设计的数据量不会真有这么大,但表结构必须从设计上考虑这个问题。
比较基础的表结构会包括这几张:
| 表名 | 主要字段 | 用途 |
|---|---|---|
| user | openid, nickname, avatar | 保存微信用户信息 |
| device | device_code, device_name, type, location, status | 管理传感器和设备 |
| sensor_data | device_id, sensor_type, value, collected_at | 保存采集数据,核心表 |
| alarm_record | device_id, alarm_type, message, status, create_time | 保存告警记录 |
| control_log | device_id, action, operator, create_time | 记录设备控制操作 |
其中sensor_data表是核心。它不要只存“当前值”,而要存每一次上报的时间和数据。因为只有保存了完整历史,前端才能绘制趋势曲线、计算平均值、生成日报。
一个容易踩的坑是把 sensor_data 设计成“一台设备一行、字段包含温度湿度和光照”。这样虽然查询单条数据很方便,但扩展性很差。今天要加一个土壤酸碱度传感器,就要改表结构加字段;明天要支持多个采集点,表会变得非常宽。更合理的做法是用“设备编号+传感器类型+值+时间”这样的长表结构,不同类型的传感器共用同一张表。
2.3 接口设计应该有哪些考虑
接口是后端和小程序之间的约定。一个典型的监控系统接口会包含这几类:
- 登录接口:小程序通过 wx.login 获取 code,后端再调用微信接口换取 openid。
- 设备接口:查询设备列表、设备详情、设备状态。
- 数据接口:查询实时数据、按时间段查询历史数据。
- 控制接口:下发设备开关指令。
- 告警接口:查询未读告警、标记已读。
以历史数据查询为例,通常需要三个参数:传感器类型、开始时间、结束时间。接口返回值可以包含时间点和数值列表,这样前端拿到的就是可以直接绘制曲线的数据。
@RestController @RequestMapping("/api/data") public class DataController { private final SensorDataService sensorDataService; public DataController(SensorDataService sensorDataService) { this.sensorDataService = sensorDataService; } @GetMapping("/history") public Result getHistory(@RequestParam String deviceId, @RequestParam String sensorType, @RequestParam String start, @RequestParam String end) { List<SensorDataVO> list = sensorDataService.getHistory(deviceId, sensorType, start, end); return Result.success(list); } }这里有个细节:时间参数最好统一成字符串格式,比如yyyy-MM-dd HH:mm:ss,前端传参和后端解析都会方便。如果你用时间戳,也不是不行,但小程序端和时间戳打交道时要注意单位,Java是毫秒,某些场景下是秒,混用会导致曲线错乱。
3. 小程序端不只是套模板,页面和信息架构要想清楚
3.1 页面拆解:首页状态、历史曲线、设备控制、告警列表
微信小程序在这个项目里的角色,是“让用户能随时看见大棚状态、接到告警、做出操作”。它不需要把所有功能都塞进一个页面,更忌讳堆一堆看不见实际用途的图表和按钮。
一个比较合理的页面结构是:
- 首页:展示当前环境状态卡片,例如“温度 26.5℃”“湿度 68%”,一眼能看清是否正常。
- 设备页:展示大棚里的设备列表,传感器状态、开关状态。
- 数据页:按传感器类型查看历史曲线,支持按小时、天、周切换。
- 告警页:展示未读告警,告警类型、时间、处理状态。
- 我的/设置页:用户信息、系统说明、常用设置。
为什么要这样拆?因为用户的需求是分场景的。日常工作里,人更想看“当前正不正常”;遇到异常时,想看“从什么时候开始异常的”;要调控设备时,才需要“操作开关”。如果所有信息堆在首页,信息过载,反而看不到重点。
另外一个关键点是首页默认展示的数据。不要一上来就说“要显示全部数据”,应该先想清楚:用户最关心什么?通常用户最关心的是当前有没有异常。所以首页应该有一块醒目的状态区域,正常时显示绿色、异常时显示红色,再配合数值卡片。
3.2 请求封装和数据刷新策略
小程序端直接调用wx.request是可以的,但项目一复杂,会出现大量重复代码,比如 baseUrl、请求头、错误提示、token 处理。更建议封装一个统一的请求模块。
const BASE_URL = 'http://localhost:8080'; function request(path, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method: method, data: data, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.data.code === 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg, icon: 'none' }); reject(res.data); } }, fail: (err) => { wx.showToast({ title: '网络错误', icon: 'none' }); reject(err); } }); }); } module.exports = { request };封装之后,页面里调用就变得很干净:
const { request } = require('../../utils/request'); Page({ data: { realtimeData: [] }, onShow() { this.loadRealtime(); }, loadRealtime() { request('/api/data/realtime', 'GET', { deviceId: '001' }) .then((data) => { this.setData({ realtimeData: data }); }); } });数据刷新策略上,有两种思路。一种是小程序端定时轮询,每几秒请求一次接口;另一种是用WebSocket建立长连接,由后端主动推送。毕业设计阶段用轮询就够了,因为实现简单、排查方便。答辩时可以说明“当前用轮询满足课堂演示,生产环境可以用WebSocket或MQTT替代”,反而能体现你思考过方案边界。
轮询要说清楚:页面离开时必须清除定时器。否则页面切到后台还在发请求,既浪费流量,又可能带来不必要的性能损耗。
3.3 数据可视化怎么做得真正有用
很多同学会在小程序里直接引入图表库,然后画一个很炫的3D曲线。但完成之后再回头看,往往发现这个图除了好看,并不能帮助用户判断问题。
一个真正有用的温室曲线图,至少要做到三点:
- 能切换传感器类型。温度和湿度走势放在一张图里反而会互相干扰。
- 能查看历史范围。只显示最近一小时,看长期变化趋势会很难。
- 要叠加阈值线。例如温度上限是35℃、下限是10℃,曲线图上画出两条阈值线,超限部分一眼可见。
这也是“精密”这个词的体现。所谓精密监控,不只是数据精确,而是信息组织得精确。用户看到图的第一眼,就能回答“当前正不正常”“何时开始异常”“持续了多久”这三个问题,而不是自己去查数值、比大小。
如果使用 ECharts 的微信小程序版本,需要注意它不能像浏览器端一样直接通过 DOM 操作。它是基于 canvas 渲染的,初始化方式和浏览器有差异。如果项目用原生小程序开发,建议找对应小程序适配版,或者用简单的 canvas 自己画折线图。后者工作量可控,但考虑答辩展示效果,图表库通常更稳。
4. 设备接入:不是每个人都有真实传感器,怎么把它做得可信
4.1 从硬件到后端的标准链路
走真实硬件链路时,常见方案是使用 ESP8266 或 ESP32 这类带WiFi功能的单片机连接传感器,然后通过HTTP请求把数据上报给SpringBoot接口。传感器类型可以是DHT11(温湿度)、光敏传感器、土壤湿度传感器等。
一条典型的请求是:
POST http://服务器地址:8080/api/data/upload Content-Type: application/json { "deviceId": "device_001", "temperature": 26.5, "humidity": 68.2, "light": 8200 }后端接收到数据后,先校验设备号是否存在、数据是否在合理范围,再写入sensor_data表。这部分逻辑和普通CRUD很像,但它多了一个隐含问题:设备端的网络可能不稳定,可能重复上报,可能上报的频率和后端处理的性能不匹配。
所以,后端接口必须做两件事。第一,接口尽量不要抛异常,哪怕数据有问题也要返回一个成功响应,避免设备端因为重试机制不断堆积请求。第二,要记录上报时间,不能依赖设备本地时间,因为设备时间和服务器时间可能不同步。
4.2 没有真实设备时,如何设计模拟数据源
大部分毕业设计的实际情况是没有硬件,或者硬件只在小范围演示时使用一次。这时候,模拟数据源就成了保证系统能持续演示的关键。
模拟数据源的设计思路是提供一个“程序化的传感器”。在SpringBoot里可以写一个定时任务,每隔几秒生成一条模拟数据,请求自己的上报接口,写入数据库。
@Component public class SensorDataSimulator { private final Random random = new Random(); private double temperature = 25.0; private double humidity = 60.0; @Scheduled(fixedRate = 5000) public void simulate() { // 温度在上一值附近波动,形成连续变化曲线 temperature += random.nextDouble() * 2 - 1; humidity += random.nextDouble() * 4 - 2; temperature = Math.max(10, Math.min(40, temperature)); humidity = Math.max(20, Math.min(95, humidity)); // 调用上报方法,写入数据库 dataUploadService.upload("device_001", temperature, humidity); } }这里最重要的细节是:模拟值必须在上一值附近波动,而不是每次完全随机。为什么?因为真实温室的环境变化是连续的,温度不会一秒内从20℃跳到35℃。如果每次都产生完全随机值,曲线图会呈现“脉冲状”,既不可信,也无法测试阈值告警的连续性。
注意:模拟数据源的目的是让系统可演示、可验证。答辩时建议提前准备好一批已经入库的数据,避免现场等待模拟数据慢慢生成。
4.3 时间对齐是比数值大小更容易忽略的问题
传感器数据里,最容易踩坑的不是数据格式,而是时间对齐。
设备上报到后端,链路中至少会有三个时间:设备采集时间、后端收到时间、数据库写入时间。如果设备本地时钟没校准,上报一条“温度35℃”的数据,时间却显示成昨天,前端画曲线时就会错位。
在毕业设计里,更稳定的做法是后端统一用服务器当前时间作为数据时间,或者允许上报请求携带采集时间,但后端要做范围校验,太离谱的时间直接拒绝或修正。
还有一个视觉层的小问题:前端绘制曲线时,如果按数据插入顺序排列而不按时间排序,一旦有数据迟到,曲线就会乱。所以不管是查询SQL还是前端排序,都要明确按collected_at升序排列。这个点看起来小,但在数据量多了之后很影响展示效果。
5. 预警与联动控制:从“看数据”到“用数据”
5.1 阈值告警不能只推送一句“温度异常”
温室监控做到“能看数据”只是第一阶段,真正有价值的是“数据异常时系统能主动发现问题”。这就涉及到阈值告警。
最容易写出来的告警逻辑是这样的:如果温度大于35,就插入一条告警记录。但这个逻辑太粗糙。因为它没有回答下面几个问题:
- 温度超过35持续了多久才告警?
- 告警是触发一次,还是持续触发?
- 温度回落后,告警是否自动关闭?
- 不同作物、不同阶段的阈值是否不同?
例如,茄果类蔬菜在开花结果期对温度很敏感,白天温度超过35℃容易落花落果;而叶菜类耐低温能力相对强一些。所以阈值不能写死在代码里,应该做成设备或大棚的配置项。
更合理的告警设计分三步:
- 定义阈值:给大棚配置温度上限、温度下限、湿度上下限。
- 判定条件:连续N次超过阈值才触发告警,降低偶发波动带来的误报。
- 恢复动作:数据恢复正常后,自动将告警置为“已恢复”,并在前端展示处理时间线。
这样设计之后,告警不再是一条孤立的提示,而是一个有时间跨度的事件。用户能看到“这台设备在什么时间段温度超过了多少,持续了多久,最后何时恢复”,这对评估环境调控效果非常有帮助。
5.2 联动控制的几种实现思路
联动控制,指的是当环境数据超过阈值后,系统自动或半自动地控制设备。比如温度过高时打开风机,湿度不足时打开灌溉。
毕业设计里常见的实现方式有三种:
| 方式 | 实现思路 | 复杂度 | 适合情况 |
|---|---|---|---|
| 手动控制 | 用户在小程序里点击按钮,后端下发指令 | 低 | 最基本的演示 |
| 手动+确认 | 系统检测到异常后,给用户推提醒,用户确认后执行 | 中 | 更接近真实场景 |
| 自动联动 | 后端定时扫描最新数据,超过阈值自动发指令 | 中高 | 演示效果更强 |
如果时间紧张,先做手动控制就够了。但要注意,控制操作一定要记录日志,包括操作人、操作时间、操作目标设备、操作结果。因为“谁在什么时候控制过哪台设备”在真实场景中是审计追踪的一部分。哪怕只是毕业设计,日志表也能让演示看起来更完整。
自动联动可以放在最后一个扩展里做。用SpringBoot的@Scheduled定时任务,每隔一分钟扫描一次设备上传的最近一条数据,超过配置的阈值就触发控制。这一步如果做得顺利,整个项目的“智能感”会明显提升。
注意:如果控制的是真实设备,需要谨慎考虑安全。毕业设计或演示场景下,控制设备前建议增加确认弹窗,并限制频繁操作,避免一次误触导致设备反复开关。
6. 从自己电脑跑到答辩演示,最大的坑往往不在功能
6.1 环境依赖:先确认这几样东西能跑起来
项目功能写得再好,环境跑不起来一切都白搭。SpringBoot + 微信小程序的开发环境依赖一般是下面几样:
| 依赖 | 说明 | 常见坑 |
|---|---|---|
| JDK | 推荐8或11 | 版本过高时部分依赖可能不兼容 |
| Maven | 管理后端依赖 | 镜像源配置会影响依赖下载速度 |
| MySQL | 数据库 | 时区配置会让时间字段偏移 |
| Redis | 可选,缓存或存储登录态 | 不是必选项,不要为了用而用 |
| 微信开发者工具 | 运行小程序 | 需要注册小程序测试号 |
这里要说清楚:开发阶段后端放在本机,需要让手机能访问,就必须保证手机和电脑在同一局域网,并且后端的服务地址不能写localhost,要写电脑的局域网IP。而且要关闭系统的防火墙拦截,或者放行对应端口。
如果实在不想处理局域网IP的问题,也可以用内网穿透工具。但这里不推荐在毕业设计里花太多时间折腾这层配置,因为它不是项目核心,却很容易消耗大量时间。
6.2 小程序请求失败的排查链路
小程序弱网环境、域名限制、证书问题、IP地址问题都会导致请求失败。接到问题不要慌,按照下面的顺序排查:
- 先看开发者工具里的网络请求状态码。是200、401、404还是500?
- 200但页面没数据:去看返回数据的结构,是不是数据层级取错了。
- 401/403:看是否带了token,登录态是否过期。
- 404:看Controller路径和小程序请求路径是否完全一致,包括大小写。
- 500:看后端日志的异常堆栈,绝大多数问题能一眼定位。
- 开发者工具能通但手机不通:重点查局域网IP、防火墙、域名合法性和HTTPS证书。
有一个非常容易踩的坑是:开发者工具调试时勾选了“不校验合法域名”,手机预览时忘了。手机预览会强制校验域名,如果你的后端是局域网IP,不是HTTPS域名,就会请求失败。这不是后端问题,而是小程序平台的限制。
6.3 后端和数据库的细节,决定演示能不能顺利进行
有些细节,不做项目时完全意识不到,做了之后才发现每一个都能让人卡住半小时:
- 数据库时区。如果MySQL连接串没有设置
serverTimezone=Asia/Shanghai,时间字段可能出现差8小时的问题。 - 时间字段类型。Java里
LocalDateTime和数据库datetime的精度要保持一致,否则查询时可能会丢毫秒。 - 批量插入。传感器数据写入频繁时,使用
INSERT INTO ... VALUES (...), (...), (...)批量插入会比逐条插入快很多。 - 日志打印。Controller入口一定要打日志,不然前端报错时你根本不知道请求到底有没有到后端。
- 统一返回值。接口尽量统一返回
{ code, msg, data }结构,前端处理和排查都会方便很多。
这些细节不会直接出现在功能列表里,但它们决定了项目能不能稳定演示。很多项目功能看起来完整,一演示就崩,基本都是这些环节出了问题。
7. 这个项目能走多远:毕业设计到真实系统的边界
7.1 当前方案够用的场景
先说清楚,这个方案适合什么场景。单栋或几栋温室、几十个传感器、每分钟到几秒级的数据采集频率、用户量很小,这套SpringBoot+小程序架构完全够用。它开发成本低、维护简单、毕业设计和中小型课程设计都能覆盖。
而且作为毕业设计,它的价值已经足够:前后端分离结构完整,覆盖了环境数据采集、展示、告警、控制等核心功能,数据库设计也符合监控类系统的特点。答辩时把数据链路、模块设计、异常处理讲清楚,已经很能说明工程能力。
7.2 要变成产品,数据层、传输层、硬件层各有缺失
如果把它放到真正的商业温室场景里,差距会变得很明显。
从传输层看,真实环境里传感器数量多、设备可能断网、数据可能延迟到达,HTTP协议在这种弱网环境下不一定可靠,工业场景更常用MQTT这类物联网协议。
从数据处理看,几万设备同时上报时,单机SpringBoot应用会面临性能瓶颈。真实系统通常会引入消息队列做削峰,比如数据先进入Kafka或RabbitMQ,再由消费者写入数据库,避免瞬时并发打崩数据库。
从设备层看,真实设备需要远程配置、固件升级、离线缓存、指令确认机制。这些都不是靠一个HTTP接口能解决的。
从前端看,小程序的轮询策略在设备数量增多后会加重服务器压力,生产环境往往需要WebSocket或消息推送替代,告警也要做去重、分级、升级通知,而不仅是简单的一条记录。
7.3 给不同目标的人一条建议路线
如果你的目标是顺利毕业:先跑通核心链路,也就是“传感器上报→后端存储→小程序展示→手动控制”这条主流程,再把演示数据准备好,把常见问题排查过程走一遍。不要贪多,一个链路完整的项目比五个半成品模块更有说服力。
如果你的目标是凭这个项目找工作:建议在现有基础上再补三样东西:一是接口的日志规范,二是统一的异常处理,三是有测试用例。这些是工程化习惯的体现。不需要为了追新而引入微服务,把单体应用做扎实,讲清楚设计取舍,已经足够打动面试官。
如果你是认真想把它做成一个能长期使用的系统:重点应该放在数据采集层的重构上,把HTTP上报换成MQTT协议,引入设备网关,增加断网补偿和数据清洗,然后再去考虑告警去重、权限分级和运维部署。
说到底,这个题目最值得学习的不是SpringBoot,也不是微信小程序,而是“如何把数据从物理世界稳定地搬到用户眼前”。这个过程里涉及的编码、协议、时间对齐、异常处理、接口边界,才是未来做任何业务系统都会用到的底层能力。把这条链路想明白,做任何监控类、IoT类、数据类项目都能举一反三。