☰
个人记账系统毕设实战:SpringBoot+Vue前后端分离开发全流程
2026/10/13 3:50:29 网站建设 项目流程

毕设季又到了,每年这个时候总能看到大量同学在选题上纠结到失眠。个人记账系统这个题目,表面看是“平平无奇的小项目”,实际上它把 SpringBoot + Vue、MySQL、数据库设计、接口文档、部署上线这些毕业设计的硬性指标全部覆盖了。源码、数据库、文档三件套都能落地,既不会像商城系统那样业务复杂到失控,也不会像普通管理系统那样看起来没啥含金量。无论你是想稳扎稳打通过查重和答辩,还是想借这个项目把前后端分离这套技术栈完整跑一遍,它都是非常合适的选题。下面我用实际做完这类项目的经验,把从选题、设计、开发到答辩的完整过程拆开讲。

1. 项目选题:为什么个人记账系统是毕业设计的稳妥之选

1.1 这类项目到底解决了什么问题

很多同学选毕设题目时,第一反应是“越热门越好”,于是扎堆去做商城、秒杀、外卖平台。这类系统确实功能丰富,但问题也随之而来:业务规则复杂、页面数量多、前后端联调周期长,一个人单打独斗可能连登录权限都还没理清楚就快到截止日期了。

个人记账系统选它不是因为简单,而是因为“业务边界清晰”。用户、账单、分类、统计,每个模块都是教科书级别的通用设计,既能体现你对 SpringBoot 和 Vue 的掌握程度,又能保证在有限时间内做出完整可演示的系统。它能够覆盖软件开发的完整生命周期:需求分析、数据库设计、后端接口开发、前端页面实现、功能测试、部署上线、文档撰写。这些环节一个都不少。

更关键的是,记账系统的需求非常贴近日常生活。任何人都能说出“我要记一笔账,月底看看钱花哪了”这种真实诉求。这在写需求分析和做功能验证时特别占优势,因为你自己就是需求方,不需要去请教业务专家,功能合不合理、流程顺不顺,试用一下就知道了。

1.2 为什么 SpringBoot + Vue 是黄金组合

技术选型直接决定开发效率和答辩效果。SpringBoot + Vue 在近几年的毕业设计中出场率极高,不是没有理由的。

后端用 SpringBoot,是因为它的生态太成熟了。自动配置帮你省掉大量 XML 配置,内嵌 Tomcat 让项目可以一键启动,配合 MyBatis-Plus 这类增强框架,增删改查的代码量能压缩到极低。而且 Java 是绝大多数高校的主语言,评委老师看到 SpringBoot 天然就有认同感,不会质疑你的技术选型。

前端用 Vue,看中的是组件化和生态。Vue 3 配合 Vite 开发服务器,热更新速度快,Element Plus 提供现成的表格、表单、弹窗、日期选择器,你不需要从零写 UI。一个记账页面从设计到实现,熟练的话两天就能搞定。

前后端分离还有一个隐藏优势:部署方案成熟,后端打一个 jar 包,前端打包成静态文件丢到 Nginx 里就行,比 JSP 那套老方案清爽得多。答辩时也能顺着这套架构讲清楚数据是怎么从 MySQL 流到页面上的。

1.3 适合什么样的同学做

如果你是 Java 方向的应届毕业生,准备用这个题目交毕设,完全没问题。基础要求其实不高:掌握 Java 语法、了解 SpringBoot 基本用法、会写简单的 SQL,前端部分只要懂 HTML 和 JavaScript 的基础,Vue 可以边做边学。

这套技术栈对刚入门的人很友好,因为 SpringBoot 的“约定大于配置”能帮你避开很多底层细节,MyBatis-Plus 让你不需要手写复杂的 JDBC 代码,Vue 的单文件组件也让页面开发变得直接。我在实际带项目时发现,一个每天能投入四五个小时的同学,从零开始做,三周左右可以完成核心功能,四周足以把文档和答辩材料全部整理好。

2. 系统整体设计与核心功能拆解

2.1 用户角色与核心业务闭环

个人记账系统原则上只有一个角色:普通用户。它和后台管理系统最大的区别就是不需要“管理员”这个角色,因为每个人的记账数据都是私有的,不存在一个超级管理员去查看所有用户账本的需求。

