ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

家庭理财记账系统:Spring Boot 3 + Vue 3 数据统计与可视化

家庭理财记账系统:Spring Boot 3 + Vue 3 数据统计与可视化 最近又在帮人看毕设选题了其中出现频率特别高的一个组合是Spring Boot 3 Vue 3 做一个家庭理财记账系统。说实话这个题目第一眼看上去很常规无非就是记账、分类、统计、图表、登录注册这几件事。真正动手做的时候你会发现这个项目麻烦的地方根本不在增删改查而是在数据统计口径、可视化图表的字段对齐以及前后端联调时那些说不清道不明的小问题。这篇文章我会按一个真实课设/毕设项目推进的顺序来写从需求判断、技术选型、表结构设计、接口设计、ECharts 可视化一直写到联调阶段的常见坑和排查路径。整个过程里我会把“为什么要这样设计”和“实际落地时通常会遇到什么”放在比“怎么敲代码”更重要的位置。很多同学做完这个项目数据库表建了接口也写了图表也出来了但被老师一问“这个统计口径是什么”“这个月结余怎么算的”就答不上来——这说明项目只是跑起来了并没有真正理解。1. 先搞清楚这套系统真正要解决的痛点是什么家庭理财记账系统这个题目表面上是“管钱”本质上是“管数据的沉淀和再消费”。如果只做一个记账本每天记一笔收入和支出那这个系统一点竞争力都没有和 Excel 表格没有任何区别。它真正有价值的部分在于把一笔一笔的流水数据转换成家庭成员能看懂的财务状况结论。1.1 记账只是入口统计和可视化才是价值你可以先问自己一个问题用户为什么要记账不只是“记录一下”而是想知道钱花到哪里去了、每个月能不能存下钱、哪一类开支超出了预算。这就决定了系统的核心模块不是“新增一笔账单”这个表单页面而是统计分析。所以在设计阶段就要把系统分成三层数据层负责把流水、分类、用户、预算这些基础数据存下来。服务层负责算清楚总收入、总支出、结余、分类占比、月度趋势这些统计口径。展示层负责用 ECharts 把统计结果变成饼图、折线图、柱状图。很多同学一上来就怼着页面写结果项目做完发现统计接口返回的数据 ECharts 根本没法直接用又要回头改接口。这是最常见的返工原因。1.2 个人记账、家庭记账和财务记账的差异这个系统叫“家庭理财记账系统”和“个人记账”的区别在于家庭记账至少要考虑多成员、多账户、共享分类。也就是说数据模型不能只为一个用户服务。通常会这样设计用户表家庭成员账号。账本表一个家庭可以有多个账本比如“日常开销”“旅行专款”。流水表每笔账单归属某个账本、某个分类、某个记账人。分类表收入和支出分类例如餐饮、交通、工资、理财收益。如果嫌表太多可以把账本表去掉一个家庭一个账本但至少要保留用户和账单之间的关联关系。否则“家庭”这两个字体现不出来。注意如果你的项目定位是单人记账就不要强行加多成员逻辑先把单人流程做完整比半吊子的多成员方案好讲得多。毕设答辩时一个能完整演示的单账本流程强过一个只能新增不能统计的多账本框架。2. Spring Boot 3 Vue 3 这套组合为什么适合做完整项目技术选型是开题报告里一定会写的部分。很多同学选 Spring Boot 3 Vue 3理由是“新”。但你要能讲清楚它新在哪里以及它带来的代价是什么。2.1 Spring Boot 3 的核心变化不只是版本号Spring Boot 3 是一个比较大的版本跳跃原因是它基于 Spring Framework 6并且把 Java 基线升到了 Java 17。这意味着本地 JDK 必须是 17 或以上否则项目都启动不了。很多老版本的第三方依赖需要升级尤其是 mybatis-plus、druid 这类常用库要选支持 Spring Boot 3 的版本。如果之前习惯用 Java 8 写代码现在可以开始用record、var、switch表达式这些新语法。对课设来说Spring Boot 3 最大的好处是项目结构清晰controller 负责接收请求service 负责业务逻辑mapper 负责数据库访问配置集中在application.yml里。这种分层很符合答辩时“讲架构”的需求。一个典型的最小依赖集合大概是dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId /dependency具体版本号我这里不写死因为你的本地环境和选用的模块可能与我不一样。落地前先确认一件关键的事所有依赖是否都支持 Spring Boot 3。最容易出问题的就是数据库连接池和分页插件。2.2 Vue 3 Vite ECharts前端技术栈的关键权衡Vue 3 带来了 Composition API、script setup语法和更灵活的响应式系统。对做课设的同学来说script setup最大的体验提升是代码量明显变少逻辑也更集中。至于要不要上 TypeScript我的建议是如果你对 TS 不熟就别硬上。真的课设阶段用 JavaScript 把功能做完比用 TS 写了一堆类型报错然后卡住更实际。但如果你的项目定位是给自己的简历加分那就用 TS并且把类型定义写得清楚一些这是面试时能聊的点。前端项目的常规结构src/ api/ // axios 请求封装 views/ // 页面比如登录、账单列表、统计报表 components/ // 复用组件比如账单表单、图表卡片 router/ // 路由配置 store/ // 状态管理选 PiniaECharts 在 Vue 3 项目里的使用方式和 Vue 2 时代不太一样。现在通常不会用vue-echarts封装组件而是直接在组件里引入 ECharts然后通过ref拿到 DOM 容器在onMounted里初始化图表实例。原因很简单ECharts 本身是一个独立的图表库它和 Vue 没有耦合直接使用反而更可控。3. 从表结构到统计接口记账系统的核心是口径问题这里我想重点展开讲一下。很多人设计表结构的时候只想着“能存数据”没有想“数据取出来之后要怎么算”。这导致后面统计接口写得特别别扭。3.1 一套常见且够用的表结构设计以单账本、多用户、多分类的方式为例最少需要四张表用户表sys_user字段说明id主键username登录名password加密后的密码nickname昵称分类表category字段说明id主键name分类名称如餐饮、交通type类型0 支出、1 收入user_id所属用户空表示系统默认分类账单流水表bill字段说明id主键user_id记账人category_id关联分类amount金额bill_type0 支出、1 收入record_date记账日期remark备注预算表budget字段说明id主键user_id用户category_id分类month预算月份如 2025-06amount预算金额这套表结构的核心设计点在于分类和账单分离。不要在每个账单里直接存一个字符串分类名因为这样统计分类占比时你会被字符串匹配玩死。分类独立成表账单只存分类 id统计时再关联查出分类名称。金额字段类型怎么选如果只是课设用Decimal或BigDecimal。如果你用 MySQL字段类型建议decimal(10,2)不要用double。因为浮点数在累计求和时会出精度问题比如 0.1 加 0.2 变成 0.30000000000000004。这种问题在演示统计报表时被老师发现是非常尴尬的。3.2 统计接口设计的三个关键点设计统计接口时最怕的不是不会写 SQL而是统计口径不一致。我见过很多项目首页的总支出显示的是一个数字折线图里月度支出加起来又是另一个数字饼图里的分类占比加起来不等于 100%。这就是口径没有统一。建议统一用三个统计接口覆盖所有展示需求第一个按月收支统计接口返回月份、总收入、总支出。这个接口用于折线图和柱状图。第二个按分类支出统计接口返回分类名称、分类支出总额。这个接口用于饼图。第三个月度汇总接口返回指定月份的总收入、总支出、结余、预算使用率。这三个接口在 service 层要使用同一个查询逻辑比如都从bill表按record_date和bill_type过滤。不要一个接口自己写 SQL另一个接口用 Java 内存计算口径很容易跑偏。一个需要注意的点是收支不要用正负号区分。很多新手会把支出记为负数收入记为正数然后统计时直接SUM(amount)。这样做的确能做出一张总余额但会导致分类统计很麻烦支出分类里出现负数饼图没法画。更合理的方案是bill_type字段单独表示收支类型amount字段永远存正数统计时用SUM(CASE WHEN bill_type 0 THEN amount ELSE 0 END)这种方式分别汇总。4. ECharts 可视化的本质是让数据“被看见”ECharts 是这个项目的重点加分项也是答辩时最好展示的部分。但这里有一个普遍的误区把 ECharts 当成一个“画图插件”文档翻到哪个配置就复制哪个配置完全没有思考图表和数据之间的关系。4.1 三种图表分别解决什么问题家庭记账系统里最常用的是三种图折线图展示最近 6 个月或 12 个月的收支趋势。折线图的 X 轴是月份Y 轴是金额两条折线分别对应收入和支出。它能直观回答“这个月比上个月花得多还是少”。饼图展示当月支出分类占比。饼图的 data 需要{ name: 餐饮, value: 1234.56 }这种格式。它回答“钱主要花在了哪里”。柱状图展示每月支出或分类金额对比。柱状图能同时展示类目和数值适合做分类对比。这三种图表的共同点是它们的 input 都是统计接口返回的数据。所以正确的工作流是先设计接口返回结构。在接口里把数据整理成 ECharts 可以直接使用的格式。前端拿到数据后直接塞给图表配置。4.2 ECharts 在前端的最小接入流程在 Vue 3 项目里使用 ECharts核心步骤是稳定的npm install echarts然后在需要展示图表的组件里script setup import * as echarts from echarts import { ref, onMounted, onBeforeUnmount } from vue const chartRef ref(null) let chartInstance null onMounted(() { chartInstance echarts.init(chartRef.value) // 这里调用接口拿到数据后 setOption }) onBeforeUnmount(() { chartInstance chartInstance.dispose() }) /script template div refchartRef styleheight: 400px;/div /template这里有几个关键点chartRef是模板里的 DOM 引用echarts.init必须在这个元素渲染完成后调用所以写在onMounted里。组件销毁时一定要dispose否则会报 “There is a chart instance already initialized on the dom” 的警告。setOption可以重复调用所以数据刷新时不需要重新init直接用同一个实例更新配置。4.3 一个常见的数据格式坑ECharts 的饼图数据格式是[{ name: 餐饮, value: 100 }, { name: 交通, value: 30 }]而后端统计接口返回的可能是[{ categoryName: 餐饮, total: 100 }]。你需要在接口返回时或者在前端做一次 mapconst pieData res.data.map(item ({ name: item.categoryName, value: item.total }))这个转换看起来很简单但恰恰是很多人卡住的地方接口返回的字段名和后端实体字段对应不上undefined 混进图表饼图就变成空白。所以前端在接到接口数据后先console.log看一眼数据再决定要不要转换。建议每个图表组件只做一件事——接收数据、渲染图表。不要在前端组件里写复杂的统计逻辑。统计逻辑放后端前端只负责把后端的统计结果呈现出来。这样分工答辩时也好讲。5. 前后端联调最容易踩的坑以及标准排查链路这个项目里前端和后端各自开发的时候都很顺一联调就出问题。这是所有前后端分离项目的通病也是课设阶段最能拉开差距的地方。下面四类问题是我见过出现频率最高的。5.1 跨域问题前端在 5173 端口后端在 8080 端口前端直接请求后端接口会被浏览器拦截。这是最常见的第一个联调问题。处理方式有两种后端配置 CORS允许指定来源访问。前端配置 Vite 代理在vite.config.js里设置 proxy让请求转发到后端。对课设来说方式二更接近生产环境因为部署时前端和后端往往经过同一个网关或同一个 Web 服务器。一个常见写法// vite.config.js export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这样前端请求/api/bill/list会被转发到http://localhost:8080/api/bill/list浏览器看到的是同源请求不会触发跨域。5.2 时间格式和金额类型的坑后端返回的时间是2025-06-01T12:00:00这种 ISO 格式前端要显示成2025-06-01这就需要在后端统一配置 JSON 序列化格式。如果是 Spring Boot 3常用做法是配置 Jacksonspring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8金额在后端是BigDecimal返回给前端的可能是100.00也可能是1.0E2这种科学计数法。为了避免这个问题金额字段在后端序列化为字符串或者确保数据库是decimal(10,2)并且在 Java 里统一用BigDecimal且保留两位小数。5.3 完整排查链路当你发现前端页面白屏、图表不渲染、列表为空时按这个顺序查打开浏览器 F12看 Network 面板里接口请求是否发出。看请求 URL 是否正确是否走代理。看请求是否返回 200如果没有去后端看日志查跨域、路径、token 鉴权。如果返回 200 但是数据为空看后端 SQL 是否查到了数据可以在 Navicat 里直接执行一下。如果数据有值但页面不显示把后端返回的 JSON 打出来看字段名跟前端代码里取的是否一致。如果是图表不渲染先写一个写死的setOption看图表是否能渲染如果能说明是数据格式问题如果图表本身都不能渲染查容器高度是否被压缩为 0。这条链路的核心思想是分层定位先确定问题在网络层、数据处理层还是展示层。不要一上来就改代码先看现象再动手。5.4 登录鉴权的边界课设项目一般都会做登录功能。常见方案是 JWT登录成功后后端返回一个 token前端存在 localStorage 里之后每个请求都在 header 里带上 token。在 axios 里统一处理// request.js import axios from axios const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config })这里要提醒一点JWT 的过期时间、密钥、token 传输方式在课设里可以简化但你必须能讲清楚它的工作流程因为这是答辩常问点。另一个容易被忽略的地方是后端的 Spring Security 或拦截器要放行登录接口、验证码接口和静态资源其他接口统一校验 token。如果你用的是拦截器注意排除路径要写全否则登录页都会请求失败。6. 从“能跑”到“能讲清楚”课设项目里真正值得投入的四个环节很多同学的项目做到最后是“能跑”但问细节就说不清楚。如果你希望这个项目成为简历上的亮点下面四个环节值得认真打磨。6.1 日志给系统装上记录仪后端至少要配置日志输出。Spring Boot 自带 Logback在application.yml里可以配置日志级别和文件输出路径。请求进来时有入参日志处理完有响应日志出错时有堆栈信息。答辩时如果老师问“万一接口出错了怎么排查”你完全可以当场演示查看日志文件定位异常修复重启。前端也要有基础的错误提示。接口请求失败时用 Element Plus 的 ElMessage 提示用户而不是直接白屏。6.2 统一返回结果和全局异常处理后端接口不要直接返回裸数据建议统一返回结构例如{ code: 200, message: success, data: {} }配合RestControllerAdvice做全局异常处理这样校验异常、业务异常、未知异常都能返回可控的格式而不是一堆堆栈信息。这个设计一旦养成习惯整个项目的代码风格会提升一大截。6.3 参数校验不只是形式写一个账单新增接口时前端要校验金额是否大于 0、日期不能为空、分类不能为空后端也要用ValidNotBlank这些注解再做一次校验。前端校验是为了用户体验后端校验是为了数据安全。课设答辩中这题是为了确认你真的理解为什么后端校验必不可少。一个最小实现// BillDTO.java public class BillDTO { NotNull(message 金额不能为空) DecimalMin(value 0.01, message 金额必须大于0) private BigDecimal amount; NotNull(message 分类不能为空) private Long categoryId; }Controller 里加Valid注解校验失败时会抛MethodArgumentNotValidException再由全局异常处理器统一转换。6.4 部署本地跑通和线上可访问是两回事课设最后一般都需要演示。本地跑没问题最怕到演示机器上起不来。建议至少提前一天做一次干净的部署演练克隆新代码、配置数据库、启动后端、启动前端、走一遍核心流程。前端部署可以选择构建后放到 Nginx 里也可以直接用npm run dev跑开发服务。后者更简单但生产演示时不如前者专业。如果选择 Nginx需要注意把前端路由的 history 模式配置好否则刷新页面会 404。一个常见的 Nginx 配置location / { try_files $uri $uri/ /index.html; }7. 如果要从这个课设里提炼一个可复用的方法论做完这个项目你会发现它其实是一个很经典的“业务系统”样本有认证、有 CRUD、有统计、有可视化、有前后端联调。它几乎覆盖了一个中小型 Web 系统的所有关键环节。这里面最值得沉淀的经验我认为是一个三步法先定义统计口径再设计表结构。没有统计需求你无法知道账单表需要哪些索引、需要按什么维度聚合。先跑通一条最小链路再扩展批量功能。比如从“新增一笔账单”开始到“查询账单列表”到“按月统计”最后到“图表展示”。链路完整了再考虑批量导入、预算管理等附加功能。先能稳定复现再谈优化。遇到问题时先确认问题的触发条件是否稳定复现再做修改。项目里最容易翻车的不是代码写不出来而是改了一行代码不知道哪块被影响到了。这三个步骤看起来简单但它的价值在于把一次课设从“写功能”升级为“做工程”。你在简历上用这个项目时面试官大概率也会问“这个项目你遇到最大的难点是什么”。如果你回答“跨域问题”“ECharts 数据格式不对”然后能完整讲清楚排查链路和最终方案这个项目就可以成为你的加分项如果你的回答是“项目功能都做完了没什么难点”那面试官就不知道该问什么了。回到最开始那个判断家庭理财记账系统的核心不是记账而是数据从产生、存储、统计到可视化呈现的整个链路。把这个链路打通它就不只是一份课设作业而是一个你真正做过、理解过、能讲明白的完整项目。
返回列表