ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于SpringBoot的家庭财务系统实战:数据库设计、核心接口与部署

基于SpringBoot的家庭财务系统实战:数据库设计、核心接口与部署 简介这套基于SpringBoot构建的家庭财务管理系统是一份面向计算机专业毕业设计的完整项目包适合需要论文源码数据库全流程参考的本科生。项目覆盖前后端代码、SQL脚本、E-R图与数据流图并基于IDEA、MySQL、Java 1.8和Tomcat 9.0搭建运行环境界面采用常见后台管理风格便于二次开发与文档对照。压缩包共807个文件约22.64MB其中包含300个gif操作演示图、108个js脚本、89个xml配置、56个html页面、51个jar依赖、30个java源文件及1份docx论文文档等gif图可用于展示系统页面流转js/css/字体文件支撑前端界面xml/yml与jar则对应后端工程结构整体目录清晰便于按模块检索。目前已有1186人学习下载适合毕业设计选题、项目复现或SpringBoot实战入门者参考能帮助快速理解家庭财务场景下的收支管理、账单记录等核心功能实现。1. 基于SpringBoot的家庭财务管理系统先搞懂你手上这份资源包能干什么把“基于SpringBoot的家庭财务管理系统”这几个词拆开它其实就是一条很常见的后端技术路线用SpringBoot框架做服务端MySQL存账目流水再配一个前端页面管收支录入、分类统计和月度报表。你拿到的.rar大概率是一个毕业设计或课程设计的完整资源包里面拖着一个word说明文档和一套带源码的工程。问题在于很多人解压之后第一步就卡住了——不知道这个项目该用什么JDK跑、配置文件里哪些要改、一启动就报错去哪里查。这篇文章就是顺着这个标题讲实操方案。我会从数据模型设计、项目跑通、核心功能落地到部署踩坑把它当成一个你要亲手从零复现并改造的项目来写。适合两类人一是拿这个题目做毕设、需要真正理解每张表和每个接口的学生二是想给家里搭一套自用记账工具、不愿用第三方App的开发者。今天这套思路不用微服务不搞分布式一台普通电脑就能跑。2. 家庭财务的底层模型从流水表到预算表的字段设计2.1 技术选型为什么这种项目最适合 SpringBoot MyBatis-Plus家庭财务系统的业务本质是“重CRUD、轻逻辑”新增一笔支出、按月分组查流水、统计年度收支。这种场景下用SpringBoot最合适的原因首先是它把Spring的自动装配做到了极致。你只需要引入spring-boot-starter-web和spring-boot-starter-jdbcDataSource、事务管理器这些就都自动配置好了不用再写一堆XML。这正是SpringBoot框架能流行起来的原因也是毕设项目愿意选它的理由。持久层我一般直接上MyBatis-Plus而不是原生MyBatis。理由有三个第一单表CRUD不用手写SQL代码生成器能直接把entity、mapper、service全部生成出来第二分页插件是官方内置的配一个拦截器就能用不用自己封装PageHelper第三字段填充create_time自动填值和逻辑删除这类家庭财务里高频的小需求它都有现成注解。如果你拿到手的项目是纯MyBatis手写SQL的建议你花半天时间迁移到MyBatis-Plus后面改需求会轻松很多。前端层面这类包常见的有两派Vue前后端分离和Thymeleaf服务端渲染。如果资源包里是前后端分离记得把Vue打包出的dist目录放进src/main/resources/static同时处理好跨域如果是Thymeleaf那你只改后端模板就够了。我自己的习惯是后端为主前端只要能展示数据就行所以后面讲接口对接时以返回JSON为准前端不绑定某种框架。2.2 建表SQL账户、分类、流水、预算的约束怎么定家庭财务最少需要四张表用户表、账户表、分类表和流水表预算表可以后补。先看用户和账户这两张基础表。CREATE TABLE user ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(255) NOT NULL COMMENT BCrypt密文, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE account ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL COMMENT 所属用户, name VARCHAR(30) NOT NULL COMMENT 账户名如工资卡/支付宝, balance DECIMAL(10, 2) NOT NULL DEFAULT 0.00 COMMENT 当前余额, KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT账户表;这里有两个细节值得注意。一是密码字段长度给255因为BCrypt加密后的字符串是60位但旧项目可能用MD5只给32位改成BCrypt后长度不够会报错。二是账户表没有外键只建普通索引idx_user——家庭财务系统数据量很小外键在删除分类时会带来一堆级联麻烦用应用层逻辑控制更顺手。流水表是整个系统的核心字段设计直接影响统计SQL好不好写。CREATE TABLE transaction ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, account_id BIGINT NOT NULL COMMENT 发生账户, category_id BIGINT NOT NULL COMMENT 分类ID, type TINYINT NOT NULL DEFAULT 0 COMMENT 0-支出 1-收入, amount DECIMAL(10, 2) NOT NULL COMMENT 金额单位元, trade_time DATETIME NOT NULL COMMENT 交易发生时间, remark VARCHAR(100) DEFAULT NULL COMMENT 备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_time (user_id, trade_time), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT流水表;金额字段必须用DECIMAL(10,2)而不是FLOAT或DOUBLE这是新手最容易翻车的地方。二进制浮点数存0.1会有精度误差月度汇总时差几分钱非常难排查。trade_time和create_time是两个概念前者是这笔钱实际发生的时间用户可能补录昨天的账后者是录入系统的时间用于审计。统计报表一律按trade_time否则补录的账会出现在错误月份。预算表的写法是记月度总额而不是每次消费记一笔扣减明细。CREATE TABLE budget ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, month VARCHAR(7) NOT NULL COMMENT 月份格式2026-05, amount DECIMAL(10, 2) NOT NULL COMMENT 当月预算总额, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_month (user_id, month) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT月度预算表;2.3 yml里的必调参数日期格式、金额精度和分页插件项目能不能启动先看application.yml写得对不对。下面是家庭财务系统最常用的一套配置你在解压后的包里有可能会看到不一样的名字application.properties或bootstrap.yml但核心内容大同小异。server: port: ${PORT:8080} spring: datasource: url: jdbc:mysql://localhost:3306/family_finance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root123 hikari: maximum-pool-size: 10 minimum-idle: 5 redis: host: localhost port: 6379 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto这段配置里有三个参数需要重点说明。第一server.port: ${PORT:8080}是让端口可被环境变量覆盖的小技巧部署到服务器时用PORT9090 java -jar xxx.jar就能换端口不用改代码打包。第二serverTimezoneAsia/Shanghai必须在数据库连接串里MySQL 8默认时区是UTC少了这个参数查出来的日期会慢8小时跨月统计必错账。第三maximum-pool-size: 10是HikariCP连接池最大连接数家庭自用3到5个就够给10是为了对付开发时多个窗口同时连库。MyBatis-Plus的分页插件需要单独配一个配置类很多资源包会漏掉这一步导致分页查询不生效、接口返回全量数据。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这个拦截器是官方推荐的方式DbType.MYSQL告诉插件按MySQL方言拼LIMIT用别的数据库就要换成对应枚举。家庭财务系统里流水表会越攒越多分页插件必须配好不然首页接口三个月后就卡。3. 把这个SpringBoot项目跑起来解压、配置、启动的完整路径3.1 导入前核对JDK与Maven版本拿到.rar后别急着双击导入IDEA先检查本机环境。SpringBoot 2.7.x配JDK 8或11都能跑SpringBoot 3.x强制要求JDK 17。如果包里pom.xml写的是spring-boot-starter-parent版本2.7.18那你用JDK 8最稳用17启动一般也没问题但用21就可能遇到某些老版本依赖的反射访问报错。在命令行确认版本java -version mvn -v我遇到过最典型的场景是用户装的是JDK 17导入一个基于SpringBoot 2.3.4的旧项目启动直接报java.lang.reflect.InaccessibleObjectException。原因不是JDK坏是JDK 9之后模块化系统封禁了内部包反射旧版本CGLIB代理访问不到。解决方式很简单要么降级到JDK 8要么给JVM加--add-opens java.base/java.langALL-UNNAMED参数我建议别折腾直接装JDK 8最省心。IDEA导入时选“Open or Import”找到解压目录里的pom.xml以Maven项目方式打开。让IDEA自己下载依赖同时检查IDEA里Maven设置是否指向本机安装的Maven而不是IDEA内置的那个。内置Maven在Windows下经常因为路径带空格下载依赖失败。3.2 改application.yml里的数据库与Redis连接资源包里的配置多半是作者本机环境数据库密码、Redis地址一定要改否则启动报连接拒绝。先建库注意字符集要跟表设计保持一致。mysql -uroot -p CREATE DATABASE family_finance DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后修改application.yml里的数据源账号密码。如果你的MySQL和Redis都装在本地host保持localhost就行如果Redis没装而项目又强依赖Redis做缓存你有两个选择第一去下载一个Windows版Rediscmd里执行redis-server.exe redis.windows.conf第二把代码里所有Cacheable和RedisTemplate的调用注释掉直接删掉spring-boot-starter-data-redis依赖这会让登录Session失效不推荐。我建议直接装Redis。家庭财务系统虽然切入点小但SpringBoot整合Redis是高频需求预算校验接口如果每次实时查库高峰期会拖慢统计报表。把分类列表和月度汇总结果缓存到Rediskey设计成family:finance:summary:{userId}:{month}过期时间设30分钟体验会好很多。3.3 启动验证与Swagger冒烟测试配置改完后先打包再启动这样能看到完整的编译错误。mvn clean package -DskipTests java -jar target/family-finance-0.0.1-SNAPSHOT.jar如果启动日志里看到Tomcat started on port(s): 8080说明应用内核已经起来了。接着打开浏览器访问两个地址验证Swagger接口文档地址和第一个RestController接口。如果项目用的是springdoc访问/swagger-ui/index.html如果是老版本springfox3.0以下访问/swagger-ui.html。我这里会顺手做一次冒烟测试确保数据库连接和MyBatis映射都正常curl -X GET http://localhost:8080/api/category/list?userId1 -H Authorization: Bearer test-token返回[]或一条分类数据都算正常如果返回500去IDEA控制台看异常栈。最常见的是Table family_finance.transaction doesnt exist——建表SQL没执行或者数据库连错库了。还有一个高频问题启动时端口被占用日志报Web server failed to start. Port 8080 was already in useWindows下用netstat -ano | findstr 8080找到PID然后taskkill /F /PID 进程号。4. 记账、预算与报表三个核心功能怎么在Service层落地4.1 记账接口与分类统计事务和索引怎么用家庭财务系统的核心操作是记一笔账。这个接口不能只插一条流水它还要同时更新账户余额。两步操作必须在一个事务里否则流水写成功了余额没扣月底对账必然不平。Service RequiredArgsConstructor public class TransactionServiceImpl implements TransactionService { private final TransactionMapper transactionMapper; private final AccountMapper accountMapper; Override Transactional(rollbackFor Exception.class) public void createTransaction(TransactionDTO dto) { BigDecimal amount dto.getAmount(); if (amount.compareTo(BigDecimal.ZERO) 0) { throw new BizException(金额必须大于0); } Transaction transaction new Transaction(); transaction.setUserId(dto.getUserId()); transaction.setAccountId(dto.getAccountId()); transaction.setCategoryId(dto.getCategoryId()); // type决定资金方向1收入加余额0支出减余额 transaction.setType(dto.getType()); transaction.setAmount(amount); transaction.setTradeTime(dto.getTradeTime()); transaction.setRemark(dto.getRemark()); transactionMapper.insert(transaction); Account account accountMapper.selectById(dto.getAccountId()); BigDecimal newBalance dto.getType() 1 ? account.getBalance().add(amount) : account.getBalance().subtract(amount); account.setBalance(newBalance); accountMapper.updateById(account); } }这里有两个细节容易被忽略。第一Transactional(rollbackFor Exception.class)不要写成默认的Transactional因为Spring默认只回滚RuntimeException和Error如果你抛的是自定义Checked异常事务不会回滚流水和余额就分叉了。第二金额判断用compareTo而不是这是BigDecimal比较的唯一正确姿势用比较两个对象地址而不是数值。分类统计是报表的基础按月查每个分类花了多少钱SQL用GROUP BY聚合。SELECT category_id, SUM(amount) AS total FROM transaction WHERE user_id #{userId} AND type 0 AND trade_time BETWEEN #{startTime} AND #{endTime} GROUP BY category_id ORDER BY total DESC;这条SQL能走idx_user_time这个联合索引。注意BETWEEN的边界如果startTime是当月1日0点endTime是下月1日0点这里查出来是包含边界且排除下月第一天的比直接MONTH(trade_time)5这种写法高效得多。后者会让索引失效数据量上到十万条后统计秒变全表扫。4.2 月度预算控制扣减、回退与超限判断预算模块的常见做法是月度预算表存一个总金额每次新增支出时把这个月已花金额加上当前金额跟预算总额比较超出就抛异常或弹提示。这个逻辑放在SpringBoot里实现并不复杂关键点在于查询与判断的写法。public void checkBudget(Long userId, String month, BigDecimal newAmount) { Budget budget budgetMapper.selectByUserAndMonth(userId, month); if (budget null) { return; // 未设置预算则不做限制 } BigDecimal monthSpent transactionMapper.sumExpenseByMonth(userId, month); BigDecimal willSpend monthSpent.add(newAmount); if (willSpend.compareTo(budget.getAmount()) 0) { throw new BudgetExceededException( 本月预算 budget.getAmount() 元当前已支出 monthSpent 元无法继续记录); } }sumExpenseByMonth这一句对应前面那条聚合SQL只不过加了type 0条件。这里要特别提醒如果同一笔流水在“新增预算校验”和“事务提交”之间发生了并发操作统计出来的已花金额会偏低。家庭自用场景并发量极低这个问题可以容忍但如果你打算部署给多人用就得给budget表加乐观锁版本字段或者在写入时用SELECT FOR UPDATE锁住当月预算行。回退操作同样存在。用户删掉一笔支出时除了删除流水还要把账户余额加回来。这个过程同样需要事务逻辑上等于记账的逆向操作。很多资源包会漏掉这个导致删一笔账后钱包余额对不上这是测试时最容易暴露的Bug。4.3 报表SQL按天聚合收支的写法与ECharts对接月度趋势图是家庭财务系统展示用的核心功能折线图的每个点代表某一天的支出总和。一条SQL就能把数据喂给前端。SELECT DATE_FORMAT(trade_time, %Y-%m-%d) AS day, SUM(amount) AS total FROM transaction WHERE user_id #{userId} AND type 0 AND trade_time BETWEEN #{startTime} AND #{endTime} GROUP BY day ORDER BY day;后端拿到这条SQL的结果后转成ListMapString, Object返回。前端用ECharts时xAxis取所有dayseries取所有total一个柱状图就出来了。如果要做收入支出双柱对比就把收入统计也查出来用type 1另跑一遍两个series并排摆。这里有一个性能权衡如果你按小时甚至按分钟统计就不要用DATE_FORMAT去格式化所有记录而是直接按天分组把耗时放在应用层的Map处理上。家庭财务一年最多几千条流水其实根本到不了需要优化的程度。但为了面试或答辩时能讲清楚“为什么这么写”你要能回答DATE_FORMAT会让索引失效吗答案是会让trade_time的索引派不上用场所以必须用trade_time BETWEEN先圈定范围再对范围内的数据格式化。5. SpringBoot家庭财务避坑指南版本、序列化与时区的五个教训5.1 SpringBoot版本太高导致依赖叛变javax与jakarta的冲突现象拿到一个基于SpringBoot 2.x的项目手痒把pom里的parent版本改成3.0.0一编译报错几十个全是程序包javax.annotation不存在。原因SpringBoot 3.0把JavaEE包名从javax.*换成了jakarta.*所有用javax.annotation.Resource、javax.validation.*的旧代码全部编译失败。如果你的项目里还引了mybatis-spring-boot-starter的2.x版本它内部还在用javax跟SpringBoot 3根本不兼容启动会直接报Bean创建异常。解决要么老老实实用SpringBoot 2.7.18这是2.x的最终版本JDK 8和11都兼容社区资料最全要么全面升级到SpringBoot 3.x同时把MyBatis-Plus升级到3.5.4以上把代码里所有javax改成jakarta。对家庭财务这种项目我强烈建议留在2.7.18不值得为了“新版本”去改一堆命名空间。5.2 BigDecimal被Jackson序列化成科学计数法现象接口返回的金额字段变成1.0E2前端拿到后显示成100倒是正常但导出Excel时变成1.00E02数据没丢但看着就是不对。原因Jackson默认会把数值很大的BigDecimal转成科学计数法而DECIMAL(10,2)的金额拆开后就容易触发这个逻辑。真正的问题是配置里没有指定金额序列化格式。解决在application.yml里配好Jackson或者在实体类字段上加注解。JsonFormat(shape JsonFormat.Shape.STRING, pattern 0.00) private BigDecimal amount;我一般全局配置spring.jackson.generator.write-bigdecimal-as-plaintrue这样所有BigDecimal都以普通十进制字符串输出一劳永逸。5.3 统计报表跨月错账时区偏移8小时的痕迹现象每月1号凌晨跑的月度统计任务总把上个月最后一天晚上的支出算到这个月而且只在trade_time精确到晚上8点之后才触发。原因MySQL连接串里没带serverTimezoneAsia/ShanghaiJDBC驱动用了默认UTC时区。应用写入的trade_time是北京时间数据库会话读出时按UTC解析日期直接少8小时。如果你写入的是DATETIME它不存时区读取时会用会话时区换算2026-06-01 01:00:00在UTC会话里读成2026-05-31 17:00:00日界线一跨月份就错了。解决在数据源URL里加serverTimezoneAsia/Shanghai同时确认应用和数据库所在主机系统时区一致。容器部署时进入容器执行date看一眼如果是UTC就在Dockerfile里加一行ENV TZAsia/Shanghai这是docker部署springboot项目最常见的默认坑。5.4 Docker部署MySQL连接超时localhost指向了谁现象打包成镜像后docker run -p 8080:8080启动日志一直报Communications link failure用IDEA连本机MySQL正常但容器里就是连不上。原因容器内的localhost是容器自己不是宿主机。你的MySQL跑在宿主机应用在容器里连localhost:3306自然被拒。很多人第一次用Docker都会在这里卡住属于入门级的部署认知问题。解决路径有两条。启动容器时加--network host让容器直通宿主机网络栈这种方式最简单、性能最好但Linux下不支持Docker Desktop的Mac和Windows或者把配置里的host改成宿主机局域网IPjdbc:mysql://192.168.1.100:3306/family_finance同时给MySQL的root账号授权允许该IP访问记得授权时不带localhost限制。docker run -d -p 8080:8080 --network host family-finance:1.0.0需要提醒的是这里跟容器编排的复杂性无关家庭自用就选--network host别去碰docker-compose网络别名那套除非你想连MySQL也一起容器化。5.5 Excel导入时中文乱码InputStream一次性读取的玄学现象Excel模板导入后中文备注全部变成“???”或者用POI读出来是乱码。原因很多人习惯直接new InputStreamReader(file.getInputStream())没指定字符集用了默认GBK去解码UTF-8编码的Excel内容。另一个更隐蔽的原因是POI读取Excel用WorkbookFactory.create(inputStream)InputStream被打开后没有正确关闭资源泄漏导致部分内容读不完整。解决如果是EasyExcel指定字符集并释放流。// 不要直接传MultipartFile的InputStream先转字节数组再解析 byte[] bytes file.getBytes(); ExcelReader reader EasyExcel.read(new ByteArrayInputStream(bytes)) .head(TradeExcelDTO.class) .build();把InputStream先转换成byte[]再做后续处理能避免多次读取时流已经关闭的问题这是我在实际项目里吃过亏后总结的习惯。6. 让这套系统真正能用定时对账与自动备份家庭财务系统跑起来只是开始真正能长期用的关键不是功能多花哨而是数据不丢、账目不错。我一般会给这套系统加两个增强定时对账和自动备份。对账用SpringBoot的Scheduled注解就能做在启动类上加EnableScheduling然后写一个每天晚上凌晨2点执行的Job。逻辑是遍历每个账户把账户表的余额跟该账户下所有流水的收支差做减法两边不一致就报警。别小看这个检查用户手工删数据、程序异常改余额都是靠它兜底。Scheduled(cron 0 0 2 * * ?) public void dailyCheck() { ListAccount accounts accountMapper.selectList(null); for (Account account : accounts) { BigDecimal flowSum transactionMapper.sumIncome(account.getId()) .subtract(transactionMapper.sumExpense(account.getId())); if (flowSum.compareTo(account.getBalance()) ! 0) { log.warn(账户{}对账不平: 流水差{}, 余额{}, account.getName(), flowSum, account.getBalance()); } } }备份比对账更简单粗暴——直接调mysqldump命令把备份文件放到指定目录然后清理30天前的旧包。这个操作放进Scheduled里完全可行注意ProcessBuilder执行外部命令时要把命令数组化传参不要拼成字符串交给shell执行否则文件路径含空格会拆参数。mysqldump -uroot -p#{dbPassword} family_finance /backup/family_$(date %Y%m%d).sql我现在给家里部署的这套财务系统就靠这两个定时任务续命。运行两年多唯一一次出问题是硬盘空间被日志占满备份任务静默失败后来加了磁盘空间监控才彻底放心。这件事给我的教训是功能可以朴素但备份不能偷懒。希望这些踩过的坑能帮你在做这个SpringBoot家庭财务项目时少走弯路。本文还有配套的精品资源点击获取
返回列表