尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于SpringBoot+Vue的大学生智能消费记账系统设计与实现

基于SpringBoot+Vue的大学生智能消费记账系统设计与实现 1. 项目概述与整体设计思路1.1 为什么选择大学生智能消费记账系统作为毕业设计每年到毕设选题阶段最让同学们纠结的问题基本绕不开三件事题目能不能做完、技术栈有没有价值、论文有没有东西可写。大学生智能消费记账系统在这个维度上几乎是天然适配的选题因为它同时满足三个条件一是业务场景贴近真实生活需求不用凭空臆想随便采访几个身边同学就能整理出一版真实的功能清单二是技术链路完整从前端页面、后端接口、数据库设计到部署上线整条路径都能踩一遍三是数据天然适合做统计分析和可视化论文里从不缺素材和图表。至于技术栈SpringBoot Vue MySQL 的组合基本是当前前后端分离项目的主流标配。选择这个组合来做毕设既不会让人觉得技术栈太老又能在有限的时间内真正做完、做明白。相比传统SSHSpring Struts Hibernate架构SpringBoot通过起步依赖和自动配置大幅降低了配置成本相比用Python写单页应用SpringBoot Vue又更贴近真实企业的开发习惯。从想让毕设成为简历亮点的角度看这是一个非常稳妥的决策。1.2 系统整体架构整个系统采用前后端分离的架构。后端基于SpringBoot搭建RESTful API前端使用Vue框架配合UI组件库实现页面与交互数据库选用MySQL存储核心业务数据。前后端通过JSON格式的HTTP接口通信登录认证采用JWT方案——前端在登录成功后把token存在本地存储里后续请求在拦截器中统一携带token后端通过拦截器完成身份校验与放行。这个架构最大的好处是前后端可以并行推进。实际做毕设时我强烈建议先把接口文档定义清楚再分别开发这样可以省下大量联调时互相等待的时间。另外前后端分离在后期部署时也更灵活——前端构建产物交给Nginx托管后端打包成jar独立运行两者互不干扰定位问题时的边界非常清晰。整个请求链路可以简化理解成页面组件发起调用、请求经过axios封装、到达后端Controller、再经过Service处理业务、Mapper访问数据库、最终数据以JSON形式返回前端渲染。1.3 功能模块拆解围绕大学生消费这个场景系统核心功能可以拆成六个模块。用户模块负责注册、登录与个人基本信息维护这是所有业务的前提。记账模块是系统的心脏支持消费和收入两条流水线的增删改查字段包含金额、分类、备注、记账时间等。分类模块允许用户自定义支出和收入分类比如餐饮、交通、学习、娱乐、兼职收入等。统计模块按日、周、月、分类等多个维度汇总数据并为后续图表展示提供接口。预算模块让用户为每个月设置消费上限系统在接近或超过阈值时给出提醒。管理后台则面向管理员提供用户列表、系统分类管理等基础运营能力。这套功能拆解既保证了业务的完整度也为论文里的需求分析系统设计章节提供了扎实内容。2. 技术选型为什么是SpringBoot Vue MySQL2.1 后端选型理由SpringBoot作为后端框架最大优势是约定大于配置。它把过去SpringMVC项目中大量手动配置的DispatcherServlet、视图解析器、数据源等打包成自动配置开发者只需要依赖几个starter就能快速跑起来一个Web服务。做毕设时最典型的好处是可以用一个main方法启动内置的Tomcat不需要额外安装独立服务器也不用管理一堆XML配置文件。SpringBoot生态里最常用的两个数据库访问方案是Spring Data JPA和MyBatis-Plus。我个人更推荐MyBatis-Plus因为它在MyBatis的基础上提供了通用Mapper、分页插件、条件构造器这些非常实用的能力SQL还是自己控制方便针对复杂统计写出更精准的查询。同时在毕业设计论文中把SQL语句作为核心设计内容展示出来比黑盒式的ORM代码更好写、更好讲。2.2 前端技术栈与Vue的优势Vue在前端三大框架中以渐进式著称最大的特点就是上手平滑、文档友好、中文社区活跃。对于后端功底比前端强的同学来说Vue的学习曲线明显比React更平缓。用Vue做这个项目时最舒服的是它的响应式数据绑定——页面中要展示的金额、分类、统计数字只需要维护好data里的状态修改后视图自动更新完全不需要手动操作DOM。配合Vue的生态还需要选定几件配套工具Vue Router负责路由管理适合做登录页、首页、记账页、统计页等页面跳转Pinia或Vuex负责状态管理用来存用户信息、token等全局数据axios负责HTTP通信统一封装请求拦截器和响应拦截器UI方面可以用Element Plus做后台风格界面也可以用Vant做移动端风格标题描述的是平台我建议直接用Element Plus表格、表单、弹窗这些组件开箱即用开发速度快很多。2.3 为什么不做微服务、不用分布式有些同学会问既然是想让毕设加分那能不能把系统做成微服务架构引入Redis、RabbitMQ、Nacos这些中间件我的建议是除非你本身对这些技术有半年以上的实战经验并且能在答辩时讲清楚每个组件的必要性和设计原理否则千万不要为了炫技而引入复杂度。微服务要解决的问题是团队协作、独立部署、高并发扩容而一个课程设计级别的记账系统单机部署完全够用。强行引入分布式组件只会让部署环境变得异常繁琐答辩时也容易陷入为了用而用的质疑。与其堆砌技术不如把技术细节做扎实JWT的Token校验逻辑、MyBatis-Plus的分页查询、MySQL索引怎么设计、ECharts统计图怎么和接口数据对接这些才是答辩时真正能体现专业水平的地方。3. 数据库设计表结构、字段与索引3.1 核心数据表梳理数据库设计是整个系统的地基。表设计得好不好直接决定后续业务逻辑能否顺畅实现。这个系统的核心表我建议划成五张用户表、收支记录表、分类表、预算表、以及一个支持智能提醒的消费预警配置表。用户表存储账号信息字段包括主键id、username登录账号、password加密后的密码、nickname昵称、avatar头像地址、role角色区分普通用户和管理员、create_time注册时间。密码字段不能存明文要存BCrypt加密后的结果这是毕设答辩时一定会被问到的安全点。收支记录表是业务核心字段包括id、user_id、category_id、type收支类型0支出、1收入、amount金额用decimal类型、note备注、trans_time实际消费时间、create_time创建时间。这里有个容易踩坑的地方金额千万不要用double否则经过浮点运算后会出现0.5700000000000001这种误差论文里用decimal(10,2)是最稳妥的。分类表设计需要区分系统预置分类和用户自定义分类。做法是在分类表中加一个is_system字段0表示用户自己建的1表示系统预置的。预置分类包括餐饮、交通、购物、学习、娱乐、居住、医疗、其他收入预置分类包括生活费、兼职、奖学金、理财收益等。当用户没有自定义分类时后端接口自动返回系统分类保证前端展示不会空。预算表比较简单字段是id、user_id、month月份格式202506、budget_amount当月总预算、alerts_enabled是否开启超支提醒。按月作为维度天然适合按月的统计查询。3.2 索引设计与查询优化索引是数据库设计中容易被忽略但非常重要的细节。对这个系统来说最频繁的查询场景是按用户查某段时间的消费记录和按月份分组统计消费金额所以索引设计围绕这两个高频查询来。我建议给收支记录表建立联合索引(user_id, trans_time)这样当用户筛选6月份的支出时MySQL可以快速定位到该用户、时间范围内的记录避免全表扫描。统计类SQL再配合type字段进行分组过滤响应时间基本都在毫秒级别。理论上type字段也可以加入联合索引变成(user_id, type, trans_time)但考虑到type只有0和1两种取值区分度不高加进索引收益有限所以我最后采用的是(user_id, trans_time)在复杂度和性能之间取了一个平衡。分页查询也是需要提前规划的。MySQL的分页查询在数据量小的时候没问题但当记录数超过几万条后用LIMIT offset, size深分页会出现明显变慢。毕设阶段虽然数据量不会多大但作为系统设计的一部分我建议在论文里写明针对大数据量的处理思路使用游标或基于索引的分页方式或者用时间范围约束来减少偏移量。这个细节写在论文里答辩时会让老师觉得你有数据库性能意识。3.3 数据库初始化脚本如果源码包里附带完整的SQL初始化脚本那么其他人拿到项目后只需要执行一条命令就能建库建表体验会非常顺畅。我在做的时候是把SQL脚本放在项目根目录的sql文件夹下包含建库语句、建表语句、系统预置分类数据的INSERT语句以及一个测试用的管理员账号。数据库编码统一使用utf8mb4这个字符集能完整支持中文以及一些emoji字符避免用户在备注里输入特殊表情时出现乱码报错。4. 后端核心功能实现4.1 登录认证与JWT实现登录模块听起来简单但它是整个系统的安全门槛做得好不好直接影响后续全部接口的开发方式。我采用的方案是登录成功后后端生成一个JWT字符串返回给前端前端存起来之后每次请求都在HTTP请求头里携带Authorization: Bearer 。JWT的生成过程需要依赖jjwt库。生成时把用户id和用户名放进claims设置过期时间为24小时并使用一个自定义的salt字符串签名。后端需要写一个拦截器对所有除登录、注册以外的接口进行token校验。拦截器里先解析请求头如果没有token直接返回401如果有token但过期或签名不对则返回登录状态已过期的统一错误提示。为了避免在Controller里到处重复判断用户身份我在拦截器解析成功后把用户ID放进了请求上下文的Attribute中后续Service层通过工具类从上下文直接取出当前用户ID大大简化了业务代码。4.2 记账业务新增、修改、删除记账模块的业务逻辑重点是数据校验和用户数据隔离。这里说的数据隔离就是用户只能操作自己的记录绝对不允许出现A用户查到B用户账单的情况。后端Controller在接收请求后从JWT上下文拿到当前用户ID所有SQL查询条件里都强制带上user_id分页查询、统计查询同理。新增记账接口的核心代码思路大致是Override public void addTransaction(TransactionDTO dto) { Transaction entity new Transaction(); entity.setUserId(LoginUtil.getCurrentUserId()); entity.setCategoryId(dto.getCategoryId()); entity.setType(dto.getType()); entity.setAmount(dto.getAmount()); entity.setNote(dto.getNote()); entity.setTransTime(dto.getTransTime()); transactionMapper.insert(entity); }事务插入前要注意金额校验金额必须大于0且最多保留两位小数type只能是0或1category_id必须是当前用户可见的分类ID。这些校验看似琐碎但能拦截掉大量前端校验绕过的情况。修改和删除操作类似第一步先按id和user_id查询记录是否存在如果不存在则抛出记录不存在或无权操作的业务异常通过全局异常处理器统一返回给前端。4.3 统计报表接口设计记账系统的智能体现很大程度上依赖统计接口。我设计了三个核心统计接口第一个是分类统计接口接收起止时间参数返回当前用户在每个支出分类下的总金额和占比前端用饼图展示第二个是月度趋势接口返回过去12个月每个月的支出总额前端用折线图展示第三个是预算比较接口返回当前月已消费金额、总预算金额、剩余金额、超支比例。这三个接口对应三条SQL查询最复杂的分类统计SQL可以这样写SELECT c.name AS category_name, IFNULL(SUM(t.amount), 0) AS total_amount FROM category c LEFT JOIN transaction t ON t.category_id c.id AND t.user_id #{userId} AND t.type 0 AND t.trans_time BETWEEN #{startDate} AND #{endDate} WHERE c.type 0 AND (c.is_system 1 OR c.user_id #{userId}) GROUP BY c.id ORDER BY total_amount DESC这里有个细节值得注意用LEFT JOIN而不是INNER JOIN是因为预算比较时即使某个分类当月没有消费记录也要返回分类名和金额0而不是直接不显示。这个SQL用到了IFNULL来处理NULL值最后按金额降序排列前端饼图展示时默认把占比最大的分类放在最前面视觉上更直观。4.4 智能预警与预算模块预算模块和智能预警是一对搭档。每个月1号后端定时任务会检测上个月的消费情况如果超支率达到100%以上就生成一条提醒消息推送给用户。这个推送不需要引入消息队列我的做法是在用户登录进入首页时调用一个预算状态查询接口后端实时计算当月消费总额和预算金额如果消费比例超过80%接口返回一个预警标记前端在首页顶部显示一条醒目的横幅提醒用户本月预算已使用完或者本月预算即将用完当前已使用XX%。这样做的好处是智能二字有了落地的功能点而且技术上不难实现——就是一个简单的聚合查询加上一个阈值判断。答辩时老师问系统哪里体现了智能你至少有实际的数据和分析逻辑可以介绍而不是空泛地讲概念。5. 前端界面与交互逻辑5.1 项目初始化与路由设计前端使用Vite脚手架创建项目相比WebpackVite在开发模式下启动速度快得多对毕设这种时间紧张的项目来说能省下不少等待时间。初始化命令很简单npm create vitelatest frontend -- --template vue创建完成后安装项目依赖包括vue-router、pinia、axios、element-plus、echarts这几个核心库npm install vue-router4 pinia axios element-plus echarts路由设计上我采用的是嵌套路由加导航守卫的结构。未登录用户访问任意页面守卫判断本地有没有token如果没有就重定向到登录页已登录用户访问登录页时如果token存在则直接跳转到首页。路由表中除了基础的登录页业务页面统一放在主布局Layout下面包括首页Dashboard、记账页、账单列表页、统计页、预算页和个人中心页。通过嵌套路由的方式页面顶部和侧边栏只渲染一次切换子路由时只更新内容区域体验更接近真实管理后台。5.2 记账与新账本的核心组件记账页是整个前端最关键的交互页面。最简方案是使用一个表单弹窗里面包含类型切换支出/收入、金额输入框、分类选择器、备注输入框、日期选择器。这里有个体验上的小细节默认金额输入框要聚焦并且弹出的键盘是数字键盘分类选择器使用图标加文字的方式展示比纯下拉框更直观。我的组件结构大致如下template el-dialog v-modelvisible title记一笔 width480px el-form :modelform :rulesrules refformRef el-form-item proptype el-radio-group v-modelform.type el-radio-button :value0支出/el-radio-button el-radio-button :value1收入/el-radio-button /el-radio-group /el-form-item el-form-item propamount el-input v-modelform.amount placeholder金额 typenumber / /el-form-item el-form-item propcategoryId el-select v-modelform.categoryId placeholder选择分类 el-option v-foritem in categoryList :keyitem.id :labelitem.name :valueitem.id / /el-select /el-form-item el-form-item proptransTime el-date-picker v-modelform.transTime typedatetime / /el-form-item el-form-item propnote el-input v-modelform.note placeholder备注 / /el-form-item /el-form /el-dialog /template组件内部保存的categoryList从接口获取后端根据type返回对应的支出或收入分类列表。当用户切换type时分类列表要同步刷新这个联动通过监听form.type变化来实现。5.3 统计可视化用ECharts展示数据统计页是前端最有亮点的部分。ECharts对Vue3的支持方式很直接只需要在组件中引入echarts然后在mounted生命周期里初始化图表实例把后端返回的数据转换成图表需要的option即可。我在统计页放了三个主要图表。第一个是饼图展示当月消费结构数据来自分类统计接口每个扇形对应一个分类图例显示分类名和金额第二个是折线图展示近6个月的消费趋势横轴是月份纵轴是金额第三个是横向柱状图展示各分类同比上月的消费增减情况可以直观看出哪些分类支出上升了。图表组件的核心代码逻辑其实非常简单import * as echarts from echarts const chartRef ref(null) let chartInstance null onMounted(() { chartInstance echarts.init(chartRef.value) fetchStats() }) const fetchStats async () { const { data } await getCategoryStats() const option { tooltip: { trigger: item }, legend: { orient: vertical, left: left }, series: [{ name: 消费结构, type: pie, radius: 60%, data: data.map(item ({ name: item.categoryName, value: item.totalAmount })) }] } chartInstance.setOption(option) }有一个特别容易踩的坑组件销毁后ECharts实例需要手动调用dispose方法释放否则在Vue3中多次切换路由后浏览器会报Get disposed的错误。我是在onBeforeUnmount生命周期中增加一行onBeforeUnmount(() { if (chartInstance) { chartInstance.dispose() chartInstance null } })这个细节在开发时折腾了我半个多小时后来才发现是实例没有释放导致的。写在这里帮大家避个坑。6. 部署与运行从本地到上线6.1 本地环境搭建项目要能跑起来第一步是环境准备。需要安装JDK 1.8或以上版本、Maven 3.6、Node.js 16、MySQL 5.7或8.0。这里我特别想提醒的是MySQL版本选择如果你是本地开发测试建议直接用MySQL 8.0因为8.0在性能、字符集和窗口函数支持上都更好但要注意SpringBoot连接MySQL 8.0时需要在JDBC连接串中增加时区参数否则会报时间相关的错误。完整的环境准备清单大致是这样JDK推荐使用JDK 1.8兼容性好网上教程最多大部分高校实验室环境也是这个版本Maven配置好阿里云镜像下载依赖速度快得多MySQL执行项目下的sql/init.sql脚本完成建库建表Node.js与npm版本不要太老Vite3以上要求Node 16以上6.2 前后端打包后端打包非常简单在项目根目录执行mvn clean package -DskipTeststarget目录下会生成一个可执行的jar包启动命令是java -jar xxx.jar。如果默认端口8080被占用可以在启动命令中添加--server.port8081参数或者直接在application.yml中修改配置。前端打包需要先配置好后端接口地址。开发环境下前端通常通过Vite的proxy代理把/api前缀的请求转发到后端的8080端口这样避免了浏览器跨域问题。生产环境下可以直接在前端代码里把baseURL配置为后端实际部署的域名或IP。打包命令npm run build构建完成后dist目录下就是静态文件包含index.html入口、js和css资源文件。把这些文件部署到Nginx的html目录再配置反向代理把接口请求转发到后端jar项目整个系统就上线了。6.3 部署方案对比我试过两种部署方案各有优劣。第一种是直接在云服务器上安装JDK、MySQL和Nginx前后端都部署在同一台机器上优点是成本最低适合学生预算第二种是使用Docker容器化部署分别构建MySQL、后端jar、前端Nginx三个容器再用docker-compose编排优点是环境一致性好、迁移方便但需要额外学习Docker基础知识。对于毕设演示场景我更推荐第一种因为现场答辩时的网络和服务器环境不一定支持你临时拉取Docker镜像。而且如果答辩老师问起项目如何部署上线用最直接的方式回答反而更踏实装JDK、装MySQL、启动jar、配置Nginx每一步都清清楚楚。把第一种方案跑通之后再在论文里写一小节基于Docker的扩展部署方案作为系统可移植性的补充说明内容上就非常完整了。7. 论文撰写与答辩要点7.1 论文结构安排毕设论文和普通技术博客不同它有固定的逻辑框架。我的论文结构是这样安排的第一章绪论讲研究背景和意义、国内外研究现状、论文主要工作第二章相关技术介绍讲SpringBoot、Vue、MySQL、JWT、ECharts这些技术第三章系统分析与设计包含可行性分析、需求分析、功能模块设计、数据库设计第四章系统实现按功能模块展示页面截图和核心代码第五章系统测试包含功能测试和性能测试最后是总结与展望。很多同学写论文时最容易犯的毛病是技术介绍写太多系统设计写太少。其实答辩老师最看重的是第三章和第四章也就是你怎么分析需求、怎么设计数据库、怎么实现核心功能。技术介绍部分只需要把选型的理由讲清楚即可不需要把SpringBoot的官方文档复制一遍。7.2 答辩常见问题与应对思路答辩现场老师的提问基本集中在几个方向为什么选这个课题、系统有哪些功能、数据库怎么设计的、核心功能怎么实现的、项目有哪些创新点。针对这本书里的系统最可能被追问的包括预算预警是怎么实现的这个要回答出统计接口SQL和阈值判断逻辑。JWT和传统Session有什么区别如果只答出不需要在服务端存储Session还不够最好还能说出JWT的过期时间设置和签名机制。系统安全上做了哪些措施可以从密码加密存储、登录拦截器、SQL预编译防注入、前端请求拦截器统一携带token这几个角度回答。如果老师问用户量变大了怎么办可以从数据库读写分离、Redis缓存热点数据、前后端横向扩展这几个方向简单提及但记住不要过度引申点到为止即可。7.3 部署文档的写作技巧部署文档虽然是附加材料但作用非常大。答辩或提交项目时导师第一件事就是打开部署文档看能不能照着跑起来。我见过太多项目因为缺少环境配置细节而让评审老师无法运行最终影响评分。部署文档我建议包含五个部分环境要求JDK、MySQL、Node版本、初始化数据库步骤附SQL执行截图、后端启动步骤修改数据库连接配置、执行mvn命令、验证启动日志、前端启动步骤安装依赖、配置代理、启动开发服务器、常见问题端口冲突、时区报错、跨域问题、依赖下载慢。每个步骤都要写得傻瓜式哪怕是一个完全不懂这个项目的同学只要按步骤操作也能在30分钟内把系统跑起来。8. 常见问题与踩坑记录8.1 典型问题速查表项目开发过程中踩过的坑我整理成了一张速查表方便大家按图索骥。问题现象根本原因解决办法前端请求接口报跨域错误前后端端口不同开发环境配置Vite proxy生产环境配置Nginx反向代理MySQL连接报The server time zone valueMySQL 8.0时区问题JDBC连接串添加serverTimezoneAsia/Shanghai使用double存金额出现精度丢失浮点运算误差金额字段改用decimalJava中使用BigDecimal登录后刷新页面就退出token仅存在内存中将token存入localStorage初始化时从localStorage恢复ECharts报Get disposed组件销毁后实例未释放在onBeforeUnmount中调用dispose()SpringBoot启动失败端口被占用8080端口被其他进程占用修改端口或使用lsof/任务管理器找出占用进程npm install速度极慢依赖源在国外配置使用国内镜像源打包后前端白屏静态资源路径不对设置Vite的base为./或使用绝对路径部署8.2 我的几点实操心得最后分享几个实际操作中得到的体会。第一接口返回格式一定要统一。我设计了一个通用的JSON返回类包含code、message、data三个字段成功时code为200业务异常时code为500未登录时code为401。前端axios拦截器统一判断code这样在写前端逻辑时不需要在每个接口里单独做错误处理代码和可维护性都好很多。第二SQL语句务必在本地数据库客户端里先验证一遍再写进代码。直接用MyBatis-Plus的LambdaQueryWrapper确实方便但复杂统计SQL还是要手写手写SQL后先在MySQL Workbench或命令行里跑一遍确认结果正确再贴到Mapper注解里能省下大量调试时间。第三整个项目的时间规划上建议遵循先跑通、再优化的原则。花一周时间把登录加记账加基础列表查询做完这个阶段是验证技术栈是否可以跑通先求能用之后再迭代加入统计图表、预算预警、管理后台这些功能逐步完善体验。如果反过来一上来就憋大招想做完美产品很可能到了中期检查时连一个完整的用户注册登录流程都没做完整体进度就容易失控。这个项目做下来我最大的感受是毕业设计的目的不是做一个商用的完美产品而是通过一个完整的项目把大学所学知识串联起来。从需求分析、数据库设计、后端接口开发、前端页面实现到部署上线每一个环节都在倒逼你把之前零零散散学的知识真正用起来。把这个过程做好答辩就不是问题更重要的是你对一个Web应用是如何从零到一跑起来的会形成一个完整而清晰的认知这才是毕设带给你的长期价值。
返回列表