
简介面向毕业设计的一套个人理财系统完整资源基于 SpringBootVueMySQL 构建包含源码、数据库、毕业论文和视频演示。系统分为管理员与普通用户两类角色管理员可维护用户信息、管理账单类型用户可录入收入与支出账单、制定理财计划、查看统计分析报表并能使用备忘录记录还款日等重要事项基本覆盖个人财务管理核心场景。压缩包共 407 个文件约 18.88MB主要包含 98 个 Java 后端源码文件、40 个 Vue 前端组件文件、161 个 SVG 图标以及 SQL 数据库脚本、Maven 与 bat 一键构建运行脚本并配有 HTML、CSS、JS 等静态资源目录按前后端分层组织便于定位和改造。已有 149 人学习/下载适合需要快速搭建毕设项目、理解前后端分离实践或进行二次开发的计算机专业学生。配套的毕业论文和视频演示可帮助梳理系统架构、接口设计、数据库表结构及答辩演示要点为课程设计与毕业设计提供直接参考。1. 用 SpringBoot Vue 做毕业设计个人理财系统到底该怎么做毕业设计选“个人理财系统”技术栈定为 SpringBoot Vue MySQL几乎是目前最稳妥且演示效果最好的组合。这套方案的价值在于后端用 SpringBoot 把接口、权限、业务逻辑统一收口前端用 Vue 把数据渲染和交互独立出来MySQL 负责把所有流水账、预算、分类数据落库。整个体系不依赖分布式中间件一台电脑就能跑通但覆盖了后端开发、前端开发、数据库设计、接口联调四条完整链路论文材料也容易整理。很多同学拿到的源码包里有“源码数据库毕业论文视频演示”但如果你只会按着视频点下一步答辩时面对“你讲讲表结构怎么设计的”“接口异常怎么排查”这类问题容易答不上来。更好的做法是自己把这个项目拆开弄清楚每张表、每个接口、每条请求为什么这样写再把别人的源码当作对照参考。这样毕业论文有内容可写系统演示也不会一问三不知。本文围绕“怎么做一套能过答辩、可扩展、代码干净的个人理财系统”展开从建表、后端接口、前端页面到联调验证全部落到可执行的细节上。2. SpringBoot Vue MySQL 的技术分工与表结构设计2.1 为什么是这套组合各层职责怎么划分个人理财系统本质上是一个“单用户或多用户的记账应用”核心操作是增删改查流水记录辅助操作是统计、预算、图表。这种业务用 SpringBoot Vue MySQL 做工作量适中且每一层都有明确的技术考点。SpringBoot 负责暴露 RESTful API处理登录鉴权、参数校验、事务控制和业务逻辑。比如“新增一笔支出”要先判断金额是否合法、分类是否存在、账户余额是否被扣。Vue配合 Vue Router、Axios、ECharts负责页面渲染、表单校验、路由跳转和图表展示。用户在页面上填金额、选分类前端把数据组装成 JSON 发给后端。MySQL 负责持久化存储。用户表、流水表、分类表、预算表都落在这层后端通过 MyBatis 或 MyBatis-Plus 操作数据。这样的前后端分离结构最大的好处是接口清晰前端拿到的永远是 JSON后端不关心页面长什么样。答辩时既能讲“松耦合”又能讲“接口设计规范”比纯 JSP 项目有更多可以展开的内容。2.2 个人理财系统需求拆解与模块划分先把功能模块列清楚再决定表结构。一个基础但完整可演示的个人理财系统至少要包含以下模块模块功能点数据库表用户模块注册、登录、个人信息修改t_user账单模块新增收支、编辑、删除、分页查询t_transaction分类模块收入/支出分类管理t_category预算模块每月预算设置、预算使用进度提醒t_budget统计模块按日期/分类汇总收支、图表展示基于 t_transaction 聚合不建议在毕业设计里堆砌“股票模拟”“账单导入”等功能功能越多越容易暴露边界 bug。把上面的五个模块做扎实已经足够支撑论文里的“需求分析”和“系统设计”两章。2.3 建表脚本字段类型、索引、外键怎么设表设计是整个数据库课程设计环节最容易丢分的点。每张表都要回答三个问题主键是什么哪些字段经常被查询数据一致性靠什么保证DROP DATABASE IF EXISTS finance_db; CREATE DATABASE finance_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE finance_db; CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(255) NOT NULL COMMENT 密码BCrypt加密, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB COMMENT 用户表; CREATE TABLE t_category ( id INT NOT NULL AUTO_INCREMENT COMMENT 分类ID, user_id INT NOT NULL COMMENT 所属用户, name VARCHAR(30) NOT NULL COMMENT 分类名称, type TINYINT NOT NULL COMMENT 类型1收入 2支出, sort_order INT DEFAULT 0 COMMENT 排序值, PRIMARY KEY (id), KEY idx_user (user_id) ) ENGINEInnoDB COMMENT 收支分类表; CREATE TABLE t_transaction ( id INT NOT NULL AUTO_INCREMENT COMMENT 流水ID, user_id INT NOT NULL COMMENT 用户ID, category_id INT NOT NULL COMMENT 分类ID, amount DECIMAL(10,2) NOT NULL COMMENT 金额, type TINYINT NOT NULL COMMENT 1收入 2支出, note VARCHAR(255) DEFAULT NULL COMMENT 备注, trans_date DATE NOT NULL COMMENT 交易日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_user_date (user_id, trans_date), KEY idx_category (category_id) ) ENGINEInnoDB COMMENT 收支流水表; CREATE TABLE t_budget ( id INT NOT NULL AUTO_INCREMENT COMMENT 预算ID, user_id INT NOT NULL COMMENT 用户ID, category_id INT NOT NULL COMMENT 分类ID, month VARCHAR(7) NOT NULL COMMENT 月份如2025-03, amount DECIMAL(10,2) NOT NULL COMMENT 预算金额, PRIMARY KEY (id), UNIQUE KEY uk_user_cat_month (user_id, category_id, month) ) ENGINEInnoDB COMMENT 月度预算表;这里选择了 InnoDB 引擎、utf8mb4 字符集、联合索引。三个细节值得注意金额用 DECIMAL(10,2) 而不是 FLOAT/DOUBLE避免浮点数精度误差答辩时提到这个点能明显体现出你懂数据精度问题。trans_date 使用 DATE 类型而不是 DATETIME因为统计查询按“天/月”做分组日期类型更干净。外键没有在表上显式声明而是在应用层维护一致性。毕业设计论文里可以这样写“考虑到后续扩展和删除分类时的灵活性选择在服务层通过事务控制关联数据的完整性”这句话就是答辩时的加分项。3. SpringBoot 后端从项目初始化到核心接口落地3.1 用 IDEA 创建 SpringBoot 项目依赖版本与配置后端开发推荐使用 IntelliJ IDEA。创建项目时选择 Spring Initializr注意 SpringBoot 版本不要选太高的预览版使用 2.7.x 或 3.x 的稳定版即可。搭建时按以下依赖选择依赖用途备注Spring Web提供 RESTController 和 HTTP 能力必选MySQL DriverJava 连接 MySQL版本跟随 SpringBoot 依赖管理MyBatis-Plus简化单表 CRUD支持条件构造器毕业设计里比 JPA 更直观Lombok减少 getter/setter 样板代码需要安装 IDEA Lombok 插件JWT 相关库生成和校验登录令牌java-jwt 或 hutool 均可在 application.yml 里配置数据源和 MyBatis-Plusserver: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/finance_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 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.StdOutImplURL 参数必须带上 serverTimezoneAsia/Shanghai否则连接 MySQL 8.x 会报时区错误。map-underscore-to-camel-case 开启后数据库字段 trans_date 能自动映射到实体类属性 transDate省掉大量 ResultMap 配置。3.2 实体类、Mapper、Service、Controller 四层编写顺序后端代码按“实体 → Mapper → Service → Controller”的顺序写。以流水表为例先用 Lombok 定义实体类Data TableName(t_transaction) public class Transaction { TableId(type IdType.AUTO) private Integer id; private Integer userId; private Integer categoryId; private BigDecimal amount; private Integer type; private String note; private LocalDate transDate; private LocalDateTime createTime; }Mapper 层直接用 MyBatis-Plus 的 BaseMapperMapper public interface TransactionMapper extends BaseMapperTransaction { }Service 层写业务逻辑比如新增支出时要校验分类是否存在同时回写预算使用记录。下面这段代码演示了核心逻辑Service public class TransactionServiceImpl implements TransactionService { Resource private TransactionMapper transactionMapper; Resource private CategoryService categoryService; Override Transactional(rollbackFor Exception.class) public Transaction addTransaction(TransactionDTO dto, Integer userId) { Category category categoryService.getById(dto.getCategoryId()); if (category null || !category.getUserId().equals(userId)) { throw new BizException(分类不存在); } Transaction tx new Transaction(); tx.setUserId(userId); tx.setCategoryId(dto.getCategoryId()); tx.setAmount(dto.getAmount()); tx.setType(dto.getType()); tx.setNote(dto.getNote()); tx.setTransDate(dto.getTransDate()); transactionMapper.insert(tx); return tx; } }Controller 层负责参数接收和结果封装RestController RequestMapping(/api/transaction) public class TransactionController { Resource private TransactionService transactionService; PostMapping(/add) public ResultTransaction add(RequestBody TransactionDTO dto, HttpServletRequest request) { Integer userId (Integer) request.getAttribute(userId); return Result.success(transactionService.addTransaction(dto, userId)); } GetMapping(/list) public ResultPageTransactionVO list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer type, RequestParam(required false) String startDate, RequestParam(required false) String endDate, HttpServletRequest request) { Integer userId (Integer) request.getAttribute(userId); return Result.success(transactionService.pageQuery(userId, page, size, type, startDate, endDate)); } }Controller 层不要写业务逻辑只做参数接收、调用 service、返回统一结果。统一返回类 Result 包含 code、message、data 三个字段前端 Axios 拦截器统一判断 code 是否为 200这样前后端协作时心智负担最小。3.3 登录鉴权JWT 令牌与路由拦截器个人理财系统的数据是用户私有的所以登录鉴权是答辩必考题。常见方案是 JWT用户登录成功后后端签发一个令牌前端把令牌存在 localStorage后续请求在请求头携带 Authorization 字段。Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { throw new BizException(401, 未登录); } Claims claims JwtUtil.parseToken(token.replace(Bearer , )); request.setAttribute(userId, claims.get(userId, Integer.class)); return true; } }注册拦截器时放行登录和注册接口其他接口全部拦截Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/user/login, /api/user/register); } }这个逻辑展开讲能覆盖“SpringBoot 拦截器”“JWT 原理”“前后端分离下怎么控制访问权限”三个论文考点。4. Vue 前端工程搭建、请求封装与核心页面实现4.1 用 Vue 创建前端项目依赖安装与代理配置前端工程建议使用 Vue 3 Vite开发效率高。创建完项目后要安装 vue-router、axios、echarts 三个核心依赖npm create vuelatest finance-web cd finance-web npm install npm install axios vue-router4 echarts npm run devVue 开发时访问后端接口会遇到跨域问题。最常见的解决办法是在 Vite 配置里加代理让前端请求/api时转发到http://localhost:8080// vite.config.js export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } });注意 target 指向的是 SpringBoot 启动的端口不是前端端口。changeOrigin 设置为 true 时后端拿到请求后会认为来自同源地址避免 CORS 报错。这是联调时最常见的坑配置不对时页面请求全是红色报错但 SpringBoot 后台日志却显示有请求进来。4.2 注册登录页面与 Axios 请求封装前端把 axios 封装成一个统一模块方便过滤 token 和统一错误提示// src/api/request.js import axios from axios; import router from /router; 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; }); request.interceptors.response.use( response { const res response.data; if (res.code ! 200) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res.data; }, error { if (error.response error.response.status 401) { localStorage.removeItem(token); router.push(/login); } ElMessage.error(error.message || 网络异常); return Promise.reject(error); } ); export default request;登录页面调用后端接口时把用户名和密码通过 POST 请求发送const login async () { const data await request.post(/user/login, { username: form.value.username, password: form.value.password }); localStorage.setItem(token, data.token); localStorage.setItem(username, data.username); router.push(/dashboard); };这里登录成功后调用了 Vue Router 的 push 方法跳转到主页面。router.push 是 Vue Router 4 的编程式导航写法如果面试问“路由跳转有几种方式”至少要答出 router-link、router.push、router.replace 三种。4.3 记账页面、ECharts 统计与分类下拉联动主页面的记账表单是核心交互区。用户选择收入/支出类型后分类下拉框要联动变化。实现方式是把分类数据按 type 字段过滤const categories ref([]); const selectedType ref(2); // 默认支出 const filteredCategories computed(() { return categories.value.filter(c c.type selectedType.value); }); // 修改类型时清空已选分类避免收入类型选了支出分类 const handleTypeChange () { form.value.categoryId null; };统计页面用 ECharts 展示近 6 个月的支出趋势。后端提供一个聚合查询接口返回结构是[{ month: 2025-03, total: 1200.5 }]前端拿到数据后拼成坐标轴需要的格式const chartOption computed(() ({ tooltip: { trigger: axis }, xAxis: { type: category, data: statData.value.map(item item.month) }, yAxis: { type: value }, series: [{ name: 支出, type: line, data: statData.value.map(item item.total), smooth: true }] }));ECharts 图表的表头、图例、数据点提示都要能随窗口自适应使用 resize 方法监听容器尺寸变化const myChart echarts.init(container.value); window.addEventListener(resize, () myChart.resize());4.4 路由守卫未登录用户强制跳转登录页前端路由不能只依赖后端的拦截器因为如果用户没登录直接访问未授权页面后端会返回 401但页面已经白屏了。更好的体验是前端在路由跳转前就做判断router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else if (!token) { ElMessage.warning(请先登录); next(/login); } else { next(); } });路由守卫是 Vue Router 的全局前置守卫。这个代码解决了“刷新页面后 token 丢失导致页面跳回登录页”的问题因为每次跳转都会检查 localStorage 里的 token。5. 联调、打包部署与答辩验证5.1 前端打包后部署到 SpringBoot 静态目录开发完成后前端需要构建成静态文件。Vue 项目执行npm run build后会在 dist 目录生成打包产物把这个目录的全部文件复制到 SpringBoot 的src/main/resources/static下重新打包运行前端和后端就合并成了一个应用。访问地址变为http://localhost:8080/。SpringBoot 的静态资源配置默认会映射/路径但 Vue 路由是 history 模式时刷新页面会 404。解决办法是配置一个路径转发把非接口请求转发到 index.html。常见做法是加一个 Controller 处理error路径或者在 Vite 配置里把路由模式改为 hash。毕业设计演示时建议使用 hash 模式避免部署时还要额外处理路由回退。5.2 用 F12 和 SQL 日志验证数据正确性联调完成后用两个手段做回归验证。第一是在浏览器按 F12 打开开发者工具切到 Network 面板点击“新增支出”按钮找到那条 POST 请求查看 Payload 里的 JSON 是否包含 userId 和 categoryId 字段。如果接口报 401说明 Authorization 头没有带上去如果请求 URL 显示 8080 端口说明 Vite 代理没生效。第二是看后端控制台的 MyBatis 日志。启动配置里启用了 StdOutImpl每个 SQL 语句都会打印出来。查询时检查 WHERE 条件是否包含user_id ?参数如果漏了这个条件就是严重的安全漏洞用户 A 可以查到用户 B 的账单。这个点可以主动写进论文的“系统测试”章节展示你做了越权测试。5.3 答辩时数据库方面会被问到的 3 个问题答辩老师喜欢围绕数据库表设计提问最容易踩坑的是以下三个问题第一个问题“金额字段为什么不选 float” 答 Float 和 Double 是二进制浮点数存储金额会有精度误差比如 0.1 0.2 不等于 0.3。DECIMAL 是定点数按位数精确存储所以用 DECIMAL(10,2)。第二个问题“t_transaction 表为什么没有外键” 答 在外键约束下删除分类时需要先处理关联流水限制较多。采用逻辑关联在应用层通过事务校验分类是否存在既保证数据完整性又保留在业务里处理错误提示的能力。第三个问题“如果用户量变大这张表怎么优化” 答 靠 idx_user_date 联合索引覆盖“每个用户按时间查账单”的场景数据量上亿后可以按 user_id 做哈希分表或按月做分区。讲到这一步会明显超过同等层级毕设的水平。5.4 演示环境自检清单演示现场最容易出意外的不是代码逻辑而是环境问题。视频演示里录得好好的到了现场 MySQL 没启动连不上。按照下面的清单提前自检检查项操作预期结果MySQL 服务mysql -u root -p可登录能进入命令行数据库初始化连接 finance_db 后SHOW TABLES能看到 4 张表后端启动SpringBoot 无红色报错控制台出现 Tomcat started on port(s) 8080前端页面注册新账号并登录能进入主页面无跨域报错新增账单添加一笔支出列表页立即刷新ECharts 数据同步变化刷新页面F5 刷新当前页停留在当前路由不跳回登录页这些检查项每一项都对应一个论文测试用例可以直接整理成“功能测试表”放进毕业论文。对于需要视频演示的场景也不要只录正常流程把“登录失败提示”“预算超额警告”这些分支情况也各录一段 10 秒短视频演示时更有说服力。整个个人理财系统的核心价值就在这套可验证的数据闭环上用户从注册拿到合法身份到录入关系型数据再到统计聚合回显图表每一步都有迹可循。把这一条链路打穿项目交付时心里就有底。本文还有配套的精品资源点击获取