
简介基于微信小程序的个人财务管理系统毕业设计论文文档完整呈现从选题意义、系统分析到技术实现的全过程适合计算机相关专业学生作为毕业设计与论文写作参考。文档围绕Uni-weixin、Spring Boot与MySQL技术路线前端以小程序页面呈现后台结合Java语言完善功能并采用MySQL数据库存储数据重点介绍支出信息管理、收入信息管理等模块同时兼顾用户体验、数据安全与可视化报表设计。读者可直接借鉴其系统架构、章节划分和关键技术表述内容包含中英文摘要、目录、绪论、系统开发技术、系统分析等规范章节便于逐章对照学习。资源包精简高效仅含1个docx文件压缩包大小约2.92MB便于在线阅读与携带。目前已有450人学习下载适合需要快速获取论文范本、搭建毕业设计框架或完善个人财务管理系统的读者。1. 微信小程序个人财务管理系统这套毕业设计源码到底值不值得拿来改如果你正在为毕业设计选题发愁又恰好看到这份基于微信小程序的个人财务管理系统的完整论文加源码包我的建议是先别急着下载把这篇文章看完再做决定。这套系统的技术栈是 Uni-weixin 做小程序前端、SpringBoot 做后端接口、MySQL 存数据覆盖了从用户注册登录、收入支出录入到管理员后台管理的完整闭环。对计算机专业的同学来说它最大的价值不是能跑而是论文结构、数据库设计、前后端联调思路都可以直接复用。这篇笔记会从技术选型、数据库设计、核心代码实现到部署踩坑把整套系统拆开讲清楚省得你下载完对着代码发愣。2. 技术选型与系统架构为什么是 SpringBoot MySQL 这套组合2.1 小程序前端选型Uni-weixin 和微信开发者工具的关系这套系统在小程序端选择了 Uni-weixin 作为开发框架注意这里说的不是原生小程序开发。Uni-weixin 基于 Vue 语法可以一套代码编译到微信小程序、H5 等多个平台但在这份毕业设计里实际上只编译到了微信小程序端。我在拆这套源码的时候发现它的 pages 目录结构和原生小程序几乎一致所以即使你完全没用过 Uni-weixin只要会 Vue 的基础语法上手并不难。微信开发者工具在这里的角色是编译预览和调试。你从 GitHub 或网盘上把源码下载下来之后需要用微信开发者工具导入项目根目录它会自动识别 app.json 里的页面配置。有一点需要注意Uni-weixin 项目需要用 HBuilderX 编译后再导入微信开发者工具或者直接在开发者工具里打开编译后的 dist 目录直接导入源码目录会报错。// pages.json 中的核心配置对应小程序全局路由 { pages: [ { path: pages/index/index, style: { navigationBarTitleText: 首页 } }, { path: pages/login/login, style: { navigationBarTitleText: 登录 } }, { path: pages/income/income, style: { navigationBarTitleText: 收入管理 } }, { path: pages/expend/expend, style: { navigationBarTitleText: 支出管理 } } ] }这段配置是 Uni-weixin 的路由表它决定了小程序每个页面的路径和顶部导航标题。path 对应 pages 目录下的文件路径style 里可以单独设置每个页面的标题、背景色、是否下拉刷新等参数。实际开发中你不需要手动改这里HBuilderX 里新建页面会自动写入。2.2 后端框架SpringBoot 的核心目录结构与启动流程后端部分用的是 SpringBoot这套框架最大的特点是开箱即用不需要像老 Spring 项目那样写一堆 XML 配置。源码包里的后端工程是一个标准的 Maven 项目pom.xml 里引入了 spring-boot-starter-web、mybatis-plus、mysql-connector-java 这几个关键依赖。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.4.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependenciesmybatis-plus 在这里很有用它封装了常用的 CRUD 方法代码里不需要手写 mapper XML。你看到 Service 层里那些.list()、.getById()方法都是 mybatis-plus 内置的。版本号这里我用的是 3.4.2如果你本地环境 JDK 版本高于 11建议升到 3.5.x不然后续会有 getter/setter 相关的序列化问题。启动入口类是Application.java右键直接 Run 就能启动内嵌的 Tomcat。数据库连接配置在application.yml里端口默认 8080上下文路径是/springboot这决定了你小程序请求接口时的 baseURL。2.3 前后端数据交互接口设计与 JSON 格式约定小程序端和后端之间的通信走的是 HTTP 请求返回格式统一是 JSON。这套系统里后端封装了一个Result类所有接口都返回{ code: 200, msg: 成功, data: {...} }这种结构。前端通过uni.request发起请求拿到 data 部分渲染页面。// 后端统一返回结构 public class Result { private Integer code; private String msg; private Object data; public static Result success(Object data) { Result r new Result(); r.code 200; r.msg success; r.data data; return r; } }这个 Result 类的好处是前端处理逻辑统一先判断 code 是不是 200是就渲染 data否则弹 toast 提示 msg。我建议你拿到源码后不要改这个结构因为前端封装的request.js里已经写死了res.data的取值逻辑改动后端会连带要改前端十几个页面的回调函数。3. 核心功能模块与数据库设计五张数据表撑起整个收支系统3.1 功能结构拆解用户端和管理员端各管哪些事这套系统把用户角色分成两类普通用户和管理员。普通用户注册登录后能操作的核心功能是收入信息管理和支出信息管理。录入一条支出记录时字段包括支出编号、月份、支出标题、支出方式、图片、支出金额、支出明细、登记时间。收入信息的字段结构类似只是把支出换成收入。管理员端的功能更多除了能看到所有用户的流水数据外还能做用户管理、公告信息管理、系统管理。用户管理实际上就是封号、重置密码这类操作公告信息管理是发一些系统通知在首页轮播展示。// 小程序端首页的数据加载逻辑 onLoad() { this.loadBanner(); this.loadUserInfo(); }, methods: { async loadBanner() { const res await request.get(/api/notice/list); if (res.code 200) { this.bannerList res.data; } }, async loadUserInfo() { const res await request.get(/api/user/profile); if (res.code 200) { this.userInfo res.data; } } }首页同时拉取公告和用户信息两个接口这在前端是很常见的并行请求模式。需要注意/api/notice/list这个接口是不需要登录就能访问的但/api/user/profile必须在请求头里带上 token否则后端会拦截返回 401。源码里拦截器配置的是/api/user/**路径校验 token这个规则你可以按需调整。3.2 数据库表设计收入表、支出表、用户表字段逐一过数据库是整个系统的核心源码包里的finance.sql文件包含了全部建表语句我拆出来主要看这几张表。用户表设计的字段有id 主键、username 用户名、password 密码MD5 加密存储、role 角色、addtime 创建时间。注意这里 password 用的是 MD5安全性一般如果你要拿去答辩建议在代码里改成 BCrypt 加密这个点导师大概率会问。-- 支出信息表 CREATE TABLE zhichuxinxi ( id bigint(20) NOT NULL AUTO_INCREMENT, addtime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, zhichubianhao varchar(200) DEFAULT NULL, yuefen varchar(200) DEFAULT NULL, zhichubiaoti varchar(200) DEFAULT NULL, zhichufangshi varchar(200) DEFAULT NULL, tupian longtext, zhichujine float DEFAULT NULL, zhichumingxi longtext, dengjishijian date DEFAULT NULL, userid bigint(20) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8;支出信息表用的字段名全是拼音这是这类毕设源码的老传统。tupian字段是 longtext 类型存的是图片的 Base64 编码字符串。这种方式在小数据量时没问题但一旦用户上传的图片多了表会变得特别臃肿。我一般会把图片传到独立存储数据库只存 URL 路径但这套源码为了简单直接在数据库存了 Base64。3.3 登录会话与权限控制token 表的过期策略除了业务表之外还有一张专门的 token 表这是权限控制的关键。它记录了 userid、username、tablename表名、role角色、token 字符串、expiratetime过期时间。// 登录成功后生成 token 的逻辑 String token UUID.randomUUID().toString().replaceAll(-, ); TokenEntity tokenEntity new TokenEntity(); tokenEntity.setUserId(user.getId()); tokenEntity.setUsername(user.getUsername()); tokenEntity.setToken(token); tokenEntity.setRole(user.getRole()); // 设置过期时间为 2 小时后 tokenEntity.setExpiratetime(new Date(System.currentTimeMillis() 7200000)); tokenService.insert(tokenEntity);这里生成 token 用的 UUID过期时间设了 2 小时。实际使用中 2 小时有点短用户正录着账突然过期会被强制踢出。我建议改为 7 天因为个人财务系统不是银行后台安全等级不需要那么高。每次请求进来时拦截器会查 token 表校验 token 字符串存在且未过期同时更新过期时间实现续期效果。4. 微信端与后台的实现细节从页面到接口的完整链路4.1 小程序端注册登录流程页面跳转与会话保持注册登录是用户接触到系统的第一个功能。注册页面收集用户账号、密码、姓名、性别、年龄、邮箱、手机号等信息点击注册按钮后调用/api/user/register接口。这里有个前后端联调的区别要注意注册接口默认不校验用户名唯一性如果注册了同名账号数据库里会存在重复数据登录时查出来第一条会覆盖第二条。// 注册逻辑 async doRegister() { const { username, password, confirmPwd } this.form; if (!username || !password) { uni.showToast({ title: 请填写完整信息, icon: none }); return; } if (password ! confirmPwd) { uni.showToast({ title: 两次密码不一致, icon: none }); return; } this.form.mima md5(password); const res await request.post(/api/user/register, this.form); if (res.code 200) { uni.navigateTo({ url: /pages/login/login }); } }注意前端把密码做了 md5 之后传给后端后端存进数据库的也是密文。这里有个常见问题后端拦截器里如果也做了二次加密就会导致密文的密文。我在源码里看到的情况是后端直接存前端传过来的值所以前端必须保证加密逻辑正确。4.2 收支信息录入与管理表单校验与图片上传收支录入页面是系统使用频率最高的地方。用户在表单里填写标题、金额、方式、明细还可以拍照上传凭证点击保存后数据通过POST /api/zhichuxinxi/save接口写入支出表。这里有个我特别提醒你要注意的点页面里登记时间字段默认取new Date().toISOString()但这个格式是带 T 和 Z 的 ISO 格式后端数据库字段类型是 dateMySQL 在存储时会自动截取但如果你在查询接口里做时间范围筛选大概率会出现匹配不上的坑。// 保存支出记录 async saveExpend() { const form { zhichubianhao: ZC Date.now(), yuefen: this.currentMonth, zhichubiaoti: this.form.title, zhichufangshi: this.form.payMethod, zhichujine: parseFloat(this.form.amount), zhichumingxi: this.form.detail, dengjishijian: this.form.date.replace(T, ).substring(0, 10) }; const res await request.post(/api/zhichuxinxi/save, { ...form, userid: this.userInfo.id }); if (res.code 200) { uni.navigateBack(); } }这段代码里我做了两个改进一是支出编号用时间戳生成而不是后端随机数二是把日期做了格式化先替换 T 再截取前 10 位确保存进去的是YYYY-MM-DD格式。原始源码里直接传完整 ISO 字符串我在复现时发现查询接口用between做日期范围筛选会有数据漏掉改成标准格式后就正常了。4.3 后台管理接口用户管理、支出管理、收入管理的实现方案管理员端的接口在/api/admin路径下核心是几个列表查询接口。由于 mybatis-plus 的存在分页查询非常简单Controller 层接收page和limit参数Service 层直接调page()方法。// 分页查询支出信息管理员端 GetMapping(/zhichuxinxi/page) public Result page(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer limit, RequestParam(required false) String zhichubiaoti) { QueryWrapperZhichuxinxiEntity wrapper new QueryWrapper(); if (StringUtils.isNotBlank(zhichubiaoti)) { wrapper.like(zhichubiaoti, zhichubiaoti); } wrapper.orderByDesc(addtime); wrapper.eq(userid, getUserIdFromToken()); PageZhichuxinxiEntity p new Page(page, limit); return Result.success(zhichuxinxiService.page(p, wrapper)); }分页参数 page 是当前页码从 1 开始limit 是每页条数。关键词搜索条件zhichubiaoti是可选的QueryWrapper 里用了 like 做模糊匹配这样管理员在后台搜索房租就能把所有包含房租的支出记录查出来。这里我加了一个wrapper.eq(userid, ...)的查询条件因为源码原始版本管理员能看所有用户的数据但我认为应该先按用户隔离否则隐私上会出问题。5. 避坑指南部署运行中的五个常见问题与排查方法5.1 微信开发者工具导入项目后白屏现象用微信开发者工具导入源码目录编译不报错但模拟器里一片空白控制台提示找不到 app.json。原因这个项目是 Uni-weixin 工程不是原生小程序工程微信开发者工具无法直接识别 src 目录下的源码。需要先用 HBuilderX 执行发行 - 小程序微信操作生成 dist/build/mp-weixin 目录再用开发者工具导入这个目录。解决在 HBuilderX 里打开项目根目录菜单栏选择运行 - 运行到小程序模拟器 - 微信开发者工具。HBuilderX 会自动调用微信开发者工具并打开编译后的产物日常调试就用这种方式别直接导入源码根目录。5.2 后端启动报数据库连接失败现象IDEA 里启动 Application.java控制台报错Access denied for user rootlocalhost。原因application.yml 里的数据库账号密码和本地 MySQL 不一致。源码包默认配的是 root/123456但很多人本机 MySQL 设置的不是这个密码或者 root 账号只允许 localhost 登录。解决打开 src/main/resources/application.yml把 url、username、password 改成自己本机的配置。记得确认 MySQL 版本url 里serverTimezoneAsia/Shanghai这个参数在 MySQL 8.x 下必须加MySQL 5.7 可以不加但建议保留。spring: datasource: url: jdbc:mysql://localhost:3306/finance?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver5.3 小程序请求接口报 404 或 405现象小程序页面能打开但所有请求都返回 404或者提示请求方法错误。原因404 大概率是 baseURL 配置错了。源码里前端request.js默认请求地址是http://localhost:8080/springboot如果后端改了端口或上下文路径前端没同步改就会 404。405 则是方法不匹配比如后端只允许 POST前端用了 GET。解决先在后端接口用 Postman 测通确认返回 JSON 再排查前端。改前端common/request.js里的 baseURL改成你的局域网 IP 加端口比如http://192.168.1.100:8080/springboot这样真机调试时手机也能访问到电脑上的后端服务。5.4 上传图片提示文件过大或请求超时现象在支出录入页面选择一张手机里拍的照片点击上传后转圈很久最终提示请求超时或者后端报MaxUploadSizeExceededException。原因这套源码把图片转成 Base64 存进数据库一张 3MB 的照片转成 Base64 后大概变成 4MB加上 JSON 封装请求体超过 5MB。SpringBoot 默认的单次请求体上限是 1MB超过了直接抛异常。解决在 application.yml 里调整上传大小限制spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB但这是治标不治本。从根本上看建议把图片上传改成 wx.uploadFile 走 multipart/form-data后端用 MultipartFile 接收后存到本地或对象存储数据库只存 URL 路径。改造工作量不大但对系统健壮性的提升很明显。答辩时导师问起这个改进能给你加分不少。5.5 token 过期导致操作频繁被踢出现象用户录了几条收支数据后下一次操作突然提示登录状态已失效请重新登录。原因token 表里过期时间设置的 2 小时而且前端request.js只在收到 401 时才跳转登录页没有做静默续期。用户在连续使用过程中超过 2 小时没有重新登录就会被强制踢出。解决两个方向一是后端把 token 过期时间调长到 7 天或 30 天二是前端在每次请求成功后重新拉取用户信息并更新本地 Storage 里的登录态。第一个方案更简单直接个人财务系统本身不是高安全敏感场景token 有效期设长一些体验更好。// request.js 中统一拦截 401 处理 if (res.code 401) { uni.removeStorageSync(token); uni.removeStorageSync(userInfo); uni.navigateTo({ url: /pages/login/login }); }6. 进阶技巧在现有框架上增加按月的收支汇总报表这套系统的数据录入和管理已经完整了但缺少一个直观的统计展示功能用户看不到自己这个月到底花了多少钱、收入多少。这里我给一个扩展思路后端加一个统计接口小程序首页加一个简易的汇总卡片。后端接口的核心逻辑是给收入表和支出表分别按月份做 SUM 聚合GetMapping(/api/report/monthly) public Result monthlyReport(RequestParam String month) { QueryWrapperShouruxinxiEntity incomeWrapper new QueryWrapper(); incomeWrapper.like(yuefen, month); incomeWrapper.eq(userid, getUserIdFromToken()); ListShouruxinxiEntity incomes shouruxinxiService.list(incomeWrapper); QueryWrapperZhichuxinxiEntity expendWrapper new QueryWrapper(); expendWrapper.like(yuefen, month); expendWrapper.eq(userid, getUserIdFromToken()); ListZhichuxinxiEntity expends zhichuxinxiService.list(expendWrapper); BigDecimal totalIncome incomes.stream() .map(i - BigDecimal.valueOf(i.getShourujine())) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal totalExpend expends.stream() .map(e - BigDecimal.valueOf(e.getZhichujine())) .reduce(BigDecimal.ZERO, BigDecimal::add); MapString, Object data new HashMap(); data.put(totalIncome, totalIncome); data.put(totalExpend, totalExpend); data.put(balance, totalIncome.subtract(totalExpend)); return Result.success(data); }前端展示时在 index 页面加两个数字卡片直接this.totalIncome res.data.totalIncome赋值即可。这里有个容易忽略的细节yuefen字段在录入时保存的是2025-01这种格式查询时用like匹配就能覆盖整月数据如果你在收入表里把这个字段改成 datetime 类型就要改成between查询了。这个扩展不仅是给系统加了个功能亮点答辩时还能引导到聚合查询优化、大数据量下的索引设计这类问题上比单纯讲 CRUD 加分很多。我自己拆这套代码的时候刚开始也觉得就是个普通的增删改查项目但真正跑起来才发现图片存储策略、token 过期处理、日期格式统一这些细节才是决定系统能不能实际用的关键。从那以后我每次拿到新的毕设源码都会先摸一遍表结构和接口列表而不是急着启动这个习惯了真的能少踩很多坑。希望帮到你。本文还有配套的精品资源点击获取