如果你拿到的题目是《2026 毕设 SSM+Vue 简单天气查询 App 论文+程序》,先说一句:这个题看起来简单,但它把“后端框架、前端框架、第三方接口对接、数据库设计、论文写作”全凑齐了,属于很经典的本科毕设题型。它要求你最后交付两样东西:一套能跑起来的天气查询应用,和一篇能说清楚“为什么这么做、怎么做、结果如何”的毕业论文。
这类题目最适合两种人:一是已经学过 Java Web、但还没完整做过前后端分离项目的人,二是想用稳妥技术栈保毕业、不希望折腾复杂分布式架构的人。只要你把 SSM 的后端接口、Vue 的前端页面、第三方天气数据的解析流程走通,再把每个环节写进论文,整个项目就会显得完整且有说服力。下面我把整个设计和实现过程拆开讲,从程序到论文,按实操顺序一步步来。
1. 毕设题目拆解:别看简单,坑都在细节里
1.1 题目到底让你做什么
先把题目里的关键词拆开看。“SSM”指的是 Spring + SpringMVC + MyBatis 这套经典 Java 后端组合;“Vue”指的是前端采用 Vue 框架;“天气查询 App”并不是让你做 iOS 或者安卓原生 App,而是做一个以移动端风格展示为主的 Web 应用,通常也叫 H5 应用;最后的“论文 + 程序”则是说,光会写代码不够,你还要能拿出一篇结构完整的毕业设计论文。
这个题的边界其实很清晰:用户注册登录后,输入城市名,系统调用第三方天气接口获取实时天气和未来几天的预报,再展示到前端页面上。再加上用户查询历史、常用城市管理、天气趋势图表这些功能,就能撑起一个 2 万行左右的课程级项目。注意,核心不是把天气 API 调通,而是要把“用户管理、接口对接、数据缓存、前端展示、测试、论文描述”串成一条完整链路。
1.2 为什么选 SSM + Vue,而不是 Spring Boot
很多同学会纠结:现在都 2026 年了,毕设还用 SSM,是不是过时了?我的观点是:如果题目明确写了 SSM,就老老实实用 SSM。这不是让你去追新技术,而是让你把 Java Web 的基本功展示出来。Spring 管对象创建和依赖注入,SpringMVC 管请求路由和参数绑定,MyBatis 管 SQL 和数据库映射,这三层结构在答辩时很容易讲清楚。Spring Boot 虽然简化了配置,但很多学校的课程设计和毕设题目还以 SSM 为主线,题目本身就是一个约束,先满足题目要求再说。
Vue 选 2 还是 3,取决于你熟悉哪个。毕设选题写下 Vue,一般没有强制版本,如果你学过 Vue2,就用 Vue2 + Element UI,教程多、资料全;如果你已经习惯 Vue3 + Element Plus,也可以。关键是要保证前后端能够通过 JSON 数据正常通信,这一点比版本新旧更重要。我建议不要在 Vue 版本上花太多时间纠结,选一个你最熟、能两天写完整套页面的方案。
1.3 程序是论文的素材,不是论文本身
程序部分和论文部分不是两件事,而是“实现”和“解释”的关系。评审老师看论文时,会重点关注你做了什么、为什么这样做、有没有自己的思考。程序里一个简单的“查询天气前先从缓存读取”的设计,写成论文就是“系统缓存策略分析与实现”,这既有技术点,又有优化意识,特别适合作为答辩亮点。
反过来,如果你只把代码跑通,论文却写得很流水账,答辩也会很痛苦。所以我的建议是,先搭好程序框架,边做边记录功能列表、数据库字段、接口返回格式、测试截图。这些记录后期会直接变成论文里的需求分析、系统设计、系统实现和系统测试章节,能帮你省下大量返工时间。
2. 程序核心功能设计与数据库准备
2.1 功能模块怎么划分最合理
天气查询 App 虽然叫“简单”,但一个合格的毕设至少要有用户模块、天气模块、历史记录模块和前端展示模块。用户模块负责注册、登录、退出,用户登录后才能在查询历史里保留记录;天气模块负责接收前端传来的城市名,调用第三方接口,返回实时天气、体感温度、风向风力、相对湿度、未来几天的天气预报;历史记录模块负责把每次查询的关键信息写入数据库,方便用户查看和删除;前端展示模块负责把数据可视化,比如今日天气卡片、7 天预报列表、24 小时温度趋势图。
功能不要贪多。有些同学会在天气模块里加入空气质量、穿衣建议、限行提醒、多城市对比,这些不是不可以,但每加一个功能,论文就要多写一节的实现和测试,工作量会成倍增加。我做的版本只保留“用户登录 + 城市查询 + 天气展示 + 历史记录”,然后把 7 天预报做成 ECharts 折线图,视觉上就很丰富了。
2.2 数据库表设计:四张表足够支撑论文
数据库是整个系统的地基,也是论文里数据库设计章节的主要内容。我建议不要用太多表,四张表就够了,分别是用户表、城市表、天气缓存表、查询历史表。
| 表名 | 主要字段 | 用途 |
|---|---|---|
| user | id、username、password、nickname、avatar、create_time | 用户注册登录 |
| city | id、city_name、city_code、province、hot_sort | 常用城市维护 |
| weather_cache | id、city_name、weather_json、create_time、expire_time | 缓存第三方天气数据 |
| query_history | id、user_id、city_name、weather_text、temperature、query_time | 记录用户查询行为 |
用户表里的password建议不要存明文,用 BCrypt 加密后存储。city 表可以用来准备热门城市列表,比如北京、上海、广州、深圳、杭州、成都这些,前端做成一排按钮,用户点击就能查询,体验比每次手动输入城市名好得多。weather_cache 表是整个项目里最有价值的表,因为第三方天气接口都有调用频率限制,直接用缓存能避免每次查询都请求外部接口,这也是论文里可以重点写的一部分。
2.3 第三方天气接口怎么对接
对接第三方天气接口,本质就是后端发一个 HTTP 请求,拿到 JSON,再把它转换成 Java 对象返回给前端。在 SSM 项目里,通常使用 Spring 提供的 RestTemplate 来做请求发送。你可以先在浏览器里调一次接口,把返回 JSON 的结构保存下来,再照着字段定义实体。
以现在的天气接口为例,请求一个城市的实时天气,一般需要的参数是城市名称或城市编码,以及开发者密钥。把城市名作为location参数时,最好先做 URL 编码,因为中文城市名直接拼在 URL 里容易出现乱码。接口返回的字段大致有temp温度、text天气现象、windDir风向、windScale风力等级、humidity相对湿度、precip降水量、pressure大气压强、vis能见度,以及daily数组里的每日最高最低温度。
需要特别注意,不要把第三方接口的原始 JSON 原封不动直接塞给前端。正确做法是在后端定义一个WeatherDTO,只保留你需要展示的字段,把原始数据结构转换成自己系统的数据结构。这样即使第三方接口哪天改字段名,你只需要改后端转换层,前端页面不需要跟着变。如果出现网络超时或者接口密钥失效,后端还要捕获异常,返回一个友好的错误信息给前端,不能直接把堆栈信息抛出来。
2.4 缓存策略:别让每次查询都打到第三方接口
天气数据的特点是变化有周期性,实时更新基本在半小时内就够用。如果用户每次点查询都去请求第三方接口,既慢又不稳定,免费额度也很快用完。我的做法是设置 30 分钟缓存:每次查询前,先根据城市名去weather_cache表查最近 30 分钟是否有数据,有就直接返回,没有再去远程接口拉,拉完写回缓存。
缓存表的核心字段是city_name和expire_time。查询的时候判断当前时间是否小于expire_time,如果是就算命中。命中率不用做得特别精细,按“用户总共 1000 次访问,其中 70% 命中缓存”来估算,实际打到第三方接口的只有 300 次,对免费额度非常友好。这个设计在论文里写起来也很有逻辑:先说明第三方接口调用成本,再提出缓存方案,最后用测试数据验证缓存减少了外部请求次数。
3. 实操实现:从后端接口到前端页面
3.1 后端工程结构与登录鉴权
SSM 项目不要把所有类都堆在同一个包下,建议按职责分层。我习惯建这几个包:controller、service、service.impl、dao、entity、common、config、util。controller 接收前端请求,service 写业务逻辑,dao 写 MyBatis 接口和 XML,common 放统一返回结果类 Result、异常类、分页对象,config 放跨域配置和拦截器配置,util 放 JWT 工具和天气接口调用工具。这种结构一目了然,后期写论文时还能直接画成包结构图。
用户登录鉴权这里,我推荐用 JWT,也就是无状态令牌。用户登录成功后,后端生成一个 token 返回给前端,前端保存在 localStorage 里。后续每次请求,前端在请求头里带上Authorization: Bearer 用户token,后端用一个拦截器统一校验。这样比传统 Session 更适合前后端分离,而且写起来也不复杂。拦截器要记得把登录、注册这两个接口放行,其他接口统一走 token 校验。
// 统一返回结果示例 public class Result { private Integer code; private String msg; private Object data; public static Result ok(Object data) { Result r = new Result(); r.code = 200; r.msg = "成功"; r.data = data; return r; } public static Result error(String msg) { Result r = new Result(); r.code = 500; r.msg = msg; return r; } }JWT 工具类里核心方法有两个:一个是根据用户 id 生成 token,一个是解析 token 获取用户 id。密钥不要写死在代码里,放到配置文件里,答辩的时候还能讲一句“密钥外置,提高安全性”。
3.2 天气查询接口的完整链路
天气查询接口是整个项目的核心,前端会传过来一个city字段,后端收到的流程应该是:校验城市名是否为空、确认用户已经登录、查缓存、缓存未命中则调用第三方接口、保存缓存、保存查询历史、返回天气数据。这个链路可以用下面的伪代码来理解,实际代码结构也是照这个写。
@PostMapping("/query") public Result query(@RequestBody QueryReq req, HttpServletRequest request) { String city = req.getCity(); if (StringUtils.isBlank(city)) { return Result.error("城市不能为空"); } Long userId = JwtUtil.getUserId(request.getHeader("Authorization")); WeatherDTO weather = weatherService.queryWeatherByCity(city, userId); return Result.ok(weather); }在 service 实现里,先尝试从weather_cache表读取。这里我用了一个 SQL 查询:按城市名查,并且expire_time大于当前时间,结果按创建时间倒序,只要最接近的一条。如果查到,直接解析 JSON 返回;如果没查到,就调用weatherApiClient.getWeather(city),那里封装了 RestTemplate 的请求过程。拿到结果后,再调用 Mapper 的upsert方法,有记录就更新,没记录就插入。
天气接口调用层要注意两点:一是设置合理的超时时间,我一般设连接超时 3 秒、读取超时 5 秒,避免接口卡住整个请求;二是要做好参数编码,用URLEncoder.encode(city, "UTF-8")转码。第三方天气接口返回的字段非常多,我只需要其中十几个,所以转换的时候要写一个实体类,字段名尽量用英文下划线风格还是驼峰风格保持一致,避免 JSON 解析时对不上。
3.3 Vue 前端如何对接接口
前端部分我建议先用 Vue CLI 创建项目,再装axios、element-ui、echarts和vue-router。页面总共四五个就够:登录页、注册页、首页天气展示、历史记录页、个人中心页。首页是最重要的,顶部放城市搜索框和热门城市按钮,中间放当前天气卡片,下面放未来几天预报列表,再往下放一个温度趋势折线图。
axios 请求要统一封装。创建一个request.js,在请求拦截器里读取 localStorage 里的 token,加到请求头;在响应拦截器里统一处理 code,如果 token 过期,就跳回登录页。这样每个页面都不需要重复写鉴权逻辑。
// 请求封装示例 import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('weather_token') if (token) { config.headers.Authorization = 'Bearer ' + token } return config }) request.interceptors.response.use(res => { if (res.data.code !== 200) { return Promise.reject(new Error(res.data.msg)) } return res.data.data }, err => { if (err.response && err.response.status === 401) { localStorage.removeItem('weather_token') window.location.href = '/login' } return Promise.reject(err) })天气展示页面的核心是把后端返回的WeatherDTO绑定到页面上。比如weather.now.temp显示当前温度,weather.now.text显示天气现象,weather.daily用v-for渲染成天气列表。ECharts 折线图则是在组件mounted之后初始化,把未来 7 天的最高温和最低温分别作为两条折线。要注意初始化图表必须在 DOM 渲染完成后,放在created里初始化经常会拿不到容器,这是一个很常见的坑。
开发时前后端分离,可以用 Vue CLI 的代理解决跨域。在vue.config.js里配置一个 proxy,把/api开头的请求转发到后端的localhost:8080。这样前端开发地址是 8081,后端 Tomcat 地址是 8080,两者互不干扰。正式部署时,可以简单点,把前端打包后的静态文件放到后端 webapp 目录下,或者放在同一个 Tomcat 的 ROOT 下,让前后端同源,连跨域问题都不用考虑了。
3.4 启动部署的四步流程
我把自己整理出来的部署流程放在这里,严格照做一般不会出问题。第一步,在 MySQL 里导入weather.sql,把四张表建好,记得创建数据库时指定utf8mb4,不然中文城市名容易乱码。第二步,修改后端配置文件里的数据库账号密码、第三方天气接口密钥、JWT 密钥。第三步,启动后端,用 IDEA 直接跑 Tomcat,或者用 Maven 打包成 war 后扔到 Tomcat 的 webapps 目录。第四步,启动前端,开发模式就用npm run serve,正式模式就用npm run build把 dist 目录部署到 Web 服务器。
启动之后先用 Postman 测一次登录接口,拿到 token,再带 token 查天气,确认返回 JSON 正常。不要一上来就打开浏览器点页面,否则出了问题分不清是前端的问题还是后端的问题。接口层通了,页面展示就是绑定字段的体力活。整个项目从零开始,我大概用了 5 到 7 天,其中数据库设计 1 天,后端接口 2 天,前端页面 2 天,联调测试 1 天,算是比较顺畅的时间安排。
4. 论文写作:结构、图表和答辩素材一次搞定
4.1 论文章节结构参考
毕业论文不是把需求文档堆在一起,而是要有一个完整的故事线。如果你的指导老师没有强制要求格式,可以参考这个结构:摘要、Abstract、第一章绪论、第二章相关技术介绍、第三章系统需求分析、第四章系统设计、第五章系统实现、第六章系统测试、第七章总结与展望、参考文献、致谢。
第一章绪论要写清楚做这个系统的背景和意义,但不要空喊“互联网发展越来越快”这种套话。可以直接说:天气信息与日常生活密切相关,现有查询工具功能繁杂,本课题设计一个轻量级天气查询系统,方便用户快速获取实时天气信息。第二章相关技术介绍,逐个写 SSM、Vue、MySQL、第三方天气接口的基本概念和在本系统中的作用。第三章需求分析,画出用例图,列出功能需求和非功能需求。第四章系统设计,包含总体架构图、功能模块图、数据库 ER 图、表结构设计、接口设计。第五章系统实现,按功能模块展示代码片段和运行截图。第六章系统测试,列测试用例、测试结果、问题修复过程。
4.2 画图和表格比长篇代码更重要
论文里最忌讳大段贴代码。评审老师看论文时,首先看到的是图表是否清晰、章节是否完整,而不是代码行数。项目里值得画的图至少有这些:系统总体架构图、功能模块图、用户用例图、数据库 ER 图、程序流程图、时序图。这些图不用画得多高级,用常见的绘图工具就能完成,重点是图里的命名要和论文正文一致。比如架构图画了三层:表现层 Vue、业务层 SpringMVC + Service、数据层 MyBatis + MySQL,那么正文里也要按这个分层去描述。
表格的用处同样重要。数据库表结构要整理成字段表格,包含字段名、字段类型、是否为空、说明;测试部分要整理成测试用例表格,包含测试编号、测试模块、测试步骤、预期结果、实际结果。表格能给论文增加很多篇幅,而且看起来非常专业。我在写论文时,会把第五章每个模块的截图和功能说明做成一对一的描述,先放一张运行截图,再写两段“该页面实现的功能是什么、关键代码逻辑是什么、效果符合预期”,一节内容自然就充实了。
4.3 如何把实现细节写成论文语言
很多同学会写“这里调用天气接口,得到数据,展示到页面”。这种描述太单薄。论文语言需要把“为什么”也加进去。比如同样是写缓存,你可以这么写:考虑到第三方天气接口调用频率受限,同时天气数据不具备强实时性,系统采用 30 分钟缓存策略。查询时优先读取缓存,若缓存不存在则调用远程接口,并在成功后更新缓存,以此减少网络开销,提高响应速度。这样写,技术深度和逻辑性都比单纯叙述步骤强得多。
第五章系统实现也要有取舍。代码只保留核心部分,比如天气查询 service 实现的逻辑、JWT 工具的生成与解析、前端拦截器、ECharts 初始化,其余结果集处理、分页代码可以不展示。每个代码片段前面用一句话说明这段代码实现的功能,后面用一段话解释关键点。这样导师会觉得你确实理解了代码,而不是拼凑出来的。
5. 常见问题与排查实录:我踩过的 10 个坑
5.1 高频问题速查表
做这个项目的过程中,最容易出问题的不是业务逻辑,而是环境、编码和跨域这些细节。我整理了一张高频问题表,你遇到类似现象可以直接对照处理。
| 问题现象 | 主要原因 | 解决思路 |
|---|---|---|
| 登录接口一直返回 401 | 拦截器没有放行登录接口 | 在拦截器配置里排除/auth/login、/auth/register |
| 前端显示“网络错误” | 跨域请求被拦截 | 后端配置 CORS 过滤器,允许来源和请求头 |
| 查询城市中文乱码 | 请求或数据库编码不一致 | 全局请求设置 UTF-8,数据库连接加characterEncoding=utf8 |
| 数据库字段读出为 null | MyBatis 驼峰映射未开启 | 在 MyBatis 配置里开启mapUnderscoreToCamelCase |
| 天气数据有了但图表空白 | ECharts 初始化过早 | 在mounted中初始化,并确保 DOM 已加载 |
| 第三方接口偶尔超时 | 没有设置超时时间 | 给 RestTemplate 设置连接和读取超时 |
| token 刷新后失效 | 前端存储方式不合适 | 使用 localStorage 持久化 token |
| war 包部署后 404 | 上下文路径不对 | 部署后确认访问路径包含 contextPath |
| 数据库中文变成问号 | 建库时不是 utf8mb4 | 数据库、表、连接 URL 统一使用 UTF-8 |
| 前端页面刷新后 404 | Vue Router history 模式未配置 | 改用 hash 模式,或用服务器重定向配置 |
5.2 数据字段和 JSON 转换的细节
天气接口的 JSON 字段名经常是驼峰和下划线混着来,比如wind_dir表示风向,wind_scale表示风力。如果你在 Java 实体里定义的字段是windDir,就必须让它和 JSON 字段对得上。MyBatis 查询结果集默认不会自动把create_time映射成createTime,需要在mybatis-config.xml里开启驼峰映射,或者直接在 SQL 里写别名。这一条能解决很多“数据库明明有值,对象却是空”的问题。
JSON 序列化也有坑。Java 的LocalDateTime在默认序列化时,可能输出成数组格式,前端拿到后没法直接展示。解决方法是配置 Jackson 的日期格式化,统一输出为yyyy-MM-dd HH:mm:ss。返回前端的对象里,数值类型字段要注意不能返回 null,比如温度显示成null℃很影响观感,可以在 DTO 里给字段设置默认值,或在前端模板里做空值判断。
5.3 部署和答辩需要注意的细节
答辩最容易翻车的是现场环境。天气查询项目依赖第三方接口,如果答辩教室网络不稳定,页面会一直转圈。我的建议是提前做一个“离线降级方案”:后端在天气接口调用失败时,从缓存表读取最后一次成功的数据,如果也没有,则返回固定的演示数据。这样即使断网,页面也不会白屏。这个设计不仅可以防止尴尬,还能在答辩时作为“系统容错处理”亮点说出来。
另一点是端口问题。很多同学本地把后端放在 8080,前端放在 8081,但答辩电脑上可能已经被其他程序占用了。提前在答辩前用同一台电脑跑一遍完整流程,保证不需要临时改配置。如果前端代理配置的是localhost:8080,而后端这次跑在 8082,那就要提前统一端口,不要现场改代码。用相对路径的静态部署方案会稳很多,把前后端合并到同一个 Tomcat 下,答辩时直接启动 Tomcat 就能访问。
6. 项目做完后,我再分享几点实际体会
这套题目最大的优点是可以“稳中求亮点”。SSM 虽然有大量配置,但正因为如此,你可以在论文里写清楚 Spring IoC 如何管理 Service、SpringMVC 如何做请求分发、MyBatis 如何映射 SQL,这些都是评委喜欢听到的“原理理解”。
我个人做完后的一个建议是,在你把基础功能跑通之后,再去检查这些细节:统一异常处理是否处理了业务异常、查询历史是否做了分页、热门城市是否支持切换、天气数据为空时前端是否有提示。这些细节不需要写很多代码,但能让系统“从能跑变成能用”。比如我给查询历史加了删除功能,论文测试章节就多了一个完整的测试用例,答辩时也更容易回答“你的项目有没有考虑用户操作”。
如果你时间充裕,还可以在天气展示页加一个 24 小时温度趋势图,或者把城市搜索做成带联想的下拉框。这些功能都能在现有代码上扩展,不需要改后端核心逻辑。但要注意,扩展不要为了炫技而做,每一项功能背后最好都能对应论文里的一句话,这样才能形成“程序实现支撑论文描述、论文描述解释程序实现”的正循环。接下来就是把浏览器打开,从注册登录开始,一步一步把这条链路跑通,祝你也顺利搞定。