核心业务闭环其实非常朴素:

  • 游客注册并登录系统;
  • 登录成功后,在记账页面输入一笔支出,比如选择“餐饮”分类,填入金额 25.5,日期选今天,备注写“午饭”;
  • 点击保存,这条记录写入账单表;
  • 首页的最近账单列表立刻出现这条数据;
  • 统计页面根据当天或当月所有账单,展示支出分类占比和金额趋势;
  • 如果发现记错了,用户可以编辑或删除这笔账单。

把这个闭环跑通,系统的主心骨就立住了。剩下的分类管理、个人资料、预算提醒,都是在这个闭环上长出来的枝叶。

2.2 功能模块清单与优先级划分

做项目最忌讳一上来就想把所有功能都做了。我建议你把功能拆成 MVP(最小可行产品)和扩展功能两个梯队。

模块核心功能优先级
认证模块注册、登录、登出、修改密码P0,第一优先级
账单模块新增、编辑、删除、分页查询、条件筛选P0,第一优先级
分类模块收入/支出分类展示、自定义分类P0,第一优先级
统计模块按时间汇总收支、按分类统计占比、趋势图P1,第二优先级
用户模块个人资料展示与编辑、头像上传P1,第二优先级
增值功能预算设置、账单导出、定时提醒P2,有余力再做

先做 P0 的功能,哪怕时间再紧,也能交出一个业务闭环完整、可演示的系统。P2 功能是答辩时的加分项,不是必选项。

2.3 技术架构与项目目录规划

技术架构上采用标准的前后端分离模式。后端使用 SpringBoot 2.7 + MyBatis-Plus + MySQL 8.0 + JWT 做接口鉴权。前端使用 Vue 3 + Vite + Vue Router + Pinia + Element Plus + ECharts。

后端项目结构我建议按职责分层:

backend ├── src/main/java/com/example/account │ ├── config # 跨域配置、拦截器注册 │ ├── controller # 接口层,只负责参数接收和结果返回 │ ├── service # 业务逻辑层,处理具体规则 │ ├── mapper # 数据访问层,MyBatis-Plus 的 Mapper 接口 │ ├── entity # 数据库实体类 │ ├── common # 统一返回结果、异常处理、常量 │ └── utils # JWT 工具类等 └── src/main/resources ├── application.yml # 数据源、端口等配置 └── sql # 初始化数据库脚本

前端项目结构按功能划分:

frontend ├── src │ ├── api # 接口封装,统一管理 axios 请求 │ ├── router # 路由配置 │ ├── store # Pinia 状态管理,保存用户信息 │ ├── views # 页面级组件,如登录页、首页、统计页 │ ├── components # 公共组件,如账单表单弹窗 │ └── utils # axios 实例、日期格式化工具

这种分层的好处是逻辑清晰,出了问题能快速定位。答辩的时候老师问你“这个功能在哪一层实现的”,你也能说得头头是道。

3. 数据库设计:记账系统的表结构到底怎么定

3.1 核心表设计与字段说明

数据库是整个系统的基石,表结构设计得好,后端代码能省一半力气。个人记账系统最核心的表只有三张:用户表、分类表、账单表。

用户表负责账号体系:

CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码,存的是加密后的哈希值', `nickname` varchar(50) DEFAULT NULL COMMENT '昵称', `avatar` varchar(255) DEFAULT NULL COMMENT '头像地址', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除标记,0正常 1删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

分类表既存系统预置分类,也存用户自定义分类:

CREATE TABLE `category` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL DEFAULT 0 COMMENT '所属用户ID,0表示系统预置', `name` varchar(50) NOT NULL COMMENT '分类名称', `type` tinyint NOT NULL COMMENT '分类类型,0支出 1收入', `icon` varchar(50) DEFAULT NULL COMMENT '分类图标标识', `sort` int NOT NULL DEFAULT 0 COMMENT '排序号', `create_time` datetime NOT NULL, `update_time` datetime DEFAULT NULL, `deleted` tinyint NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

账单表是业务核心,字段最多:

