基于Spring Boot和MyBatis的个人财务管理系统设计与实现

发布时间:2026/10/22 19:48:47
基于Spring Boot和MyBatis的个人财务管理系统设计与实现 简介一份基于Web的个人财务管理系统毕业设计源码包涵盖前台用户端与后台管理端适用于计算机相关专业毕设、课程设计或项目实践。系统采用响应式设计兼顾手机与电脑访问功能模块包括收支记账、类别管理、月度统计、财务分析、预算管理、心愿单、备忘录、计算器及财务新闻等后台支持用户管理、收支类别维护与新闻发布结构完整可直接运行或二次开发。资源共1050个文件以Java源码、JSP页面、CSS样式、JavaScript脚本及PNG图片为主另有项目配置文件与说明文档压缩包约22.93MB层级清晰便于查阅。已有333人学习下载。代码经功能验证附带项目说明能帮助理解完整开发流程也可作为初期立项演示与扩展基础。1. 毕设选个人财务管理系统图的是「能跑完也能讲清」毕业设计最怕的不是题目难而是做到一半发现自己根本讲不清「为什么要这么做」。个人财务管理系统这个方向恰恰避开了这个坑业务场景每个人都知道需求不用去猜账户、分类、流水、统计这些概念一解释老师就懂技术实现又足够撑起一篇完整的毕设论文不至于被质疑工作量。更重要的是它天然带一个可视化展示点——图表统计答辩时往屏幕上一放效果比空讲架构直观得多。这个系统解决的是个人日常收支管理里的实际问题记账、分类、查流水、看月度统计。用户能注册登录能记一笔收入或支出能按时间或分类筛选账单最后用图表看清钱花在了哪。适合的人群很明确正在选毕设题目的学生想快速搭一个可演示 Web 系统的开发者以及需要一套带源码和文档的完整项目作参考的初学者。下面从选型到落地按我实际做这类项目的顺序讲。2. 技术栈这样定从 SSM 到 Spring Boot 的取舍2.1 为什么 Spring Boot MyBatis 是这类项目的主流选择个人财务管理系统属于典型的 CRUD 统计报表类应用没有复杂的实时计算也没有高并发压力。选技术栈的第一原则不是「新」而是「你自己能讲清楚」。我见过不少同学选了 Spring Cloud 微服务来做这种单机小系统结果答辩时被问「你的注册中心解决了什么问题」直接卡住。对毕设而言Spring Boot MyBatis MySQL 是性价比最高的组合Spring Boot 让配置简化MyBatis 让 SQL 可控MySQL 则是面试和答辩都绕不开的熟人。有人会问为什么不用 JPA我的习惯是涉及统计报表的项目SQL 的可控性优先。JPA 的实体映射写起来快但遇到「按月分组求和」「按分类统计占比」这类查询最终还是得写 JPQL 或原生 SQL反而绕了一圈。MyBatis 直接把 SQL 写在 XML 里哪里性能有问题一眼就能看到调试时也方便单独测某条语句。前端方面个人财务管理系统的页面量不大且是给个人用的工具型系统我不建议上前后端分离。用 Thymeleaf 服务端渲染配合 Bootstrap 做样式ECharts 做图表三件事都是业界成熟方案且全部在同一个工程里部署时打一个 jar 包就完事。前后端分离意味着要维护两套工程、处理跨域、额外部署前端静态资源这些复杂度对毕设来说全是负担不是亮点。2.2 工程结构与包划分照着这个搭不会乱工程结构决定了你写代码时的心情也决定了导师看代码时的印象。我见过太多「所有类都堆在一个包」的项目一个 controller 里写满 SQL这种情况别说答辩自己后期改需求都想重写。一个清晰的分层结构长这样src/main/java/com/example/finance/ ├── controller/ # 接收请求参数校验返回页面或数据 ├── service/ # 业务逻辑事务控制 ├── mapper/ # MyBatis 接口与 XML 对应 ├── entity/ # 数据库实体类 ├── dto/ # 前端交互的数据对象 ├── config/ # 拦截器、异常处理等配置 └── FinanceApplication.javasrc/main/resources 下放 mapper 的 XML 文件、application.yml、static 静态资源和 templates 模板页面。这个结构对应的是最常见的分层架构controller 只做参数接收和响应封装service 里写业务规则mapper 只做数据读写。这样分层之后每一层都能单独测答辩时被问到任何一层你都能解释职责。2.3 核心依赖与配置最小 pom 与数据源参数用 Spring Initializr 生成工程时勾选 Spring Web、Thymeleaf、MyBatis、MySQL Driver 四个依赖就够了后续按需再加。这是最简可运行组合dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency注意 mybatis-spring-boot-starter 的版本需要显式指定因为 Spring Boot 的父级依赖不会统一管理它的版本。我建议用 2.3.x 而不是 3.x3.x 要求 Spring Boot 3 和 JDK 17很多人的本机环境还停在 JDK 8用 2.3.1 配合 Spring Boot 2.7 是最不折腾的组合。数据源配置写在 application.yml 里基本就是连接地址、用户名、密码三项。我额外会配一个map-underscore-to-camel-case: true这样数据库字段create_time能自动映射到实体的createTime不用在 XML 里写一堆resultMap别名。这个配置能省下不少重复代码。3. 数据建模先行账户、分类与流水三张核心表3.1 表设计决定了这个项目的天花板很多毕设项目翻车不是翻在代码上而是翻在表设计上——用户模块建了表功能模块却不知道要存什么字段。财务管理系统的主干就三条钱放在哪账户、钱是什么类型分类、钱怎么流动流水。围绕这三条核心表就是账户表、分类表和交易流水表再加一张用户表做归属隔离。系统性设计是所有业务表都带user_id外键保证每个用户只能看到自己的数据。我给出的表结构不追求大而全但每张表都有明确职责。账户表管「钱在哪」分类表管「钱算什么」流水表管「每一笔钱的进出」预算表管「钱花超了没」。后加的预算表是给系统提亮点的答辩时可以讲「我对预算超支做了实时提醒」属于低成本高产出的一块。3.2 表结构定义与字段约束说明CREATE TABLE account ( id int NOT NULL AUTO_INCREMENT, user_id int NOT NULL COMMENT 所属用户, name varchar(32) NOT NULL COMMENT 账户名称, type tinyint NOT NULL DEFAULT 1 COMMENT 1现金 2银行卡 3电子支付, balance decimal(12,2) NOT NULL DEFAULT 0.00 COMMENT 余额, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;账户表的balance字段用的是decimal(12,2)不是float也不是double。财务数据用浮点类型存储会出精度问题比如 0.1 0.2 不等于 0.3这在月度统计汇总时会显现为对不上的金额。Decimal 是定点数存钱的场景必须用它。余额字段每次记账后要同步更新这就引出流水表。CREATE TABLE transaction ( id int NOT NULL AUTO_INCREMENT, user_id int NOT NULL, account_id int NOT NULL COMMENT 关联账户, category_id int NOT NULL COMMENT 关联分类, type tinyint NOT NULL COMMENT 1收入 2支出, amount decimal(12,2) NOT NULL, remark varchar(255) DEFAULT NULL COMMENT 备注, trans_date date NOT NULL COMMENT 交易日期, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_date (user_id, trans_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;流水表的amount只存正数收支方向由type字段区分。这是一个容易踩坑的设计点不要在金额里存负数否则统计时「收入总和」和「支出总和」的 SQL 就得做额外判断。分类表我更建议预置好数据而不是让用户自由创建常见做法是在系统初始化时插入「餐饮、交通、购物、工资、理财、其他」等固定分类这样报表统计时不用处理千奇百怪的分类名后续做权限也能给每个用户复制一份默认分类。3.3 预算表与统计所需的冗余字段如果要做预算提醒预算表的逻辑是按月记录某个分类的预算上限当月支出汇总后与之比较。这里有个优化点——在流水表里存month字段比如202506这种格式统计时直接用WHERE user_id? AND month?过滤比DATE_FORMAT(trans_date, %Y-%m)走函数索引要快得多。虽然数据量小感觉不出差异但这是一个值得在文档里写一笔的设计取舍能体现你思考过索引和查询性能。至于生成的月度统计我一般会让流水表保留明细统计结果用 SQL 实时聚合不单独建汇总表因为个人级别的数据量完全不需要冗余存储。4. 核心功能实现记账、分类统计与图表展示4.1 记账接口先从 service 层的事务说起记账这个动作涉及两次写操作插入一条流水记录、更新对应账户余额。这两步必须在一个事务里完成否则会出现「流水记了但余额没变」或反过来的一致性问题。Spring 的Transactional注解解决的就是这个Service public class TransactionServiceImpl implements TransactionService { Autowired private TransactionMapper transactionMapper; Autowired private AccountMapper accountMapper; Override Transactional(rollbackFor Exception.class) public void addTransaction(TransactionDTO dto) { Transaction t new Transaction(); t.setUserId(dto.getUserId()); t.setAccountId(dto.getAccountId()); t.setCategoryId(dto.getCategoryId()); t.setType(dto.getType()); t.setAmount(dto.getAmount()); t.setRemark(dto.getRemark()); t.setTransDate(dto.getTransDate()); transactionMapper.insert(t); // 更新账户余额收入加支出减 Account account accountMapper.selectById(dto.getAccountId()); BigDecimal newBalance dto.getType() 1 ? account.getBalance().add(dto.getAmount()) : account.getBalance().subtract(dto.getAmount()); accountMapper.updateBalance(account.getId(), newBalance); } }rollbackFor Exception.class这个参数值得说明默认情况下Transactional只对RuntimeException回滚如果你的 service 方法抛了一个受检异常事务是不会回滚的。显式指定rollbackFor是想让所有异常都触发回滚这是保险写法。另外注意上面的余额更新是「读出来再算再写」在并发场景下有丢更新的风险但对毕设系统来说完全够用你可以在文档里提一句「后续可改为 SQL 原子更新」这反而是加分项。4.2 分类统计一条 SQL 搞定月度支出构成统计报表的核心是「按分类聚合」。这个需求用 MyBatis 写一段自定义 SQL 比 java 代码里做循环相加清晰得多select idsumByCategory resultTypemap SELECT c.name AS categoryName, SUM(t.amount) AS totalAmount FROM transaction t LEFT JOIN category c ON t.category_id c.id WHERE t.user_id #{userId} AND t.type 2 AND DATE_FORMAT(t.trans_date, %Y-%m) #{month} GROUP BY t.category_id, c.name ORDER BY totalAmount DESC /select这里resultTypemap省去了定义 DTO 的步骤适合这种一次性查询。DATE_FORMAT在数据量大时可能导致索引失效但个人财务系统一个月几百条流水性能上完全没问题。返回的ListMapString, Object直接交给前端塞进 ECharts 的饼图数据格式里即可。4.3 图表页面的数据对接ECharts 的 format 处理前端图表我建议单独做一个统计页面和后端接口分离。后端返回 JSON前端用 jQuery 的$.get拉数据后拼接 ECharts 的 option。关键点是 ECharts 饼图的数据格式是[{name: 分类, value: 金额}]而后端返回的 map 里 key 是categoryName和totalAmount需要映射一次$.get(/api/statistics/monthly, { month: $(#month).val() }, function(res) { var pieData res.map(function(item) { return { name: item.categoryName, value: Number(item.totalAmount) }; }); var chart echarts.init(document.getElementById(pieChart)); chart.setOption({ title: { text: 月度支出构成 }, tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: pieData }] }); });Number(item.totalAmount)这行是常见的坑点Java 后端返回的 BigDecimal 在 JSON 序列化后可能是字符串格式具体取决于你用的序列化配置直接传给 ECharts 会导致数值计算异常。包一层Number()保证转成数值类型。另外month参数格式统一成YYYY-MM这样后端DATE_FORMAT也容易匹配不需要做任何日期格式转换。4.4 收支趋势折线图按日分组的数据聚合月度对比折线图稍微复杂一点它需要知道某月内每一天的收入和支出分别多少。后端写一个按日聚类的查询前端再填成折线图。这里有个体验上的细节一个月可能有几天没有记账如果后端只返回有记录的天前端画出来的折线就是断断续续的。更好的做法是后端返回的数据缺失日期用 0 填充或者在 SQL 里用「连接一个日期序列」。前者比较容易实现我推荐在 java 层处理后端返回的数据再传给前端。这块实现属于加分项在论文里可以作为「面对空数据的优化处理」来写。5. 部署与联调避坑从本地跑通到答辩演示的 5 个常见问题5.1 时区问题导致的时间差 8 小时现象插入一条流水记录页面显示的时间比本地时间早或晚8 小时。原因MySQL 连接串里没指定时区驱动用了服务器默认时区而本机 MySQL 的system_time_zone是 CSTJVM 用的是 UTC。解决在 application.yml 的 JDBC 连接串加serverTimezoneAsia/Shanghai或统一用CST。这个问题的根源是时区配置不一致而不是代码逻辑错误排查时先看连接串最省时间。5.2 静态资源 404界面没有样式现象Thymeleaf 页面能打开但 CSS、JS 全部加载失败控制台报 404。原因Spring Boot 对静态资源的默认映射是classpath:/static/**如果你的静态文件放在src/main/resources/static下且路径正确通常是访问路径写错了比如在模板里写了/css/style.css但实际目录多了一层。解决打开浏览器 F12 看 Network 里静态资源的实际请求路径对照static目录下的真实路径修改。如果是 Thymeleaf 模板用th:href{/css/style.css}而非硬编码路径这样能自动带上 context path。5.3 MyBatis 的 XML 文件没被编译进 target现象启动报Invalid bound statement (not found)检查 mapper 接口和 XML namespace 都对。原因Maven 默认只把resources目录下的文件编译进 classpath如果你的 XML 放在src/main/java下面需要改 POM。但如果你按照本文的工程结构把 XML 放在resources/mapper下就根本不会碰到这个问题。解决确认 XML 在 resources 目录并且在 application.yml 里配置mybatis.mapper-locations: classpath:mapper/*.xml。5.4 本地跑得通换电脑连不上数据库现象项目在自己电脑上正常把代码或有源码文档的压缩包发给同学后对方启动就报数据库连接失败。原因application.yml里写死了本机数据库的用户名密码对方的环境不一样。解决把配置抽到application-local.yml和application-prod.yml两套或者直接在文档里写明「请修改数据库用户名密码」。毕设项目的说明文档里至少有一个人人可复现的环境准备清单这件事做好能避免交代码后被反复问「为什么我跑不起来」。5.5 演示时图表不显示或数据不对现象答辩时打开统计页面饼图空白或者数字和流水明细对不上。原因最常见的是统计 SQL 的月份参数格式不一致比如前端传了2025-6后端用的是DATE_FORMAT匹配%Y-%m得到2025-06匹配不上。解决在前端把月份 patch 成两位或者在 SQL 里用DATE_FORMAT(trans_date, %Y-%m) DATE_FORMAT(#{month}, %Y-%m)让 MySQL 自己处理格式。这条我建议在写接口时就统一约定前端传YYYY-MM后端不要做任何加工直接透传到 SQL。6. 答辩前值得做的三件事权限、图表与测试用例先说权限。很多初版系统只有登录功能没有做拦截意味着用户登录后可以拼 URL 访问别人的接口。我建议用 Spring 的 HandlerInterceptor 写一个简单的登录拦截器放行/login、/register和静态资源其他路径全部判断 session 里有没有 user没有就重定向到登录页。这个实现短小但能讲清楚「拦截器 vs 过滤器」的区别——拦截器是 Spring 层的概念能拿到 Handler 信息过滤器是 Servlet 层更底层。答辩被问到为什么用拦截器时可以对比着讲这是个加分点。然后说图表。如果只想做一个图表优先做「月度支出构成饼图」因为它最能直观反映系统价值。有两种加分做法一是加时间筛选控件让老师选不同月份看图表变化二是再加一个「支出趋势折线图」展示最近 6 个月每个月的支出变化这个图不需要额外接口后端加一个GROUP BY month的查询即可。图表的配色和单位也要检查一遍金额单位用「元」饼图把比例显示打开这些细节在投屏上比代码更能体现用心。最后是测试用例。不要求做完整的单元测试覆盖但至少给 service 层最核心的方法写 3 个测试正常记账时余额是否正确更新、预算超时是否有提示、删除流水时余额是否回滚。用 JUnit 加构造数据跑一遍把测试结果截图放进论文比在论文里写「已进行充分测试」有说服力得多。我自己做这类项目时吃过亏——某次演示前临时改动余额更新逻辑结果忘了同步改测试数据导致删除流水后的余额回滚错了方向。从那以后养成了改业务逻辑先跑一遍测试的习惯。这三点做完系统的完成度已经是「能答辩」的水平。回头看你手上的源码包如果它已经包含了以上功能那你要做的事就是在懂原理的基础上把细节补齐比如前端传参格式、时区配置、静态资源的路径如果它还缺这些那正好按上面顺序补上。个人财务管理系统这个题目的上限不高但下限也低——认真做完并讲清每一张表和每一个接口它就很稳。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询