这段时间接到好几个学生的私信,都在问同一个方向的毕设题目——“空气污染预警系统”。说实话,这个题的含金量比“图书管理”“宿舍管理”那类常规管理系统高不少:它有明确的数据计算逻辑(AQI指数)、有预警触发机制、有可视化大屏展示,在答辩时候能讲的东西多,写论文也有内容可写。更重要的是,SSM + Vue 这套前后端分离的组合,正好踩在当下企业主流技术栈的范围内,就算简历上写这一条,面试官也不会觉得是纯粹的“玩具项目”。
这篇文章就围绕这个项目标题展开,把技术选型、系统设计、核心代码、论文框架、部署避坑一次性讲清楚。内容按顺序分为五个部分:先聊整体架构和为什么这么选型,再拆数据库和核心算法,然后讲前后端联调和权限控制,接着给论文写作模板和答辩要点,最后是部署流程和常见问题速查。
1. 项目整体设计与技术选型思路
1.1 空气污染预警到底在解决什么问题
做项目之前先把业务想明白,不然代码写出来也是空的。空气污染预警系统要干的事情可以拆成三件事:第一是看得见,把监测站采集到的污染物浓度数据可视化呈现出来,用户能直观看到哪些站点超标;第二是算得准,把原始浓度数据换算成 AQI 空气质量指数,并且按国标划分等级;第三是跑得动,当指数超过阈值时自动触发预警,记录预警日志,提醒相关管理员。
业务模型想清楚之后,再去对照功能清单:站点管理、监测数据录入与查询、AQI计算与排名、预警规则配置、预警记录、用户登录与权限、数据看板。这些功能覆盖了 Web 开发常见的增删改查、计算逻辑、定时任务、图表渲染,恰好又是一套完整的闭环,写在任务书里很体面。
1.2 为什么是 SSM + Vue,而不是 Spring Boot 全家桶
很多同学上来就问:“现在企业都用 Spring Boot 了,毕设还用 SSM 是不是过时了?”我的回答是:SSM 没有过时,它只是把 Spring、SpringMVC、MyBatis 三个框架拆开摆在你面前,逼着你把底层的原理学明白。Spring Boot 默认帮你把配置全干了,写的时候很爽,但答辩时老师一句“拦截器是怎么配置的”,你可能就愣住了。
SSM 组合下,SpringMVC 的 Controller 层、Service 层、Mapper 层的职责边界非常清晰,论文里画分层架构图也顺手。Vue 负责前端渲染,通过 Axios 调后端 API,数据和界面分离,这就是标准的前后端分离模式。虽然 Spring Boot 也可以做同样的分离,但 SSM 在表达“框架原理”这件事上讲得更透,而这恰恰是毕业设计评分的关键项。
1.3 系统架构与前段链路设计
整个系统的请求链路是:Vue 页面通过 Axios 发起 HTTP 请求 → Nginx 或开发代理转发到 Tomcat → SpringMVC 的 DispatcherServlet 分发到 Controller → Controller 调 Service 层处理业务 → Service 调 MyBatis 的 Mapper 接口 → Mapper 通过 XML 映射执行 SQL → MySQL 返回结果 → 逐层封装成 JSON → 前端拿到数据渲染 ECharts 图表。
这个链路里每一步都对应论文里的一小节:表现层、业务逻辑层、数据持久层,答辩的时候照着链路讲,逻辑顺畅不卡壳。项目结构上做前后端分离两个项目目录,前端叫air-pollution-web,后端叫air-pollution-server,启动时后端占 8080 端口,前端 dev server 占 8081 端口,开发环境用 Vue CLI 的 proxy 转发解决跨域,生产环境打 jar/war 包部署。
2. 核心功能模块与数据库设计
2.1 数据模型:从站点到监测记录
先看数据库,这是整个系统的地基。我的建议是至少设计这几张表:station(站点表)、monitor_record(监测数据表)、warning_config(预警阈值配置表)、warning_log(预警记录表)、user(用户表)、role(角色表)。
站点表保存站点名称、区域、经纬度;监测数据表是核心业务表,一次插入一条记录时带上六项污染物浓度:PM2.5、PM10、SO2、NO2、CO、O3。注意这里一定要用datetime类型存监测时间,不要用varchar,否则后面做时间范围查询和按天分组聚合会特别痛苦。预警日志表记录触发时间、站点ID、AQI值、预警等级、处理状态,方便导出报表。
建表的时候把外键逻辑想好,站点和监测记录是一对多,预警配置和预警日志是一对多,用户和角色是多对多(用中间表user_role关联)。下面是站点表和监测表的参考 SQL:
CREATE TABLE `station` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL COMMENT '站点名称', `region` VARCHAR(50) COMMENT '所属区域', `longitude` DECIMAL(10,6), `latitude` DECIMAL(10,6), `status` TINYINT DEFAULT 1 COMMENT '1启用 0停用', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE `monitor_record` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `station_id` INT NOT NULL, `pm25` DECIMAL(10,2), `pm10` DECIMAL(10,2), `so2` DECIMAL(10,2), `no2` DECIMAL(10,2), `co` DECIMAL(10,2), `o3` DECIMAL(10,2), `monitor_time` DATETIME NOT NULL, KEY `idx_station_time` (`station_id`, `monitor_time`) );2.2 AQI 计算逻辑:论文里最硬的干货
AQI 是这套系统里最有技术含量的地方,我建议把计算公式完整写进论文正文,并在代码里按公式严格实现。AQI 的计算分三步:
第一步,根据各项污染物的实测浓度,查询该污染物对应的浓度限值区间。注意国标给的是七个区间,步长各不相同。第二步,用分段线性插值公式计算单项空气质量分指数 IAQI,公式是:
IAQI = (AQI_Hi - AQI_Lo) / (C_Hi - C_Lo) × (C - C_Lo) + AQI_Lo
其中 C 是实测浓度,C_Lo 和 C_Hi 是限值表中该浓度所在区间的下限和上限,AQI_Lo 和 AQI_Hi 是对应的指数区间上下限。第三步,当前站点的 AQI 取所有污染物 IAQI 中的最大值,即 AQI = max(IAQI_pm25, IAQI_pm10, IAQI_so2, ...),该项污染物的 IAQI 最大,就当它是首要污染物。
我直接贴一段可运行的 Java 工具类核心代码:
public class AqiUtil { // 以 PM2.5 为例:浓度下限、浓度上限、IAQI下限、IAQI上限 private static final double[][] PM25_BRACKETS = { {0, 35, 0, 50}, {35, 75, 50, 100}, {75, 115, 100, 150}, {115, 150, 150, 200}, {150, 250, 200, 300}, {250, 350, 300, 400}, {350, 500, 400, 500} }; public static int calcIaQi(double concentration, double[][] brackets) { for (double[] b : brackets) { if (concentration >= b[0] && concentration <= b[1]) { double cLo = b[0], cHi = b[1], aqiLo = b[2], aqiHi = b[3]; return (int) Math.round((aqiHi - aqiLo) / (cHi - cLo) * (concentration - cLo) + aqiLo); } } // 超过上限,按 500 封顶 return 500; } public static int calcAqi(double pm25, double pm10, double so2, double no2, double co, double o3) { int iaqiPm25 = calcIaQi(pm25, PM25_BRACKETS); // 其余污染物同样用对应的限值区间表计算 return Math.max(iaqiPm25, Math.max(iaqiPm10, ...)); } }AQI 等级划分也要写明白:0-50 优(绿色)、51-100 良(黄色)、101-150 轻度污染(橙色)、151-200 中度污染(红色)、201-300 重度污染(紫色)、300 以上严重污染(褐红色)。前端显示颜色直接对照这个表。
2.3 预警规则与定时任务触发
预警不是用户点一下“刷新”才检查的,而是要有后台定时任务自动扫描。我的做法是引入 Quartz 定时框架,配置一个每小时的 Job:Job 里查出所有启用状态的站点,取最近一条监测记录算 AQI,再和warning_config表里的阈值比较,超过阈值就写一条warning_log,同时在站点的当前状态字段上标记预警等级。
预警等级我设计了四级:蓝色预警(AQI > 100)、黄色预警(AQI > 150)、橙色预警(AQI > 200)、红色预警(AQI > 300)。阈值不要写死在代码里,因为论文里要体现“可配置”的设计思想,预警规则做成配置项就是加分项。另外,同一站点同一时段不要重复生成日志,Job 里要做幂等校验:判断最近一条预警日志的生成时间是否在 1 小时内,如果在则跳过。
3. 前后端分离下的接口设计与权限联调
3.1 接口规范:后端只管数据,前端只管渲染
前后端分离项目里,接口设计直接决定联调效率。我的习惯是先列接口文档,再后端写接口,再前端写页面。接口统一返回 JSON 对象,格式是{code: 200, message: "success", data: {...}},前端拿到后先看 code 再处理 data,错误信息统一弹出 Message 组件展示。
核心接口大概是这样一张表:
| 功能描述 | 请求方法 | 接口路径 | 说明 |
|---|---|---|---|
| 获取所有站点 | GET | /api/station/list | 返回启用状态站点 |
| 获取实时监测数据 | GET | /api/monitor/latest | 各站点最新一条记录 |
| 获取历史趋势 | GET | /api/monitor/history?stationId=1&days=7 | 用于折线图 |
| 当前预警列表 | GET | /api/warning/current | 按时间倒序 |
| 配置预警阈值 | POST | /api/warning/config | 管理员操作 |
| 获取污染排行 | GET | /api/rank/aqi | 按 AQI 倒序 |
| 登录 | POST | /api/auth/login | 返回 JWT token |
| 获取当前用户 | GET | /api/auth/info | 携带 token 访问 |
前端的页面模块也随之确定:登录页、大屏看板(实时数据 + ECharts 曲线 + 排行表格)、监测历史页、站点管理页、预警管理页、用户管理页。路由用 Vue Router 配置,大屏看板是默认首页,登录后跳转到看板。
3.2 ECharts 大数据看板的实现细节
可视化这块最容易出效果,也最容易踩坑。我的组合是 Vue 2 + ECharts 5,看板顶部放四个指标卡片,展示 AQI 均值、污染站点数、预警中站点数、首要污染物占比;中间区域用折线图展示各站点最近 24 小时 AQI 变化趋势;底部放两个图表,左侧饼图展示各污染物超标占比,右侧条形图展示 AQI 排行前五的站点。
ECharts 在 Vue 里的正确用法是 mounted 里初始化实例,watch 数据变化后调setOption更新,组件销毁前调dispose销毁实例。之前有个同学把setOption写在 created 里,结果图表容器还没渲染,宽度是 0,图表死活不显示。遇到这种情况检查两点:容器是否有固定高度、初始化是否在 DOM 挂载之后。
3.3 基于拦截器 + JWT 的登录鉴权
权限是所有管理系统的底线,空气污染预警系统也不例外。后端我用 JWT 做 Token 鉴权,用户登录成功后把用户 ID、用户名、角色封装进 Token,设置 24 小时过期时间,返回前端。前端把 Token 存 localStorage,Axios 请求拦截器里加Authorization: Bearer xxx。
后端 SpringMVC 里写一个TokenInterceptor拦截器,继承 HandlerInterceptor,在 preHandle 方法里从请求头取 Token,解析失败或过期直接返回 401 JSON,不放行。配置拦截器规则时注意放行/api/auth/login和静态资源,其他接口全部拦截。这里有个小坑:拦截器只拦截 Controller 路径,不拦截静态文件,不用担心 JS、CSS 被拦,但 Swagger 之类的接口文档页面要单独放行。
4. 论文写作框架与答辩要点
4.1 毕设论文的章节安排
空气污染预警系统这套题写论文,推荐按七章结构走:第一章绪论(背景意义、国内外研究现状、论文结构);第二章相关技术介绍(SSM 框架、Vue、MySQL、ECharts、JWT);第三章需求分析(可行性分析、功能需求、用例图);第四章系统设计(总体架构、功能模块设计、数据库设计);第五章系统实现(每个功能模块的核心代码和截图);第六章系统测试(测试用例表、测试结果);第七章总结展望。
写作顺序上先写三四章,因为数据库表结构一旦确定,后端的 Mapper 才有的放矢;再写二章和五章,最后补绪论和摘要。摘要一定要在全文写完后再写,不然写出来的摘要跟正文对不上,答辩会被老师一眼看穿。
4.2 为什么说需求分析是拿分大项
很多学生论文的需求分析章节写得很敷衍,就丢两张用例图上去,但实际上这一章才是老师判断你是不是真懂业务的地方。空气污染预警系统的用例图要画四种角色:普通用户(浏览实时数据)、监测站管理员(录入、修改监测数据)、系统管理员(配置预警阈值、管理站点)、游客(仅看公开看板)。
用例图之外还要写业务流程图,描述“数据从采集到预警”的全过程。这张流程图一定要把 AQI 指数计算、阈值判断、预警日志写入、消息通知四个环节串起来,画完之后你会发现第五章节的代码实现小节全都对应上了。
4.3 答辩高频追问与应答思路
答辩的时候老师大概率会问这几类问题,提前准备好:
第一,“AQI 计算哪个污染物占主导?”答:AQI 取各污染物 IAQI 最大值,对应项为是首要污染物,比如某站点 PM2.5 的 IAQI 最大,首要污染物就是 PM2.5。
第二,“为什么用 SSM 不用 Spring Boot?”答:SSM 分层清晰,MyBatis 手写 SQL 更灵活,适合教学场景下体现框架原理;Spring Boot 是简化封装,两者核心容器都是 Spring。
第三,“预警任务是怎么定时执行的?”答:Quartz 按小时触发扫描任务,先幂等校验再计算 AQI,超过阈值写日志。
第四,“前端跨域怎么解决的?”答:开发环境用 Vue CLI proxy 代理,生产环境用 Nginx 反向代理,将/api前缀转发到后端。
5. 从 0 到 1 的开发部署流程与避坑实录
5.1 环境准备与启动顺序
整套项目跑起来需要这些环境:JDK 1.8+、Maven 3.6+、Tomcat 8.5(或用内置容器)、MySQL 5.7+、Node.js 14+、npm、Vue CLI 4+ 或 Vite。建议用 IDEA 开发后端,VSCode 开发前端,数据库工具用 Navicat 或 DataGrip。
启动时先创建数据库并导入项目里的sql脚本,再启动后端:IDEA 里直接运行 SpringMVC 的 WebInitializer 配置类,或打成 war 包丢进 Tomcat。后端启动成功后用 Postman 先测/api/auth/login,拿到 Token 后测其他接口,确认接口都通再启动前端。前端启动命令npm run serve,启动前检查vue.config.js里的 proxy 配置有没有指向 8080。
后端 Maven 项目如果是从零搭建,核心依赖就九个:spring-webmvc、spring-jdbc、mybatis、mybatis-spring、mysql-connector-java、jackson-databind、jjwt、quartz、lombok。不要贪多,依赖越多启动越容易出问题。
5.2 高频问题与排查思路速查表
我把做这个项目最常见的坑整理成一张表,每个都是实际踩过的,直接照单排查:
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 前端请求 404 | proxy 路径写错或后端没启动 | 核对 /api 前缀和 8080 端口 |
| 前端请求 401 | Token 过期或未携带 | 重新登录,检查 axios 拦截器 |
| CORS 跨域报错 | 前后端端口不同未走代理 | vue.config.js 配置 devServer.proxy |
| 日期字段返回格式不对 | Jackson 没配日期格式 | 加 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| ECharts 图表空白 | 容器高度为 0 | 给容器加固定 height: 400px |
| 定时任务不触发 | Quartz 配置表达式错误 | 检查 cron 表达式,如 0 0 * * * ? 表示每小时 |
| Maven 依赖下载慢 | 默认中央仓库 | settings.xml 配置阿里云镜像 |
| Vue 路由刷新 404 | history 模式服务端没兜底 | 改用 hash 模式,或 Nginx 配置 try_files |
| MySQL 时区报错 | 连接串缺 serverTimezone | URL 加 ?serverTimezone=Asia/Shanghai |
5.3 演示前千万别忘的细节
答辩演示比代码本身重要,至少提前一天做三轮自测。第一轮按业务流程走:登录 → 看大屏 → 查历史 → 管理站点 → 配置预警 → 查看日志;第二轮测边界:输入负值浓度会不会报错、访问无权限页面能不能拦截住、停用站点后数据是否消失;第三轮做数据准备:提前往数据库里插入最近 7 天的测试数据,保证大屏图表有内容可展示。
还有一个小技巧:把预警阈值临时调低一点,比如把蓝色预警阈值调成 60,这样演示时现场录入一组 PM2.5 超标数据,预警日志立刻生成,效果非常直观。调低阈值前记得截图或备份原值,演示完再调回来。
空气污染预警系统做到这个程度,无论是代码量、业务复杂度还是论文可写性,都已经超过普通管理系统的毕业设计标准。我个人做下来的体会是,这类型题目最大的价值不是技术有多新,而是把“数据分析、规则引擎、可视化呈现”这条链路完整走通了一次,答辩时老师问的每一个细节你都能用实际代码和运行逻辑去回答,自信感是完全不一样的。最后再提醒一句:论文里的截图务必从自己的系统里截,数据库中测试数据要覆盖多种 AQI 等级,这几张图就是你的门面。后续如果想扩展,还可以接入实时地图点位展示或增加日报邮件推送,但先把核心链路做扎实,这条路一定走得顺。