简介面向大学生消费场景的智能记账系统毕业设计项目基于Java语言与SpringBoot框架构建后端服务前端使用Vue渐进式框架数据库采用MySQL实现了前后端分离的完整架构。压缩包内包含项目源码、数据库脚本、论文文档以及开发工具配置经过严格调试下载后可直接运行非常适合作为毕业设计、课程设计或期末大作业参考。整个资源包共374个文件大小约15.51MB文件类型包括Java源码、Vue组件、SVG图标、PNG图片、SQL数据库脚本、bat启动脚本等目录组织清晰便于读者快速定位代码模块并开展二次开发。系统功能覆盖消费记账、账户管理、统计分析等核心模块界面设计简洁美观操作流程顺畅能够以图表直观展示个人消费习惯与财务状况有助于大学生培养理性消费意识。目前已有72人学习下载作为高分毕业设计项目无论是用于完整项目学习还是作为实际财务工具都具有良好的参考与应用价值。1. 大学生智能消费记账从记了白记到越记越省的高分毕设工程大学生记账这事十个人里有八个坚持不过两周原因倒不是懒而是市面上的记账 App 对校园场景的适配太弱食堂刷卡、外卖红包、宿舍水电分摊这类高频低额消费记录成本高月底复盘又看不出钱到底花在哪。这套基于 SpringBootVueMySQL 的智能消费记账系统把「AI 消费分类 预算超支预警 月度趋势分析」做成了完整的闭环后端是标准的三层架构前端是 Vue 组件化页面数据库落六张核心表答辩演示时能直接拿出「分类占比图」「预算消耗进度条」「月度对比折线图」三样硬货。适合两类人一类是把这套源码当毕业设计基座、想在两周内凑齐完整系统的同学另一类是已经写过单体 CRM 或商城、想看看前后端分离项目怎么把业务模块串起来的从业者。下面按实际拆解顺序把技术选型、关键代码、踩坑点和验收路径逐层讲透。2. 整体架构与数据设计六张表怎么扛住核心业务闭环2.1 前后端分离架构里的职责边界打开工程目录第一眼会发现它没有把 Controller 和页面堆在一起而是严格分成了 backend 和 frontend 两个独立模块。这是我推荐所有毕设项目都遵循的边界SpringBoot 只负责暴露 RESTful 接口Vue 只负责渲染和交互两者通过 JSON 通信。这套系统的接口前缀统一走/api前端在vue.config.js里配了 devServer 代理开发环境下把/api转发到localhost:8080后端不关心页面长什么样前端也不关心 SQL 怎么执行。这么做的好处有两个一是答辩时能讲清楚「低耦合」这个概念而不是含糊地说用了 SpringBoot 和 Vue二是后期你想换掉前端框架或者改成小程序端后端一套接口可以原样复用。实际项目中某跨平台系统就是把这套 Vue 页面替换成了 uni-app 实现接口只改了个跨域配置业务逻辑一行没动。2.2 六张核心表的字段设计与关系梳理数据库脚本在sql/目录下初始化脚本里一共有六张表大部分表之间靠逻辑关联而非物理外键。很多同学拿到手第一件事就是删掉外键约束怕报错这个做法不算错但你要知道为什么这么设计——核心业务表的写入频率高物理外键在插入时会多一次约束校验对性能不友好所以这系统只在事务层面保证一致性。用户表存账号密码和基础信息密码用了 MD5 加盐存储表里那个salt字段很多同学会忽略答辩时主动提一句密码不是明文存储是加分项。账单表是绝对的核心字段包含bill_type1 支出 2 收入、category_id、amount、bill_time、remark其中amount用 DECIMAL(10,2) 而不用 FLOAT避免浮点精度问题。分类表里内置二十种预设分类数据在初始化脚本里已经灌好前端加载时会按icon字段渲染对应的图标。预算表是预警功能的数据源按月存储budget_amount和used_amount两个字段配合计算进度。提醒表存储预算超支后的通知记录共享表则用来处理 AA 制场景下的多人消费分摊。如果你想把项目做得更有亮点我建议在现有基础上加一张tag表做消费标签比如奶茶打车学习资料这属于低成本高回报的优化——不用动现有表结构加张关联表即可。3. 后端核心模块落地三大功能从请求到 SQL 的完整链路3.1 登录鉴权的 Session 与拦截器实现登录模块是整个系统的入口技术选型走的是传统 Session 拦截器方案没有引入 JWT 或 Spring Security。对毕设级别项目来说这个选择很务实——代码量少答辩时能把逻辑讲透出问题也好排查。核心拦截器如下Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(user); if (user null) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或登录已过期\}); return false; } UserContext.setCurrentUser(user); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.clear(); } }这段代码里有两个容易被忽略的点。第一是UserContext我用 ThreadLocal 存当前登录用户这样在 Service 层任何地方都能直接取到 userId不用把用户对象一层层往下传。第二是拦截器里设了 401 状态码而不是 200这样前端 axios 响应拦截器能统一跳转到登录页而不是每个接口都去判断返回码。参数说明如果改造成 JWT核心变化是把HttpSession session request.getSession()换成从 Header 里取 token其他业务代码基本不用动这个点可以在答辩时作为项目可扩展性的论述素材。3.2 账单单条新增与批量导入的双通道设计账单管理模块同时支持单条录入和批量导入单条录入走常规的 Controller-Service-Mapper 链路批量导入则接收前端上传的 Excel 文件用 EasyExcel 解析。PostMapping(/bill/import) public Result importBills(RequestParam(file) MultipartFile file) { Long userId UserContext.getCurrentUser().getId(); try { ListBillImportDTO dtoList EasyExcel.read(file.getInputStream()) .head(BillImportDTO.class) .sheet() .doReadSync(); if (dtoList.size() 500) { return Result.error(单次导入不能超过500条); } int successCount 0; ListString failReasons new ArrayList(); for (BillImportDTO dto : dtoList) { try { Bill bill new Bill(); bill.setUserId(userId); bill.setAmount(dto.getAmount()); bill.setCategoryId(categoryService.findByUserAndName(userId, dto.getCategory())); bill.setBillTime(DateUtils.parseDateTime(dto.getTime())); bill.setBillType(dto.getType()); bill.setRemark(dto.getRemark()); billService.save(bill); successCount; } catch (Exception e) { failReasons.add(第 (dtoList.indexOf(dto) 1) 行导入失败 e.getMessage()); } } return Result.success(导入完成成功 successCount 条失败 failReasons.size() 条, failReasons); } catch (Exception e) { log.error(Excel解析失败, e); return Result.error(Excel格式错误请使用系统提供的模板); } }这段代码为什么用doReadSync()而不用监听器方式因为同步读完后直接返回统计信息交互上对用户更友好——能看到成功失败明细而不是异步抛个导入中请稍后然后没下文。setCategoryId这行的逻辑值得注意用户 Excel 里填的是分类名称系统需要按 userId 和名称映射到 ID找不到分类时findByUserAndName会抛异常被内层 catch 捕获后计入失败列表保证一整批文件不会因为一行脏数据全部回滚。3.3 预算预警定时任务与超支扣减的临时一致性方案预算模块的实现有两条线查询时实时计算和定时任务扫描生成提醒。实时计算走的是聚合 SQL预算详情页需要展示「本月预算、已用金额、剩余比例」三个数字这不能逐条把账单 load 进内存再累加而是用一条分组查询。SELECT category_id, SUM(amount) AS used_amount FROM bill WHERE user_id #{userId} AND bill_type 1 AND bill_time BETWEEN #{startTime} AND #{endTime} GROUP BY category_id重点说一下定时任务那条线Scheduled(cron 0 0 2 * * ?)每天凌晨两点跑一次扫描所有预算表的记录找出used_amount / budget_amount 0.9的项生成一条提醒记录。这里有个常见的理解误区定时任务读的是预算表里的used_amount字段而不是实时去账单表聚合所以每晚两点前这个字段可能不是最新的。维护方式是在账单保存后同步updateBudgetUsedAmount()如果改造成更严谨的方案可以加事务消息或事件驱动但毕设场景下「保存时更新 凌晨对账」已经够稳。提醒模块还做了「连续超卖保护」——同一天内同一分类已生成超支提醒时不再重复生成避免用户每次登录都看到五条相同的提醒。这个去重逻辑用了一条带date字段的唯一索引简单但有效。4. 前端工程化落地Vue 组件树、状态管理与 ECharts 图表联动4.1 从路由守卫到页面骨架的初始化链路前端技术栈是 Vue 2 Element UI EChartsmain.js里注册了全局组件和 axios 实例路由配置里做了一层全局前置守卫router.beforeEach((to, from, next) { const token sessionStorage.getItem(token); if (to.path ! /login !token) { next(/login); } else { next(); } });这一段看起来简单但定位 401 响应需要两处配合路由守卫挡「没登录就访问」的场景axios 响应拦截器挡「登录过期后继续操作」的场景。很多同学只在路由层做了拦截结果用户登录状态失效后在页面上点击任何按钮都没反应还以为是系统的 bug。页面骨架采用经典的「左侧菜单 右侧内容区」布局菜单项根据路由表自动生成activeMenu从this.$route.path动态取刷新页面不会菜单高亮错乱。顶栏右侧有用户头像下拉菜单退出登录时调/logout接口并sessionStorage.clear()。4.2 账单页面的条件查询与分页联动账单列表页是业务逻辑最密集的页面搜索条件有「时间范围、分类、金额区间、类型」每个条件变化都要重新拉取数据。这里用 watch 监听条件对象变化后重置页码为 1 再触发查询避免用户在第三页筛选时跳到空白页。data() { return { queryParams: { pageNum: 1, pageSize: 10, categoryId: null, type: null, startDate: null, endDate: null, minAmount: null, maxAmount: null }, tableData: [], loading: false }; }, watch: { queryParams: { handler() { this.queryParams.pageNum 1; this.fetchList(); }, deep: true } }deep: true这一步很关键对象内部字段变化不会触发浅层监听。分页组件用 Element UI 的el-paginationcurrent-change事件触发查询如果你发现切换页码时查询条件丢失检查有没有把queryParams传到后端——这里的写法是每个查询条件都作为独立参数拼接到查询字符串后端用RequestParam(required false)接收。4.3 ECharts 三张图的响应式适配技巧「分类占比」「月度趋势」「预算进度」这三张图是答辩演示的颜值担当全部在Statistics.vue里。ECharts 的图表容器必须要有明确高度styleheight: 360px写在div上初始化时机放在mounted里但数据是异步加载的所以要先初始化图表再setOption数据回来后再setOption一次。窗口自适应这块有个容易踩的坑直接监听window.resize后调用chart.resize()如果页面上多个图表会卡顿。实操上我用了一个轻量方案——在resize回调里加debounce300ms同时用document.querySelectorAll(.chart-container)定位所有图表实例统一调整。另外页面隐藏再显示时tab 切换会导致图表宽度计算成 0所以用nextTick重新调用chart.resize()配合keep-alive保留图表状态。5. 常见问题排查从跨域到金额精度的五个真实踩坑记录5.1 CORS 跨域配置导致登录后接口全部 401现象前端能打开登录页但登录成功后所有业务接口都报「跨域错误」浏览器控制台显示CORS policy红字。原因前端开发服务器在 8081 端口后端接口在 8080浏览器拦截了跨域请求。很多同学以为在vue.config.js配了代理就完事但代理只在开发环境生效你直接输入http://localhost:8080/api访问还是会被拦。解决后端加一个 WebMvcConfigurer 配置类允许指定来源和携带凭证。注意allowCredentials(true)时allowedOriginPatterns不能用*必须写具体域名或使用allowedOriginPatterns(*)来适配。5.2 前端金额显示 0.30000000000000004现象账单列表里金额明明是 9.9 元显示成 9.899999999999999。原因MySQL 字段类型用了 FLOATJava 实体类用了 Float/Double二进制浮点运算产生了精度误差。解决数据库amount字段改成 DECIMAL(10,2)Java 用BigDecimal前端展示直接拿后端返回的数字字符串不要再做乘除运算。所有涉及金额聚合统计的地方都走 SQL 的SUM(amount)不要在 Java 里逐个累加。5.3 时区问题账单时间总是差八小时现象前端录入晚上八点的账单查询列表时变成凌晨零点。原因MySQL 连接串没加serverTimezoneAsia/ShanghaiSpringBoot 默认按 UTC 时区解析时间字段。解决JDBC URL 追加?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8同时实体类里时间字段统一用java.util.Date前端传参时用timestamp字符串new Date().getTime()避免字符串格式解析出歧义。5.4 自定义分类删除后旧账单显示空白现象删除一个分类历史账单的分类名称显示为空。原因账单表只存了category_id没冗余存分类名删除分类后 JOIN 查不到数据。解决删除分类时做逻辑删除给category表加is_delete字段查询账单时 LEFT JOIN 分类表并过滤is_delete 0如果执意物理删除就把分类名冗余到账单表的category_name字段查询后直接展示不用 JOIN。5.5 打包后前端访问/api404现象本地开发一切正常npm run build后部署到 Nginx页面能打开但所有接口报 404。原因Nginx 没有做/api反向代理到后端服务开发环境的 proxy 配置不会跟着打包产物走。解决Nginx location 配置加一段proxy_pass http://127.0.0.1:8080;注意斜杠路径匹配规则。另外打包后用nginx -c /usr/local/nginx/conf/nginx.conf验证配置语法改完执行nginx -s reload。这套流程我在多个项目中都用同样的套路基本不会再遇到打包后接口不通的问题。6. 验收自测清单与两个低成本功能扩展从拿到源码到确认系统可运行我建议新设备上按顺序做一遍完整验证数据库导入初始化脚本 → 启动后端 → 启动前端 → 注册一个测试账号 → 手动录入十条不同分类的支出数据 → 检查分类占比图和月度趋势图是否多出对应点位 → 把月预算调低到 500 元 → 继续录满到超过 500 → 观察首页是否弹出超支提醒。这个过程如果顺利说明环境配置、数据表和核心业务链路都没有被改坏。验收之后如果想加亮点优先做「月度对比视图」在统计页加一个同比选项SQL 用DATE_FORMAT(bill_time, %Y-%m)做维度分组前后端各加一个小模块即可比新做一个模块省时间但展示效果好。共享账单功能如果原始模块不够完善可以做简化版一张共享表记录账单 ID 和用户 ID 的映射查询时用IN子句拼 userId 列表也够应付演示。这套系统对我最大的启发是毕设项目不需要追求技术栈多新把 SpringBoot 的拦截器、MyBatis-Plus 的 CRUD、Vue 的生命周期这几样吃透再配合合理的表结构就能搭出一套逻辑自洽的完整系统。从那以后我每次评审毕设项目都强制走一遍上面的验收清单——登录、增删改查、统计图表、预算预警各摸一遍有问题的模块在当下暴露总比答辩现场翻车好。这套源码和配套论文我一直留在本地工程库里希望帮到正在做类似选题的你。本文还有配套的精品资源点击获取