
各位做 Java 后端、或者正在折腾毕业设计/个人项目的朋友应该对“个人财务管理系统”这个题目不陌生。它听起来不复杂真正动手做的时候才发现账目流水怎么设计才不臃肿、统计报表用什么方案算得又快又准、预算提醒怎么做得不鸡肋还有“论文怎么写才能跟代码匹配上”这些问题一个比一个现实。我最近刚好整完一套基于 Spring Boot 的个人财务管理系统从数据库建模到接口实现再到论文框架梳理全程自己踩了一遍。这篇文章就把我的完整思路、核心代码逻辑、碰到过的坑和排查方法都倒出来给准备做类似系统的你一个能直接参考的底稿。这个系统能做的事情很直白多账户管理、收支流水记录、分类统计、月度/年度报表、预算超支提醒、账单导入导出。前端用的是轻量级模板加一点原生交互后端是标准的 Spring Boot 三层架构。适合谁看正在做课程设计、毕业设计或者想自己写个小工具管理钱袋子又不想把技术栈搞得太重的开发者都可以照着这份思路往下走。1. 整体设计与技术选型为什么是 Spring Boot而不是别的先聊选型。个人财务管理系统这种项目业务不复杂但涉及“钱”的数据对稳定性和可追溯性有要求。我当时对比过几套方案最后锁定了 Spring Boot理由很实际。1.1 技术栈的横向对比与取舍有人会问这种规模的系统直接用 Python Flask 或者 Node.js 不更轻快确实论启动速度和代码量Flask 半小时能出一个能跑的 demo。但项目往往还挂着“课程设计”或“论文”的指标这意味着你需要一套足够“规范”的工程骨架而不是一个脚本式应用。Spring Boot 的价值在于它自带完整的依赖管理、分层约定和生态Controller-Service-Mapper 的结构让代码天然清晰写论文的时候可以非常顺理成章地画出架构图、数据流图而不需要为了“凑架构”硬造层次。另一个关键因素是 Spring Boot 和 MySQL 的组合在就业市场上最通用答辩时被问到的概率低面试时也容易展开。前端我没有用前后端分离。原因很简单个人财务系统核心是数据的增删改查和统计用 Theymeleaf 模板加 Bootstrap 撑起管理界面足够。前后端分离意味着要额外处理跨域、Token 认证、前端工程化构建这些对单人小项目来说是徒增负担而且论文里还要多写一章前端工程化容易分散主线。当然如果你想把系统包装成前后端分离架构作为学习成果那另说但我会先劝你把后端做厚前端够用就好。最终的技术栈清单如下后端Spring Boot 2.7.x、MyBatis-Plus、Spring Security用于登录鉴权数据库MySQL 8.0模板引擎Thymeleaf前端组件Bootstrap 5 jQuery ECharts图表展示工具Lombok、Hutool、EasyExcel导入导出1.2 架构分层与设计模式我按照经典的四层结构来组织代码controller接口层 - service业务层 - mapper数据访问层 - entity实体类 / dto传输对象 / vo视图对象这种分层的核心价值不只是“好看”而是把每个动作的职责钉死。比如你写一个“新增一笔支出”的接口Controller 只负责接收参数和返回结果Service 负责校验金额是否合法、账户余额是否够扣、流水表是否要插入记录Mapper 只做数据库交互。这样一旦出问题你顺着调用链就能定位到是哪一层的事而不是在一坨面条代码里翻来覆去。实体类我用了 MyBatis-Plus 的注解风格避免写一堆 XML。数据模型里有几个关键表后面会详细说。DTO 和 VO 我是严格分开的Controller 层绝不直接暴露实体类给前端因为实体字段可能包含一些内部标记比如逻辑删除标志、更新时间直接返回会泄露不必要的信息也容易让前端字段和数据库字段强耦合。这一点建议在写论文时单独拎出来讲能体现你的工程意识。2. 数据库设计与核心功能拆解账本系统的灵魂在表结构个人财务系统功能看着简单但表设计决定了报表能不能跑得动、扩展性好不好。我见过太多人上来就建一张“流水表”一个“用户表”结果做到统计一门心思用 SQL 嵌套子查询数据库累死代码也难写。这一节讲清楚我最终表结构是怎么定的以及每一步背后的思考。2.1 核心表结构与字段设计我一共建了六张业务表加一张用户表。六张业务表分别是账户表、流水表、分类表、预算表、账单导入记录表、系统配置表。每张表都要说清楚为什么建、字段怎么定。账户表account——记录用户下的多个资金账户比如现金、储蓄卡、信用卡、微信钱包。关键字段有账户名称、账户类型1 现金 2 借记卡 3 信用卡 4 电子钱包、初始余额、当前余额、币种当前只做人民币但留字段以后扩展、状态可用/冻结、用户 ID、备注、创建时间。特别注意账户当前余额不要通过流水表实时 sum 去算那样性能会越来越差。每次发生收支时在事务里同时更新账户余额这是个人财务系统的一个基础优化点。流水表transaction——这就是记账的主表。字段流水类型1 收入 2 支出 3 转账、金额、账户 ID、分类 ID、交易时间、商户/摘要、备注、关联流水 ID处理转账时需要成对出现、创建时间。金额我用的是 DECIMAL(12,2)绝不用 DoubleJava 对应 BigDecimal这是财务数据的常识。交易时间单独存时间戳而不是用创建时间替代因为用户可能会补录前几天的账。这一点很重要很多初学者直接把创建时间当交易时间做统计时会出现“明明应该是上个月的支出却算到了这个月”的诡异现象。分类表category——收支分类。包括分类名称、类型收入/支出、图标标识、父分类 ID支持二级分类、是否系统预置、用户 ID。系统预置分类比如餐饮、交通、工资、理财收入用户还可以自定义。这里注意预置分类的 user_id 设置为 0表示全局共享自定义分类的 user_id 是自己的查询的时候要 OR 一下确保系统分类和用户分类都能加载。预算表budget——预算对象是“分类 周期”。字段用户 ID、分类 ID、预算周期1 月 2 季 3 年、预算金额、起始日期。预算触发逻辑需要配合定时任务或者实时计算后面展开。账单导入记录表import_record——因为要支持 Excel 导入银行流水需要记录每次导入的文件名、导入时间、成功条数、失败条数、导入批次号。这个表的最大作用是幂等控制用户重复导入同一个文件时通过批次号或者文件名加内容哈希判断是否已经导入过避免流水翻倍。系统配置表config——键值对结构存一些用户可调的阈值比如“单笔支出超过多少时进行提醒”、“预算超支提醒提前几天”、“默认的统计币种”。把这类参数做成配置而不是写死在代码里以后调整不需要重新发版本。2.2 为什么流水表不直接存“余额”这里有个容易引起讨论的点流水表里我存了“发生后的余额”字段吗没有。原因很简单如果存了一旦用户删除或修改某笔历史流水后续所有流水的余额就全部错乱你必须写一大段更新逻辑去重算。而不存余额余额的实时值始终由账户表保存任何流水的增删改只需要修订账户表的余额即可。但查询时往往需要展示“这笔交易发生后的账户余额”怎么办我的做法是默认不展示这个值只展示交易时间和金额。如果要看账户余额变化曲线就按时间区间分段计算流水累计金额再叠加账户初始余额。这在数据规模个人级下不会慢。对于千万级大数据量的系统另说但个人财务系统到不了那一步。2.3 分类与预算的联动设计预算模块的思路是预算挂在某个分类下。月度预算是最常用的数据库里只需要存“周期类型”和“起始日期”用的时候根据起始日期和周期类型计算出当前周期的起止时间再在这个时间区间内去流水表汇总该分类下所有支出金额对比预算值。举个例子你在 3 月 10 日设置“餐饮”分类的月度预算为 2000 元周期起始日期是 2025-03-01。那么当前周期起止时间是 2025-03-01 到 2025-03-31。系统查询流水表里分类为“餐饮”、交易时间为该区间内的支出流水总和比如已花了 1800 元剩余预算 200 元。如果又新增一笔 250 元的餐饮支出预算立即超支这个时候可以触发提醒。这种实时计算的好处是数据永远最新不用等到月底对账。缺点是每次新增流水都要多查一次预算对个人系统这完全不是问题甚至可以接受。3. 核心功能模块实现细节从登录鉴权到统计报表系统功能我拆成了五个模块来写用户与账户管理、收支流水管理、统计报表、预算提醒、账单导入导出。每个模块都有一些值得记录的实现细节。3.1 登录鉴权用 Spring Security 还是自己写拦截器很多课程设计项目都在用自己写的拦截器加 Session不是说不行但 Spring Security 作为业界成熟的权限框架写论文时是一个加分项。我用的是 Spring Security JWT 的无状态方案。登录成功后后端返回一个 Token前端存储在 localStorage后续每次请求在请求头里带Authorization: Bearer token。Security 配置里比较重要的是定义哪些路径放行、哪些需要认证。比如登录接口、注册接口、静态资源放行其余全部认证。同时定义一个自定义过滤器解析 Token 并设置用户上下文。自定义 UserDetailsService 实现从数据库加载用户信息。这里容易踩的坑是Spring Security 默认会开启 CSRF如果你的接口是无状态的 JWT必须禁用 CSRF否则 POST 请求全部会被拒绝。我当时就是被这个坑卡了半天一直报 403排查到最后发现是 CSRF 没有关。3.2 收支流水的增删改查与事务控制新增流水的接口步骤是这样的校验参数类型、金额、账户、分类不能为空金额必须大于 0根据流水类型处理账户余额收入则账户余额增加支出则账户余额减少转账则从转出账户扣钱、转入账户加钱生成流水记录插入数据库如果是转账同时生成两条流水记录一条类型为转账-转出关联对方流水 ID一条类型为转账-转入更新账户余额提交事务。以上每一步都在同一个事务类里面方法上加Transactional。这是一个非常关键的设计。假设转账扣了 A 账户的钱但在插入流水 B 时数据库异常没有事务就会导致钱扣了账没记上。加了事务后任何一步失败前面所有数据库操作都回滚保证数据一致性。修改和删除流水要麻烦一些。修改流水时需要先把旧流水的影响还原再按新流水重新计算。删除流水时同样要把账户余额恢复为扣除该笔交易之前的状态。这些逻辑必须在事务里执行否则数据会越改越对不上。我建议把这部分写一个独立的 Service 方法并加详尽的注释因为在论文的“系统测试”部分你可以把这几个边界场景写成测试用例展示系统的健壮性。3.3 统计报表ECharts 图表的背后是聚合查询报表模块是个人财务系统里最能出效果的部分也是用户在答辩时最容易“哇”一声的亮点。我使用了 ECharts 展示三类图折线图收支趋势、饼图分类占比、柱状图月度对比。后端查询这块我没有用复杂的 SQL。MyBatis-Plus 的 QueryWrapper 可以完成大部分条件筛选但涉及到按月分组、按分类分组时要写自定义 SQL。比如统计某个月份每天的收入和支出总额SQL 大概是SELECT DATE_FORMAT(transaction_time, %Y-%m-%d) AS day, SUM(CASE WHEN type 1 THEN amount ELSE 0 END) AS income, SUM(CASE WHEN type 2 THEN amount ELSE 0 END) AS expense FROM transaction WHERE user_id #{userId} AND transaction_time BETWEEN #{startDate} AND #{endDate} GROUP BY DATE_FORMAT(transaction_time, %Y-%m-%d) ORDER BY day;注意这里的统计日期要基于交易时间而不是创建时间。如果用户补录了几笔上个月的账统计结果也要相应体现到上个月的报表里这样报表才符合真实的“个人财务”视角。还有一种常见的需求查询某分类下的累计支出占比。SQL 的编写思路和上面类似用 type2 且分类分组。而在 Service 层用 BigDecimal 做除法计算百分比时一定注意除零异常。用BigDecimal.ZERO.compareTo(total) 0判断。3.4 预算提醒定时扫描还是实时触发预算提醒我做了两种方式。一种是实时提醒每次新增或修改支出流水后立刻查询当前周期的预算使用情况如果超过预算就在响应里返回一个提醒字段前端弹出提示。这种方式用户体验最好但不是所有场景都覆盖因为用户可能批量导入账单根本不会逐条看到提醒。另一种是每日定时任务用 Spring 的Scheduled在每天 20:00 检查所有用户的所有预算如果触发阈值生成一条待办提醒写入提醒表用户登录后在首页看到未读提醒。这种方式更保险。定时任务记得在启动类开启EnableScheduling同时注意定时任务方法不要忘了做时间边界上的计算避免由于时区或者系统时间不一致出现偏差。我最终是“实时触发 每日定时”双保险。论文里你可以把这两种方式的优缺点对比写出来既是设计思考也是体现工程权衡的好素材。3.5 账单导入导出EasyExcel 的实用细节支持从 Excel 导入银行流水是非常实用的功能。我采用的是 EasyExcel它对大数据量的写入做了优化个人场景更是游刃有余。导入的模板结构是交易时间、类型、金额、分类、商户、备注。用户上传文件后后端解析每一行做校验日期格式是否合法、金额是否正数、分类是否存在。如果某一行失败记录失败原因继续解析其他行最后生成一条导入记录返回成功和失败条数。这里要注意文件大小限制我配置了最大 10MB。导出功能相对简单把查询到的流水列表直接写入 Excel同时生成一个汇总 Sheet包含总收入和总支出。导出的时候文件名不要用固定名字要拼上时间戳避免同一个浏览器重复下载相同文件时被缓存。4. 论文怎么写才能和源码呼应上我的写作顺序和框架建议源码做完了论文往往是很多人的痛点。代码写了一千行却不知道怎么落到 Word 里。我自己的经验是论文的最佳写作时间不是代码写完以后而是边写代码边记录设计决策和测试过程。如果代码已经完成后再回忆着写很多细节容易模糊。4.1 论文整体框架从摘要到结论的段落设计标准的软件系统类论文推荐按照下面的骨架来填充摘要和关键词摘要要包含四要素背景个人财务管理的重要性、系统目标一个基于 Spring Boot 的个人财务管理系统、技术路线Spring Boot MySQL Thymeleaf ECharts、成果实现了多账户管理、预算提醒与统计报表等功能。第一章 绪论研究背景与意义、国内外研究现状、主要工作与论文结构。这一章不用写太多但要体现出你了解为什么做这个系统。研究现状不要直接照搬百度百科可以写传统手工记账的弊端、Excel 记账的不足然后引出信息化方案。第二章 相关技术介绍Spring Boot、MyBatis-Plus、MySQL、ECharts、Spring Security。不要写教科书式的定义要结合“你在这个系统中用它做了什么”来写。比如“MyBatis-Plus 用于简化数据持久层开发通过注解和 Wrapper 构造查询条件”比单纯介绍框架特性更有价值。第三章 系统分析可行性分析技术、经济、操作、需求分析功能性需求和非功能性需求、用例图、业务流程分析。画用例图可以用开源绘图工具论文里放清晰的矢量图。第四章 系统设计总体架构图、模块设计、数据库设计ER 图、表结构说明、接口设计。这一章是你系统设计的核心需要和源代码完全对应。第五章 系统实现每个核心功能模块的界面截图、核心代码片段和关键逻辑说明。代码片段不要贴大段只贴最核心的方法并加上讲解。第六章 系统测试测试环境、测试用例表、功能测试结果分析、性能测试比如并发登录或者大数据量查询的响应时间。第七章 总结与展望说清楚完成了什么、不足是什么、未来可以怎么改进。4.2 如何把源码和论文对应起来避免“文不对码”我见过大量论文里代码截屏和实际源码不一致或者文中描述的功能在代码里根本没有。这个问题会造成两个影响答辩时被问到细节回答不上来或者查重的时候出现“逻辑断裂”。我的做法很简单论文里涉及到的每个功能都要在源码里能找到对应模块写出一张“功能-模块-方法”的对照表。比如“月度预算超支提醒”这个功能在论文中写实现逻辑时要明确指出对应的 Service 类是 BudgetServiceImpl核心方法是checkBudgetExceeded(Long userId, Long categoryId, BigDecimal amount)。这听起来像是多余的工作但对答辩非常有利。老师问“你预算提醒是怎么实现的”你直接说“我在新增流水后调用 BudgetService 的实时检查方法同时每天定时扫描所有用户预算两周后生成提醒列表。”如果你在论文里已经把流程图画出来再配合源码对照那就是稳的。4.3 图表收集与测试数据准备论文中需要的数据图表不要临到用时才去找而是在开发过程中每个功能稳定后随手截图保存。我建议按模块建目录存放截图登录、账户列表、新增流水、统计报表、预算设置、导入导出。测试数据也尽量做得真实比如三个账户、连续六个月的数据每个月有日常消费的波动。这些数据可以帮助你生成漂亮的折线图和饼图让论文版面更有说服力。测试部分我写了十几个用例覆盖核心业务正常新增收入、正常新增支出、支出大于余额是不是要拦截注意个人财务系统不建议拦截因为很多时候信用卡或信用账户允许超额但这个决策要在论文里说明、转账后两个账户余额是否正确、删除流水后余额是否恢复、预算超支是否提醒、导入重复文件是否幂等、未登录访问接口是否返回 401。每个用例包含输入、执行步骤、预期结果、实际结果这样的测试报告一眼就能看出工作量。5. 常见问题与排查技巧实录我踩过的坑和解决方案开发过程中有几个问题非常典型我几乎每次做同类项目都会碰到这里统一列出来并附上我的排查思路大家可以少走弯路。5.1 新增支出后账户余额变成负数刚开始我把余额更新逻辑直接写在 Service 里支出时balance.subtract(amount)。看起来没问题但一旦金额字段类型不匹配比如前端传的是字符串“50”类型转换异常或者精度丢失就会出现余额不符。后来我在 DTO 里对金额字段使用 BigDecimal 类型并在 getter/setter 写校验统一在 Service 层做金额格式化。余额负数本身不算 bug关键是你需不需要限制。多数个人财务管理软件允许负数比如信用卡透支我的处理是如果账户类型是信用卡允许余额为负是借记卡或现金则提示“余额不足”。这种业务规则要写清楚不然用户会疑惑。5.2 时间统计相差八小时的问题查询月度收支时总是出现数据偏少。排查后发现MySQL 连接串里没有配置serverTimezoneAsia/Shanghai导致 JDBC 时区使用了默认时区和本地时间差八个小时。在 database url 上加上参数就能解决。另外Java 后端返回日期给前端的时候如果用的是Date类型JSON 序列化有时会变成时间戳需要在实体字段上用JsonFormat(pattern yyyy-MM-dd HH:mm:ss)明确格式。5.3 JWT 登录后每次请求 401这个问题的常见原因有两个。第一是 Token 过期时间设置太短比如 30 分钟用户用着用着就失效了前端没有做好刷新机制第二是前后端 Token 拼接格式不一致。我在前端封装了一个 axios 实例统一在请求拦截器里加Authorization头避免漏加。后端自定义过滤器里如果解析 Token 失败要返回 JSON 提示而不是默认的重定向到登录页。5.4 数据库字段与实体类字段映射报错MyBatis-Plus 默认实体类字段驼峰转下划线但如果数据库字段命名不规范比如一个字段是budgetCycle数据库中写成了budget_cycle没问题如果写成了budgetcycle那就会映射不上。这种问题报错不一定明显数据查出来是 null。排查时候把 SQL 日志打开MyBatis-Plus 会在控制台打出执行的 SQL对照字段就能发现。5.5 导入 Excel 时中文乱码EasyExcel 本身对 UTF-8 支持很好但如果用户上传的 Excel 是旧版的.xls编码会有点小问题。我的处理方式是强制要求上传.xlsx格式并在前端限制文件后缀。如果确实要兼容.xls可以读取流并转码但会增加复杂度建议不要为了这种边缘需求拖累主流程。5.6 定时任务重复执行部署到服务器后有时候发现预算提醒重复发。这可能是因为部署了多个实例例如通过多个端口启动每个实例都跑定时任务。个人项目一般只有一个实例但如果用 Docker 扩容时要注意这个问题。解决方案有很多比如使用分布式锁或者为任务指定一个固定实例执行。论文里如果提到部署方案这个点可以作为一个“系统不足与展望”来写。6. 代码层面的几个实用优化让系统跑得更稳更合理除了功能实现我还做了几处工程优化这些细节虽然不起眼但能让你写论文时有更多“干货”可写。6.1 统一响应结果封装后端所有接口统一返回ResultT结构包含 code、message、data 三个字段。前端判断 code 是否为 200 来决定提示成功还是失败。这个封装的好处是无论控制器里面执行什么操作前端都可以用一套逻辑处理成功和异常代码整齐很多。在全局异常处理类里捕获参数校验异常、业务异常、未知异常分别返回不同的 code日志记录异常堆栈。6.2 参数校验怎么写得优雅又严谨我用的校验方式是 Spring 的Validated注解配合 DTO 上的校验注解。在新增流水接口中DTO 字段上标注NotNull、DecimalMin(0.01)、Size等避免在 Service 里手写一长串 if。手写校验容易遗漏而且代码可读性差。使用注解校验后参数校验逻辑清晰还会自动把错误信息返回给前端。当然有些跨字段的校验比如转账时转出账户不能等于转入账户需要放在 Service 层处理因为依赖业务上下文。6.3 为什么数据库连接池要配置参数默认的 HikariCP 连接池开箱即用但如果不配置最大连接数当并发量稍高时可能出现连接获取超时。我给项目配置了最小连接数 5、最大连接数 20连接超时 30 秒。这个配置对于个人系统绰绰有余但论文里的“性能测试”如果拿这个参数来说明系统能支撑多少并发会更有说服力。实际要做并发测试可以用简单工具模拟 50 个用户同时调用查询流水接口观察响应时间。6.4 接口层面的防重复提交个人财务系统的用户如果手快可能双击“保存”按钮导致同一条流水插入两次。为了防止这种问题我给“新增流水”接口增加了一个基于 Token 的防重复提交机制前端在首次请求时申请一个幂等键后端在 Redis 里记录这个键过期时间 5 分钟处理请求前检查键是否存在如果已存在就拒绝重复提交处理完成后删除键。当然没有 Redis 的课程设计也可以基于数据库唯一索引或应用内 ConcurrentHashMap 实现后者适合单机场景但不够规范。6.5 大数据量查询如何避免“慢 SQL”假设用户连续记账几年流水表可能有几万条数据按月统计如果不走索引也会慢。我设计的索引策略是在transaction表上以user_id transaction_time作为联合索引同时transaction_time单独建索引因为每次统计都要按时间过滤。分类统计的查询会用到category_id因此也建了索引。这些索引在数据量不大时看不出来效果但写论文时可以在数据库设计里说明“为了提高报表查询效率建立了如下复合索引”这是很标准的加分项。7. 个人经验总结与扩展想法整套系统从零到完成我前后花了两周左右的业余时间其中数据库设计和预算模块的打磨花费的精力最多。数据库设计功在一时如果你一开始表结构建得不合理后面所有代码都会受影响预算模块则是因为它同时涉及实时、批量、提醒、状态管理等多个维度联动性最强。如果你也准备做类似项目我建议先不要急着写代码花一个晚上把核心流程捋清楚。怎么算账先说清楚支出、收入、转账的业务规则。比如“转账算不算收入”我这里的定义是转账不算收入和支出只是账户间资金转移。那你把这两条流水归到“转账”类型统计收入支出时要把它们排除。这种业务定义在论文的“需求分析”里必须写明白不然图表会乱。还有一点做完系统后我强烈建议用一套自己攒的测试数据跑一遍从月初到月末的完整流程录入收入、分多笔支出、调整某笔大额支出、导出月度报表看整体数据是否一致。我当时跑出了一个问题分类统计的报表把“转账”也算成了支出后来在 SQL 里加上了type ! 3条件才解决。这类问题如果不通过整体流程测试很难暴露出来。最后分享一个小技巧项目里可以存放一个docs/目录把开发中的截图、关键设计的思维导图、测试用例的表格都放进去。写论文时直接从这里取材料省去到处找截图的痛苦。代码的版本管理建议从第一天就建立我在项目早期因为偷懒没有用版本管理后来改动分类表字段时差点回不去从此再也不敢不开仓库。这个系统后续想继续扩展的话方向其实很多。比如接入真实银行 API 做自动同步账单或者加入简单的投资理财账户基金、股票来记录持仓市值。不过这些功能对个人系统来说边际成本会明显上升建议先把手头的记账和预算功能做到顺手再考虑更复杂的扩展。我个人的体会是好用的财务系统核心不在于功能多而在于每一次记账都干脆利落每一次统计都对得上账。先把这一点做扎实源码和论文都会自然立得住。