ARTICLE DETAIL

资讯详情

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

SpringBoot+MySQL实现学生财务管理系统:从建表到部署避坑指南

SpringBoot+MySQL实现学生财务管理系统:从建表到部署避坑指南 简介基于Spring Boot与MySQL的财务管理系统设计与实现资料包适合毕业设计、课程设计以及Spring Boot初级开发者参考。系统覆盖收入支出记录、资产负债管理、会计科目维护、预算编制与执行监控、财务报表生成、财务审批流程、数据统计分析与备份恢复等核心功能界面友好直观并具备安全保护机制保障财务数据隐私。压缩包大小约17.76MB内含项目源码、设计文档、部署说明与视频演示等主要文件源码均经过测试校正可百分百成功运行设计文档帮助理解架构与各模块关系视频演示便于快速掌握环境部署和系统操作内容组织清晰、便于按需查阅。目前该资源已有231人学习适合需要完成毕设或课设、希望直接获得完整可运行项目的读者可大幅减少从零搭建和排查问题的时间。1. SpringBootMySQL学生财务管理系统最稳的一条路SpringBootMySQL的财务管理系统是Java课程设计和毕业设计里出现频率最高的选题之一也是我见过交付最顺的一种技术组合。SpringBoot负责把业务接口暴露给前端MySQL负责把每一笔账落库两者一配合一个能演示、能答辩、能在本地跑起来的财务管理系统从拿到源码包到完成部署通常只需要一个晚上。这篇文章不止讲这是什么还会顺着这个标题里带出的源码包、设计文档、部署说明和视频演示把表结构怎么设计、核心代码怎么写、MySQL怎么配、哪些地方容易翻车全部拆开。适合正在赶课程设计的学生也适合第一次接触SpringBootMySQL业务开发的从业者照着复现。2. 设计文档先于代码财务系统的表结构和业务边界2.1 先分清业务边界财务管理系统不是记账本很多人在做这个题目时直接把系统做成“增删改查的记账本”这是最严重的误解也是答辩时最容易被打断的原因。财务管理系统和普通记账软件的区别在于它要管住账目的“凭证”和“科目”而不是只记录“花了多少钱”。凭证是业务的原始单据每一笔收入和支出都要对应一个凭证号科目是钱的分类比如“办公费”“差旅费”“销售收入”每一笔凭证都要落到具体的科目上。拿到源码包之后我建议你先看设计文档里的用例图和ER图而不是急着跑代码。设计文档通常会把系统拆成用户管理、科目管理、凭证管理、报表统计、预算管理这几个模块。用户管理负责登录和角色区分科目管理负责定义收入和支出的分类凭证管理是整个系统的核心报表统计负责按月份和科目汇总金额预算管理是可选的加分项。把这条业务边界定清楚后面对表结构设计才有方向。常见的做法是角色拆两种管理员和普通用户。管理员维护科目和用户普通用户录入凭证和看报表。不需要做复杂的审批流、不做多租户、不做权限细粒度控制课程设计和中小型内部系统把这些边界守住就好多做的功能往往会成为部署和维护的负担。2.2 四张核心表建表SQL与字段选择理由数据库设计是整个系统最关键的部分表结构定错后面代码全部要返工。我一般会建四张表用户表、科目表、凭证表、预算表。先建库再建表字符集统一用utf8mb4避免中文乱码。执行下面这段SQL前请先确认MySQL已经启动。在MySQL命令行或者Navicat里执行都可以。CREATE DATABASE IF NOT EXISTS finance_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE finance_db; CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 密码建议存BCrypt值, real_name VARCHAR(50) COMMENT 姓名, role VARCHAR(20) NOT NULL DEFAULT USER COMMENT 角色ADMIN/USER, deleted TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除0未删1已删, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB COMMENT用户表; CREATE TABLE base_subject ( id BIGINT AUTO_INCREMENT PRIMARY KEY, subject_code VARCHAR(30) NOT NULL UNIQUE COMMENT 科目编码如 1001, subject_name VARCHAR(50) NOT NULL COMMENT 科目名称, subject_type VARCHAR(10) NOT NULL COMMENT 类型INCOME/EXPENSE, parent_id BIGINT DEFAULT NULL COMMENT 父科目ID支持二级分类, status TINYINT NOT NULL DEFAULT 1 COMMENT 启用状态1启用0停用, deleted TINYINT NOT NULL DEFAULT 0, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT科目表; CREATE TABLE fin_voucher ( id BIGINT AUTO_INCREMENT PRIMARY KEY, voucher_no VARCHAR(30) NOT NULL UNIQUE COMMENT 凭证号如 20250616-0001, voucher_date DATE NOT NULL COMMENT 业务日期, subject_id BIGINT NOT NULL COMMENT 关联科目ID, direction VARCHAR(10) NOT NULL COMMENT 方向INCOME/EXPENSE, amount DECIMAL(14,2) NOT NULL COMMENT 金额单位元, summary VARCHAR(200) COMMENT 摘要, pay_method VARCHAR(20) DEFAULT CASH COMMENT 支付方式CASH/BANK/ALIPAY/WECHAT, create_by BIGINT NOT NULL COMMENT 录入人ID, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted TINYINT NOT NULL DEFAULT 0, INDEX idx_voucher_date (voucher_date), INDEX idx_subject_id (subject_id) ) ENGINEInnoDB COMMENT凭证表; CREATE TABLE fin_budget ( id BIGINT AUTO_INCREMENT PRIMARY KEY, subject_id BIGINT NOT NULL COMMENT 科目ID, budget_month VARCHAR(7) NOT NULL COMMENT 预算月份如 2025-06, budget_amount DECIMAL(14,2) NOT NULL COMMENT 预算金额, used_amount DECIMAL(14,2) NOT NULL DEFAULT 0.00 COMMENT 已用金额, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_subject_month (subject_id, budget_month) ) ENGINEInnoDB COMMENT预算表;这段SQL里有几个字段选择值得说道。amount用DECIMAL(14,2)这是财务系统的基本要求float和double在金额累加时会产生精度漂移累计对账对不上查半天都查不出问题。deleted字段是逻辑删除标记数据不物理删除报表统计时才不会丢历史数据。fin_voucher表里单独存subject_id而不存科目名称是保留将来修改科目名称的余地报表展示时再去join科目表拿名称。预算表用subject_id加budget_month做联合唯一键保证同一个科目在同一个月只有一条预算记录录入时做一次INSERT ON DUPLICATE KEY UPDATE就能快速完成预算更新。所有外键关系没有在SQL里显式声明这是有意为之。课程设计阶段、演示阶段删除测试数据时外键约束会变成负担逻辑关系在Service层做校验就够了。很多人用Navicat建表时习惯顺手加外键后期改数据麻烦这是血泪经验。2.3 设计文档里必须画清楚的三张图拿到源码包后你会看到设计文档里有功能结构图、ER图和系统流程图。我建议你自己也补一遍这三张图因为答辩时老师会围绕它们发问。功能结构图就是把上面说的五个模块画成树状图。ER图的核心是实体之间的对应关系用户对凭证是一对多科目对凭证是一对多科目对预算是一对多。系统流程图要画出从“用户登录”到“录凭证”再到“生成报表”的完整链路。画图不是做形式主义的作业画的过程能把表关系在脑子里过一遍代码里join的时候不迷糊。很多同学代码写完了图表还是空的到答辩前熬夜补结果说出来的业务逻辑和代码对不上反而被问出破绽。3. 写核心代码凭证录入、收支汇总与分页查询3.1 后端分层controller、service、mapper这样拆项目结构直接决定代码好不好维护。我做这个题目时分包是固定的controller放接口入口service和service.impl放业务逻辑mapper放数据库访问接口entity放Java实体类config放配置类common放统一返回结果。Controller只做参数接收和结果封装不写任何SQL相关逻辑Service负责事务、校验和业务规则Mapper层只做数据读写。SpringBoot项目入口依赖比较简单pom.xml里核心是spring-boot-starter-web、mybatis-plus-boot-starter和mysql-connector-java三个依赖。MyBatis-Plus对单表CRUD非常省事BaseMapper内置了selectById、insert、updateById不用手写XML就能完成基础操作这对写课设题目的效率提升很明显。手写XML的场景只出现在多表关联统计。3.2 凭证录入接口Controller到数据库的完整链路凭证录入是财务系统的核心接口。前端传来科目ID、金额、方向、日期、摘要和支付方式后端要做三件事校验科目存在且启用、校验金额大于0、生成唯一凭证号。凭证号格式定为“日期加四位序号”比如20250616-0001在同一天内自增。这里我直接写一个录入凭证的ControllerService在同一个代码块后面说明。RestController RequestMapping(/api/voucher) public class VoucherController { Resource private VoucherService voucherService; PostMapping(/add) public Result add(RequestBody VoucherDTO dto) { // 参数基本校验交给JSR303注解这里只处理业务校验 if (dto.getAmount() null || dto.getAmount().compareTo(BigDecimal.ZERO) 0) { return Result.error(金额必须大于0); } Voucher voucher new Voucher(); voucher.setVoucherDate(dto.getVoucherDate()); voucher.setSubjectId(dto.getSubjectId()); voucher.setDirection(dto.getDirection()); voucher.setAmount(dto.getAmount()); voucher.setSummary(dto.getSummary()); voucher.setPayMethod(dto.getPayMethod() null ? CASH : dto.getPayMethod()); voucher.setCreateBy(CurrentUser.getId()); boolean saved voucherService.addVoucher(voucher); return saved ? Result.success(录入成功) : Result.error(录入失败请检查科目状态); } }VoucherDTO里有NotNull注解spring-boot-starter-validation一引入就能生效。amount用BigDecimal接收而非Double这是为了避免JSON反序列化时浮点丢失。接下来是Service实现类。凭证号生成和事务控制都放在这里任何一步失败整笔凭证都要回滚不能出现凭证存在但科目状态异常的脏数据。Service public class VoucherServiceImpl implements VoucherService { Resource private VoucherMapper voucherMapper; Resource private SubjectMapper subjectMapper; Override Transactional(rollbackFor Exception.class) public boolean addVoucher(Voucher voucher) { Subject subject subjectMapper.selectById(voucher.getSubjectId()); // 科目必须存在且启用这是财务数据一致性的第一道防线 if (subject null || subject.getStatus() ! 1 || subject.getDeleted() ! 0) { return false; } // 凭证号生成取当前日期序列再补足4位序号 String datePrefix new SimpleDateFormat(yyyyMMdd).format(new Date()); QueryWrapperVoucher wrapper new QueryWrapper(); wrapper.likeRight(voucher_no, datePrefix).orderByDesc(voucher_no).last(LIMIT 1); Voucher last voucherMapper.selectOne(wrapper); int seq 1; if (last ! null last.getVoucherNo() ! null) { String lastSeq last.getVoucherNo().substring(9); seq Integer.parseInt(lastSeq) 1; } String voucherNo datePrefix - String.format(%04d, seq); voucher.setVoucherNo(voucherNo); return voucherMapper.insert(voucher) 0; } }这里需要重点看两个参数。第一个是Transactional(rollbackFor Exception.class)rollbackFor指定了事务回滚的触发条件默认回滚只对RuntimeException生效SpringBoot里写事务时建议显式声明为Exception.class否则SQL异常可能不回滚。第二个是QueryWrapper里的likeRight它生成的条件是voucher_no LIKE 20250616%配合orderByDesc和last(LIMIT 1)取当天最后一条凭证来算序号。这样做在高并发下有极小的重复概率但课程设计和中小内部系统完全够用。有个坑藏在字段类型里。voucher_no是VARCHAR类型但由于前缀是日期按字典序排序和按日期排序结果一致所以直接orderByDesc不会出错。如果哪天凭证号前缀格式改了排序就会乱到时候再排查会很费时间。3.3 收支报表一条GROUP BY语句解决月度汇总报表统计模块最常用的查询是按月份、按科目类型汇总金额。下面是统计某个月收入支出的核心SQL我放在一个Mapper XML里。查询条件是月份字符串MyBatis通过参数传入并统计出该月各科目的发生额。select idsumByMonth resultTypemap SELECT DATE_FORMAT(v.voucher_date, %Y-%m) AS month, s.subject_type AS type, SUM(v.amount) AS total_amount, COUNT(*) AS voucher_count FROM fin_voucher v LEFT JOIN base_subject s ON v.subject_id s.id WHERE v.deleted 0 AND DATE_FORMAT(v.voucher_date, %Y-%m) #{month} GROUP BY DATE_FORMAT(v.voucher_date, %Y-%m), s.subject_type ORDER BY total_amount DESC /select这条SQL的价值在于把报表逻辑放在数据库里做而不是把数据查到Java内存里再用循环累加。SUM和GROUP BY在MySQL里是秒级完成的内存聚合则会有明显的卡顿数据量到上万条时差距就很清楚了。ORDER BY total_amount DESC让金额最大的科目排在前面做柱状图时直接拿这个结果集填数据。如果要做分页展示凭证列表我的习惯是用MyBatis-Plus内置的分页插件。在config包里加一个MybatisPlusInterceptor配置然后让Mapper接口的selectPage方法直接返回IPage对象。分页插件要单独配置不配置的话调用selectPage只会查出全部数据再内存分页数据量大了页面会很慢。参数方面推荐每页默认10条前端传pageNum和pageSize两个参数这两个值都要做上限校验pageSize最大设为100防止有人一次拉走全表数据。4. 本地部署MySQL配置到SpringBoot启动的一次完整复现4.1 环境三件套JDK、Maven和MySQL安装注意点开始部署前先确认环境。JDK建议装1.8或11版本过高或过低都可能导致mvn spring-boot:run编译失败。如果你用的SpringBoot是2.xJDK8完全够用。Maven用3.6以上版本配置阿里云镜像会让依赖下载快很多。MySQL用5.7或8.0版本两者的连接驱动和配置稍有差别下文会分别标注。MySQL安装这一步看起来简单实际上很多同学翻车都翻在“能打开命令行但连不上服务”上。Windows安装时注意安装向导里要选上“Configure MySQL Server as a Windows Service”这样开机后MySQL才会自动启动。如果安装时忘了勾选服务不会自动运行应用启动时会报连接失败。检查方式很简单在命令行执行mysql -uroot -p如果提示ERROR 2002 (HY000): Cant connect to local MySQL server through socket那就是服务没起来要去Windows服务管理器里手动启动。这句话里的报错信息是一个高频搜索词提前知道能少走很多弯路。4.2 修改application.yml驱动类与连接池参数的临界点源码包里自带的配置文件一般是application.yml。打开后核心配置在spring.datasource这一段。SpringBoot默认连接池是HikariCP不需要额外引入依赖参数已经按推荐值写好的话不需要改动但下面的示例里我标了常用的关键参数。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/finance_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 你的密码 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: classpath:mapper/*.xmldriver-class-name这里是MySQL 8的驱动类名。有的老项目里写的是com.mysql.jdbc.Driver在MySQL 8下会抛出ClassNotFoundException。如果你的MySQL是5.7建议把驱动类改成com.mysql.jdbc.Driver两者不要混用。url里的serverTimezoneAsia/Shanghai也建议保留MySQL 8默认时区是UTC不加这一项连接时会报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized启动直接就失败了。useSSLfalse是关闭SSL握手本地开发会明显加快连接速度。连接池参数里maximum-pool-size是最大连接数设20完全够一个课设项目用。设成几百反而会拖垮MySQL因为每个连接都要占用内存和文件描述符。minimum-idle是池中常驻的最小空闲连接数设5意味着启动后立刻建立5条连接第一次请求不会有明显的冷启动延迟。connection-timeout是等待连接的超时时间30秒比较合理。如果是在云服务器上部署把localhost改成服器IP改完重启才会生效。4.3 启动与验证从命令行到浏览器看到登录页先确认MySQL中已经导入了SQL脚本、建好了finance_db然后用命令行启动项目。IntelliJ IDEA里可以直接点SpringBoot启动类运行第一次跑建议用命令方式看日志更直观。cd finance-project mvn clean package -DskipTests java -jar target/finance-system-1.0.0.jar看到类似Tomcat started on port(s): 8080的日志就说明应用已经起来了。这时不要急着登录先用curl验证接口是否真的有响应。一个健康检查接口或者登录接口都可以我习惯直接请求登录接口看返回结构。如果返回JSON说明后端就没有问题问题大概率出在前端页面路径上。curl --location --request POST http://localhost:8080/api/user/login \ --header Content-Type: application/json \ --data-raw {username:admin,password:123456}登录成功会返回一个token拿到token后可以进一步调用凭证新增接口测试数据链路。这一步跑通整个系统的前后端数据链路就已经通畅了。接下来打开浏览器访问http://localhost:8080看到登录页就说明本地部署完成。整个过程如果顺利不超过十分钟如果中间报错按第五部分的排查清单逐条对。5. 部署和联调避坑5个高频翻车点排查记录5.1 Connection refusedMySQL没启动或者端口被改启动SpringBoot后控制台报Communications link failure或Connection refused第一反应不是检查代码而是先检查MySQL进程是否存在。Windows用户在服务管理器里看MySQL服务状态Linux用户在命令行执行systemctl status mysqld。还有一种情况是MySQL端口被改成3307但配置文件里写的还是3306报错信息里会带出详细端口号对不上就直接改配置。这类问题的特点是报错信息很吓人实际原因非常简单。5.2 Unknown database库没建或者连接连到了错误的库报错Unknown database finance_db多半是SQL脚本没执行成功或者执行到了别的实例上。比如本机装了多个MySQL服务Navicat连接的是3306但脚本是在3307上执行的。解决方法是先登录MySQL命令行执行SHOW DATABASES;看有没有finance_db库。没有就重新执行一次建库脚本有就检查url里的库名是否写错。这个排查过程用命令行比用图形化工具更可靠。5.3 中文乱码库表字符集还是latin1启动后往凭证表里插入中文摘要查询出来全是问号。原因一般是建库时没有显式指定字符集MySQL 5.7的默认字符集可能是latin1。解决方法是先改库的字符集再改表的字符集已经建好的表要单独ALTER TABLE CONVERT TO CHARACTER SET utf8mb4。如果代码和数据库都设置对了还有乱码看看url里有没有characterEncodingutf8没有就补上。这套操作下来乱码基本绝迹属于“一次配好永远不犯”的配置。5.4 Port 8080 already in use本机其他服务占了端口有的同学电脑上装了其他Java服务或IM工具默认8080被占用。启动日志会直接提示端口冲突解决办法有两种一是杀掉占用的进程二是在application.yml里改server.port。我建议直接改端口到8081因为杀进程可能误伤其他正在运行的工具。改完端口记得前端页面的请求地址也要同步改如果前端是npm run serve在localhost:8081启动而后端改到8081那就冲突了给前端的devServer配置里加一个target代理指向后端端口更稳妥。5.5 mybatis-plus的SQL日志乱码或打印不出调试期间SQL日志打印不出来排查时很没头绪。原因多半是mybatis-plus配置里的log-impl没有设置。配置文件里已经写了StdOutImpl会打印到控制台。如果日志没有内容先确认查询有没有真的执行再确认日志级别是不是被全局日志配置覆盖。还有一种玄学情况是IDEA的控制台编码被设成了GBKSQL里的中文条件显示乱码修改IDEA的file encoding为UTF-8后重启就能解决。这五条是我从这个题目的源码包、部署说明和常见提问里整理出来的高发问题。出现报错时优先看第1条和第3条因为它们覆盖了超过一半的启动失败场景。其余的情况用排除法验证也很快。6. 投入开发前的最后一个把关对账测试财务系统的核心指标只有一个账要平。这里的平指的是本月收入总发生额减去本月支出总发生额等于财务余额变动的绝对值。很多同学部署完界面跑通就觉得万事大吉实际上只要数据录得不一致报表就是错的这个方向再做下去没有任何意义。我一般在交差前都会做一道对账操作。对账测试的输入是手工录入几笔测试数据例如收入三笔共5000元、支出两笔共2800元然后执行下面这段SQL看汇总结果是否等于2200元。SELECT i.month, i.total_income, e.total_expense, i.total_income - e.total_expense AS balance FROM (SELECT DATE_FORMAT(voucher_date, %Y-%m) AS month, SUM(amount) AS total_income FROM fin_voucher WHERE direction INCOME AND deleted 0 GROUP BY DATE_FORMAT(voucher_date, %Y-%m)) i LEFT JOIN (SELECT DATE_FORMAT(voucher_date, %Y-%m) AS month, SUM(amount) AS total_expense FROM fin_voucher WHERE direction EXPENSE AND deleted 0 GROUP BY DATE_FORMAT(voucher_date, %Y-%m)) e ON i.month e.month;如果这个结果和手工预期一致系统的核心数据链路就是可信的。我个人习惯还会额外检查一个点凭证表里是否存在方向字段与科目类型不匹配的记录比如科目类型是EXPENSE但direction写成了INCOME。这类脏数据在报表统计出来后会让人百思不解查半天也找不到原因。可以多做一步SQL查一下SELECT FROM fin_voucher v JOIN base_subject s ON v.subject_ids.id WHERE v.direction ! s.subject_type有结果就是原始录入时校验没做好趁早处理。对账测试通过后真正的投产信心才竖得起来。财务管理系统这个方向的护城河不在于界面多好看而在于数据链路的严谨性验证链路正确再去做预算超出提醒、导出Excel、按年同比统计这些进阶功能设计的底气完全不一样。这也是我每次拿到一个SpringBootMySQL财务系统源码后第一件事就是先把账算平的原因。希望这个完整链条能帮到你。本文还有配套的精品资源点击获取
返回列表