CREATE TABLE `bill` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL COMMENT '所属用户', `category_id` bigint NOT NULL COMMENT '分类ID', `amount` decimal(10,2) NOT NULL COMMENT '金额,保留两位小数', `bill_type` tinyint NOT NULL COMMENT '类型,0支出 1收入', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `bill_date` date NOT NULL COMMENT '记账日期', `create_time` datetime NOT NULL, `update_time` datetime DEFAULT NULL, `deleted` tinyint NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `idx_user_date` (`user_id`, `bill_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

三张表通过外键逻辑关联:user 和 bill 通过 user_id 关联,bill 和 category 通过 category_id 关联。我没用数据库物理外键,而是在业务层维护关联关系,这样可以避免外键带来的插入和删除性能损耗,在并发量不高的个人项目里是更合理的选择。

3.2 金额字段为什么要用 decimal

这是一个答辩高频问题,建议你提前吃透。金额字段绝对不能用 float 或 double,因为浮点数在计算机内部是二进制表示的,很多十进制小数无法精确表达。举个例子,0.1 加 0.2 在浮点数运算里结果是 0.30000000000000004,账目系统里这样的误差是致命的。

decimal 是定点数,按十进制字符串存储,能保证精度。配合 Java 端使用 BigDecimal 类型接收,整个链路都不会丢精度。在账单表里我统一用 decimal(10,2),意思是总位数最多 10 位,小数位固定 2 位,对个人记账来说绰绰有余。

前端处理金额时也有坑。如果后端把 decimal 转成 JSON 数字,JavaScript 的 Number 类型在遇到超大数字时也可能丢精度。我的做法是后端统一把金额转成字符串返回前端,前端展示时直接原样使用,提交时再把字符串传给后端,后端用 BigDecimal 接收。这套方案在实践中非常稳。

3.3 分类与标签的两种设计思路

分类设计有两种常见方案。第一种是固定枚举,在代码里写死“餐饮、交通、购物、娱乐”等几个分类,简单粗暴,但用户没法自定义,体验很差。第二种是用户分类表,每个用户自己维护一套分类,灵活但初始数据为空,用户进来要自己建很多分类,门槛高。

我采用的是折中方案:系统预置一批常见分类,作为默认数据在用户注册时自动复制到该用户下,用户也可以随时新增、修改自己的分类。这种方式既保证了开箱即用,又保留了灵活性。分类表的 user_id 字段加上逻辑删除,删除分类时还要处理关联账单的归属问题,一般有两种做法:不允许删除已有账单的分类,或者把相关账单标记为“未分类”。建议在代码里实现前者,更符合用户直觉。

4. 后端核心实现:从登录鉴权到账单增删改查

4.1 登录鉴权与拦截器

个人记账系统虽然只有一个人用,但也必须有登录功能,这是毕设的硬性要求。密码存储我强烈推荐使用 BCrypt 哈希,而不是 MD5 或 SHA。MD5 加盐虽然能用,但 BCrypt 自带随机盐且计算缓慢,能有效抵御彩虹表攻击,是当前业界主流做法。

后端生成 token 使用 JWT,登录成功后服务端返回一个包含用户信息的 token,前端保存到 localStorage,后续请求在请求头带上Authorization: Bearer <token>。服务端通过拦截器统一校验 token,不需要像 Session 那样在服务端保存状态,天然适合前后端分离架构。

核心代码结构大致如下:

@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行 OPTIONS 预检请求 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { return true; } String token = request.getHeader("Authorization"); // 校验 token,失败则返回 401 Long userId = JwtUtil.parseToken(token); if (userId == null) { throw new BusinessException(401, "登录已过期,请重新登录"); } // 将 userId 存入 ThreadLocal,后续业务方法直接获取 UserContext.setUserId(userId); return true; } }

这里有一个容易忽略的细节:前端发送跨域请求时,如果请求方式是 PUT、DELETE 或者请求头包含自定义字段,浏览器会先发送一个 OPTIONS 预检请求。后端如果把这个请求拦截了或者返回错误状态码,前端就会报跨域错误。所以拦截器里必须放行 OPTIONS 请求。

4.2 账单模块的代码结构

账单模块是后端工作量最大的部分,但逻辑其实很直接。接口设计遵循 RESTful 风格:

  • POST /api/bill新增账单
  • PUT /api/bill修改账单
  • DELETE /api/bill/{id}删除账单
  • GET /api/bill/page分页查询账单

所有接口必须从 UserContext 中获取当前登录用户 ID,而不是信任前端传过来的参数。这一点特别重要,否则用户可以通过伪造 user_id 看到别人的记账数据,答辩时被问到权限控制,答不上来就尴尬了。

典型的 Service 层新增账单逻辑:

public void addBill(BillDTO dto) { Long userId = UserContext.getUserId(); if (dto.getAmount() == null || dto.getAmount().compareTo(BigDecimal.ZERO) <= 0) { throw new BusinessException("金额必须大于0"); } Category category = categoryMapper.selectById(dto.getCategoryId()); // 分类必须存在且属于当前用户或系统预置 if (category == null || (category.getUserId() != 0 && !category.getUserId().equals(userId))) { throw new BusinessException("分类不存在"); } Bill bill = new Bill(); bill.setUserId(userId); bill.setCategoryId(dto.getCategoryId()); bill.setAmount(dto.getAmount()); bill.setBillType(dto.getBillType()); bill.setRemark(dto.getRemark()); bill.setBillDate(dto.getBillDate()); billMapper.insert(bill); }

查询接口使用 MyBatis-Plus 的分页插件,配合 QueryWrapper 做条件拼接。要注意的是,金额和备注字段允许为空,但 bill_date、category_id、bill_type 不能为空。前端提交之前要校验,后端同样要校验,两层校验缺一不可。

4.3 统计报表接口的实现思路

统计模块是个人记账系统的亮点,也是答辩时最容易出彩的部分。我主要实现了两个统计接口。

第一个是月度收支趋势接口,按自然月分组,统计每个月的支出总额和收入总额,前端折线图直接用:

SELECT DATE_FORMAT(bill_date, '%Y-%m') AS month, SUM(CASE WHEN bill_type = 0 THEN amount ELSE 0 END) AS expense, SUM(CASE WHEN bill_type = 1 THEN amount ELSE 0 END) AS income FROM bill WHERE user_id = #{userId} AND bill_date BETWEEN #{startDate} AND #{endDate} GROUP BY month ORDER BY month;

第二个是分类占比接口,按分类分组统计支出金额,前端饼图直接用:

SELECT c.name AS categoryName, SUM(b.amount) AS total FROM bill b LEFT JOIN category c ON b.category_id = c.id WHERE b.user_id = #{userId} AND b.bill_type = 0 AND b.bill_date BETWEEN #{startDate} AND #{endDate} GROUP BY b.category_id ORDER BY total DESC;

这两个 SQL 的查询结果可以直接封装成一个包含月份、支出合计、收入合计字段的 VO 对象返回。需要注意的是,如果某个月没有任何账单,分组结果里就不会出现这一行,前端折线图会缺数据,处理方式是在前端补全月份序列。这个小细节做到位,答辩时能体现你的工程思维。

5. 前端页面实现:Vue3 + Element Plus 的落地细节

5.1 项目初始化和路由设计

前端建议直接用 Vite 搭建 Vue 3 项目,执行npm create vue@latest之后按提示选择需要的特性。我个人的习惯是手动选择 Vue Router 和 Pinia,其他选项先不勾,保持项目干净。

路由需要做登录守卫,核心逻辑是:进入页面之前判断 Pinia 或 localStorage 里有没有 token,没有就跳转到登录页,有就放行。这是一个很小的拦截器,但在答辩演示时很加分,因为老师第一眼看到的是“游客不能直接访问记账页面”这一安全特性。

Element Plus 按需引入最方便的方式是全量引入,虽然打包体积大一点,但个人项目中性能差距不明显,胜在省心。ECharts 也可以用全量引入,后续如果做按需加载,再通过unplugin-auto-import插件优化。

5.2 记账表单与账单列表

记账表单是整个系统的核心交互,我建议使用弹窗组件承载。表单字段包括:收支类型(单选)、分类(下拉选择)、金额(数字输入框)、日期(日期选择器)、备注(文本输入框)。

校验规则这样配置:金额必须大于 0,日期不能为空,分类不能为空。这里有一个很实在的技巧:切换收支类型时要联动刷新分类下拉框的数据,因为支出和收入的分类是两套。分类数据通过接口获取,存在变量里,切换类型时直接过滤,避免重复请求。

账单列表页使用 Element Plus 的el-table组件,展示日期、分类、备注、金额、操作列。操作列提供编辑和删除按钮。列表顶部放筛选条件:类型下拉框、分类下拉框、日期范围选择器。查询按钮点击后重新请求分页接口。

删除操作建议用ElMessageBox.confirm弹窗二次确认,防止手滑误删。这是用户交互的细节,也是毕设文档中测试用例的素材来源。

5.3 图表统计页怎么做

统计页是区分“做完”和“做好”的分水岭。我建议至少做两个图:近 6 个月收支趋势折线图,和本月支出分类占比饼图。

ECharts 的使用逻辑很清晰:先调用统计接口拿到数据,然后把数据处理成 ECharts 需要的series结构。折线图的数据结构大致是:

{ xAxis: ['2024-01', '2024-02', '2024-03'], series: [ { name: '支出', data: [1200, 980, 1500] }, { name: '收入', data: [5000, 6000, 5500] } ] }

饼图则是[{ name: '餐饮', value: 1200 }, { name: '交通', value: 300 }]的结构。前端拿数据之后稍微转换一下就能用。有一点要注意:分类占比返回的是 BigDecimal 转成的字符串,前端要parseFloat之后再传给 ECharts,否则图表会报类型错误。

6. 联调、打包与部署:从本地跑到服务器

6.1 前后端联调常见坑

开发环境联调最容易踩的坑就是跨域。前端跑在 5173 端口,后端跑在 8080 端口,端口不同就属于跨域。解决办法有两种。

第一种是后端配置 CORS,简单有效:

@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }

第二种是前端代理,在 Vite 的vite.config.js里配置:

server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

个人建议开发环境用第二种,生产环境用 Nginx 反向代理。这样前端代码里可以统一写/api开头的相对路径,不需要根据环境切换 baseURL,省去很多麻烦。

联调中还有一个隐蔽问题:后端返回的时间字段默认格式是yyyy-MM-dd'T'HH:mm:ss.SSSZ,前端展示时非常难看。我通常在 application.yml 里配置统一的 JSON 序列化格式,或者在实体类时间字段上加@JsonFormat注解,把格式统一成yyyy-MM-dd HH:mm:ss,展示端直接省事。

6.2 打包部署的两种方式

部署的核心目标就是生产环境能稳定跑起来。最常规的流程是:

后端执行mvn clean package -DskipTests,生成 jar 包,上传到服务器后执行java -jar xxx.jar --spring.profiles.active=prod启动。

前端执行npm run build,生成 dist 目录,把 dist 里的文件上传到服务器,用 Nginx 托管静态资源,同时配置反向代理把/api请求转发到后端。

Nginx 配置示例:

server { listen 80; server_name your-domain.com; root /opt/account-frontend; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }

第二个部署方案是用 Docker。写一个 Dockerfile 把后端 jar 打成镜像,前端用 Nginx 镜像托管,再用一个 docker-compose.yml 编排前后端和 MySQL 三个容器。这种方式在简历上是一个很亮眼的加分项,但如果你对 Docker 不熟,不建议答辩前临时折腾,容易翻车。

7. 毕业设计文档与答辩准备

7.1 文档结构模板

很多同学代码写完了,文档却不知道怎么组织。我提供一个比较通用的目录框架,直接套用即可:

  • 摘要与 Abstract
  • 第一章 绪论:研究背景、国内外现状、研究内容
  • 第二章 需求分析:可行性分析、功能需求、非功能需求、用例图
  • 第三章 系统设计:系统架构图、功能模块图、数据库 E-R 图、表结构设计
  • 第四章 系统实现:按模块写关键功能实现,配核心代码片段和界面截图
  • 第五章 系统测试:测试环境、测试用例表格、测试结果分析
  • 第六章 总结:项目完成情况、遇到的问题、下一步改进

写文档最忌讳的是大段贴代码。老师更想看到的是你的思考过程:为什么用这个方案、遇到了什么问题、怎么解决的。数据库设计部分要画 E-R 图,架构部分要画系统架构图,这些图在答辩时的表达价值甚至超过文字。

7.2 高频答辩问题与回答思路

答辩演示的路径建议固定为:注册新账号、登录、记一笔支出、查看列表、查看统计图表、退出登录,演示完再打开数据库证明数据写入了。这个路径能把项目的所有亮点串起来。

老师的高频提问基本集中在几个方向:

“为什么用 JWT 而不是 Session?”回答思路:前后端分离架构下服务端不保存会话状态,JWT 无状态、可扩展,适合分布式部署。

“金额字段精度问题怎么处理?”回答思路:浮点数有精度误差,改用 decimal 存储,Java 端用 BigDecimal 配合运算,前端用字符串传递。

“逻辑删除和物理删除有什么区别?”回答思路:物理删除直接删除记录且无法恢复,逻辑删除通过 deleted 字段标记,保留历史数据便于统计和审计,缺点是每次查询都要带 deleted = 0 的条件。

“项目有什么可以改进的地方?”回答思路:引入 Redis 缓存热数据、账单导入导出、预算超支提醒、部署到 Docker 等。这是一道送分题,提前准备好就稳了。

8. 实操经验与避坑指南

8.1 踩坑记录速查表

整理了几个我做类似项目时最容易踩的坑,按出现频率排序:

问题现象根本原因解决方案
前端请求接口报 404,但后端接口存在前端代理没配置或 Nginx location 路径不对检查 Vite proxy 和后端 context-path 是否匹配
登录成功后刷新页面就退出了token 只放在了内存里持久化到 localStorage,路由守卫读取存储
金额显示成 25.999999999数据库用了 float 或前端 JSON 序列化丢失精度字段改 decimal,返回字符串
日期差了 8 小时JDBC 连接参数没设置 serverTimezone数据库连接串加 serverTimezone=Asia/Shanghai
Element Plus 表格数据不刷新修改数据后只更新了当前行变量,没有重新请求列表统一在保存后调用列表查询接口刷新
删除分类时提示外键错误没有处理账单表里的关联数据先校验该分类下是否有账单,有则拒绝删除
接口能通,但 OPTIONS 请求被拦截拦截器没放行预检请求在拦截器里对 OPTIONS 直接放行
MyBatis-Plus 更新时字段没更新updateById 默认忽略 null 字段有置空需求时使用 UpdateWrapper 显式 set

8.2 这些扩展功能值得做

如果你的时间还有富余,强烈建议加一个 P2 级扩展功能,这会让你的项目在答辩时明显区别于“增删改查”项目。

我自己最推荐的是“账单导出 Excel”。后端用 EasyExcel 或 POI 把当前筛选条件下的账单写入 xlsx 文件,前端提供一个“导出”按钮。这个功能工作量不大,但涉及到文件流传输、文件下载、文件名编码等细节,写进文档里特别有料。

其次是“预算提醒”。每个月设定一个支出预算,后端在记账时检查当月已支出金额是否超过预算,如果超过在接口返回一个提醒标记,前端在页面上显示“本月支出已超预算”。这个功能展示了事务处理和业务判断能力,也是一个不错的亮点。

8.3 最后分享一点个人体会

做这类项目最有价值的不是最终那个 demo,而是中间踩坑、排查、解决的完整过程。我在实践中发现,最从容的做法是先把数据库表和接口约定想清楚再动手写代码,而不是界面写到哪、数据库设计到哪。个人记账系统的边界很清楚,前期花一两天把表和接口定好,后期开发速度会非常快。

很多同学容易犯的错误是一上来就钻到前端页面里,把界面调得花里胡哨,结果后端接口没接几个。正确的推进顺序应该是:需求确认、数据库设计、后端接口开发与自测、前端页面开发与联调、部署上线、写文档。每一步都对齐时间节点,哪怕中途遇到问题,也留有余地。

如果你正准备用这个题目做毕设,照着这条链路走下去,三到四周能出完整成果。如果时间允许,再挑一个扩展功能做深一点,答辩时讲出来的东西会完全不一样。希望这些经验能帮你少走点弯路,也祝你顺利搞定毕业设计。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询