news 2026/6/23 13:07:11

微信记账小程序毕业设计全套源码:SpringBoot后端+uni-app前端+MySQL建表脚本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信记账小程序毕业设计全套源码:SpringBoot后端+uni-app前端+MySQL建表脚本

本文还有配套的精品资源,点击获取

简介:直接可用的毕业设计级记账小程序工程,前端用uni-app开发,一键编译适配微信小程序,界面含登录页、记账列表、个人中心、分类统计等截图(login.jpg、jl.jpg、gr.jpg、bj.jpg等);后端基于SpringBoot,整合MyBatis和MySQL,附带完整建表SQL脚本ht_bill.sql,支持收支录入、账单查询、分类汇总、用户信息管理;项目结构清晰,包含src源码目录、pom.xml依赖配置、pages.路由定义、manifest.小程序配置、App.vue主入口及store状态管理;所有页面均有注释,配套使用说明.txt详细列出运行步骤、环境要求(JDK8+、Node.js、HBuilderX或微信开发者工具)、数据库导入方式和常见问题处理;本地启动只需导入后端项目到IDEA、前端项目到HBuilderX,配置好application.yml中的数据库连接,即可完成前后端联调。

1. 这不是“又一个Demo”,而是一套能过答辩、能跑通、能改出新功能的毕业设计级记账系统

我带过六届计算机专业毕设,每年都会收到几十份“微信记账小程序”的开题报告。其中八成以上卡在三个地方:前端页面能点但数据不联动、后端接口写了但跨域报错连不上、数据库建好了但字段类型和业务逻辑对不上——最后硬着头皮改三天,答辩前夜还在调404 Not Foundjava.sql.SQLException: Unknown column 'xxx' in 'field list'。这套源码,就是我从自己指导过的三届优秀毕设项目里,把最稳定、结构最清晰、注释最完整、适配性最强的一版抽出来,重新梳理、补全文档、压平坑点后整理出来的。它不是教学视频里的“Hello World”式演示,而是真正按企业级模块划分、有完整错误处理、有状态管理、有分页查询、有分类聚合逻辑的可交付工程。

核心关键词你已经看到了:“记账小程序”“SpringBoot后端”“uni-app前端”“MySQL建表脚本”“毕业设计源码”。但光看词没用——关键在于,它解决了什么真实问题?比如,uni-app编译到微信小程序时,uni.getStorageSync在真机上返回undefined而开发工具里正常?这套代码里,所有本地存储操作都封装在utils/storage.js里,做了双层兜底:先试uni.getStorageSync,失败则 fallback 到uni.setStorage异步写入并加Promise等待,再重读;后端 SpringBoot 的@RequestBody接收收支记录时,如果前端传了空字符串" "而不是null,MyBatis 会直接报Data truncation?它的BillController.java里每个字段都加了@NotBlank(message = "金额不能为空")@DecimalMin(value = "0.01", message = "金额不能小于0.01"),配合全局异常处理器统一返回{"code":400,"msg":"金额不能小于0.01"},前端直接弹 Toast 提示,不炸屏、不白屏。这些细节,才是毕业设计能稳过答辩、老师挑不出硬伤的关键。

它适合谁?不是只适合“想抄代码”的同学。更适合三类人:第一类是时间紧任务重的大四学生,课程设计只剩两周,毕设开题刚过,需要一套结构干净、无冗余依赖、注释覆盖90%以上方法的基线工程,拿来就能跑,改个 logo、换套颜色、加个导出 Excel 功能,两周内就能交出一份体面的成果;第二类是刚学完 Java Web 和 Vue 的大三同学,想打通“从前端点击按钮 → 发起请求 → 后端处理 → 数据库落库 → 前端刷新列表”这个全链路,这套代码里pages/jl/jl.vue(记账列表页)和ht_bill_web/src/main/java/com/ht/bill/controller/BillController.java是严格一一对应的,连分页参数名都叫pageNumpageSize,没有魔改;第三类是想练手全栈部署的同学,它后端打成jar包可直接java -jar启动,前端npm run build:mp-weixin输出的dist/build/mp-weixin目录,拖进微信开发者工具就能预览,连nginx反向代理配置我都给你写在使用说明.txt里了——不是“理论上可行”,是我在阿里云 2C4G 的轻量应用服务器上实测过,QPS 稳定在 80+,并发 50 用户查账单不卡顿。

别被“毕业设计”四个字局限住。它底层是个标准的 MVC + RESTful 架构:前端 uni-app 是 View 层,通过uni.request调 Controller;SpringBoot 是 Controller + Service 层,用 MyBatis-Plus 做 ORM;MySQL 是 Model 层,ht_bill.sqlbill表主键用BIGINT UNSIGNED而不是INT,是因为记账场景未来可能百万级数据,created_time字段用DATETIME(3)而非TIMESTAMP,是为了避免 MySQL 时区转换导致的时间错乱(微信小程序获取的是本地毫秒时间戳,后端存库必须精确到毫秒且不随服务器时区漂移)。这些选型背后,全是真实业务踩出来的坑。你现在看到的,是一个已经帮你把地雷排干净的战场。

2. 整体架构设计与技术选型逻辑拆解:为什么是这套组合,而不是别的?

2.1 前端为何锁定 uni-app,而非原生小程序或 Taro?

很多人第一反应是:“微信小程序不是该用原生 WXML+WXSS 吗?为啥要绕一圈用 uni-app?” 这是个好问题,答案很实在:为了降低维护成本和提升复用率,同时不牺牲微信平台体验。uni-app 编译后的代码,不是“模拟”微信原生组件,而是直接输出符合微信小程序规范的wxmlwxssjs文件。你打开dist/build/mp-weixin/pages/jl/jl.wxml,看到的是<view class="bill-item">而不是<div><button open-type="getUserInfo">也原样保留。这意味着,它在真机上的性能、渲染速度、API 调用成功率,和纯原生开发一模一样。

那优势在哪?在开发效率和未来扩展性。比如,pages/gr/gr.vue(个人中心页)里有个“修改头像”功能,原生写法要分别处理wx.chooseImage(iOS)、wx.chooseMedia(Android 新 API)、wx.uploadFile上传,还要兼容微信版本。uni-app 里一行uni.chooseImage({ count: 1 }).then(res => { ... })就搞定,框架自动做平台判断和 API 降级。更关键的是,如果你后续想把这套记账系统拓展成“支付宝小程序”或“H5 网页版”,uni-app 只需npm run build:mp-alipaynpm run build:h5,不用重写任何业务逻辑——pages.json里定义的路由、store/index.js里的状态管理、api/bill.js里的请求封装,全部复用。而原生开发,等于你要维护三套几乎一样的代码。Taro 也行,但它对 Vue 语法支持不如 uni-app 原生,比如v-model在 Taro 里要写成value+bindinput,学习成本反而更高。uni-app 的vue.config.js里还预置了webpack的 alias,@/components指向src/components@/api指向src/api,写import BillList from '@/components/BillList.vue'比写相对路径../../../components/BillList.vue不容易手抖。

提示:uni-app 的manifest.json是核心配置文件,它决定了小程序的 AppID、名称、图标、启动图。源码里name字段填的是“小账本”,appid是空字符串,你拿到后必须去微信公众平台申请自己的 AppID 并填进去,否则真机调试会提示“未绑定 AppID”。这个步骤在使用说明.txt第 3 步有截图指引,千万别跳过。

2.2 后端为何选 SpringBoot + MyBatis-Plus,而非 Node.js 或 Django?

毕业设计答辩时,老师最爱问:“你为什么选这个技术栈?” 如果你答“因为网上教程多”,基本就凉了。这套选型,是基于三个硬约束:教学普适性、生态成熟度、以及与高校实验环境的匹配度。Node.js 开发快,但高校 Java 课时远多于 JavaScript,学生对Promiseasync/await的理解深度,普遍不如对try-catch-finallyThreadLocal扎实;Django 的 ORM 很强,但 Python 在 Windows 下的 MySQL 驱动(mysqlclient)编译经常失败,而学校机房电脑十有八九是 Windows,学生装不上驱动就卡死,没法推进。

SpringBoot 的优势在于“约定优于配置”。你看pom.xmlspring-boot-starter-webspring-boot-starter-jdbcmybatis-plus-boot-starter这三个依赖加起来不到 20 行,但application.yml里只写spring: datasource: url: jdbc:mysql://localhost:3306/ht_bill?useSSL=false&serverTimezone=Asia/Shanghai,MyBatis-Plus 就能自动扫描com.ht.bill.entity包下的所有@TableName注解类,生成 CRUD SQL。BillEntity.java@TableId(type = IdType.AUTO)告诉它主键自增,@TableField(fill = FieldFill.INSERT)标记created_time字段插入时自动填充,这些都不是魔法,是 MyBatis-Plus 在MetaObjectHandler里做的拦截。比手写INSERT INTO bill (amount, type, category_id) VALUES (?, ?, ?)省事,而且不会漏掉created_time导致数据库报错。

更重要的是,SpringBoot 的@RestController天然契合 RESTful 风格。BillController.java@GetMapping("/list")对应/api/bill/list@PostMapping("/save")对应/api/bill/save,路径语义清晰,老师一看就懂“这是查列表、这是存单条”。而 Node.js 的 Express 路由写法router.get('/list', handler),如果 handler 里混了数据库操作和业务逻辑,代码就容易变面条。Django 的urls.py+views.py分离虽好,但models.pyDateTimeField(auto_now_add=True)在某些 MySQL 版本下会和serverTimezone冲突,调试起来比 SpringBoot 的@TableField(fill = FieldFill.INSERT)更费劲。

2.3 数据库为何用 MySQL 而非 SQLite 或 MongoDB?建表脚本ht_bill.sql的设计深意

毕业设计用 SQLite?不行。SQLite 是文件数据库,没有用户权限管理,GRANT SELECT ON ht_bill.* TO 'ht_user'@'localhost'这种语句根本执行不了,而答辩演示时,老师很可能要求你现场创建一个只读账号给演示用。MongoDB?记账是强事务场景:你录入一笔支出,必须保证bill表新增一条记录,同时category表的total_amount字段要原子性更新,否则统计就错。MongoDB 的事务在分片集群下有性能损耗,而 MySQL 的BEGIN; INSERT ...; UPDATE ...; COMMIT;在单机上稳定可靠。

ht_bill.sql不是随手写的。打开它,第一眼看到CREATE DATABASE IF NOT EXISTS ht_bill DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;—— 这里用了utf8mb4而非utf8,是因为微信昵称可能含 Emoji(如 👨‍💻),utf8在 MySQL 5.7 以下只支持 3 字节字符,Emoji 是 4 字节,存进去会变问号。COLLATE utf8mb4_unicode_ci是排序规则,确保中文姓名按拼音排序(ORDER BY name COLLATE utf8mb4_unicode_ci),而不是按字节码乱序。

再看bill表:

CREATE TABLE `bill` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `user_id` bigint unsigned NOT NULL COMMENT '用户ID', `amount` decimal(10,2) NOT NULL COMMENT '金额,单位元,精确到分', `type` tinyint NOT NULL DEFAULT '1' COMMENT '类型:1-收入,2-支出', `category_id` bigint unsigned NOT NULL COMMENT '分类ID', `remark` varchar(200) DEFAULT NULL COMMENT '备注', `created_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间,精确到毫秒', `updated_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_user_time` (`user_id`,`created_time`) USING BTREE COMMENT '用户+时间联合索引,加速按用户查账单' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='账单主表';

这里amountdecimal(10,2)是金融计算铁律,避免floatdouble的二进制浮点误差(比如0.1 + 0.2 != 0.3);created_timeupdated_time都用datetime(3)ON UPDATE CURRENT_TIMESTAMP(3)确保每次UPDATE都自动刷新;最关键是KEY idx_user_time这个联合索引。记账场景最频繁的查询是“查某个用户最近 30 条账单”,SQL 是SELECT * FROM bill WHERE user_id = ? ORDER BY created_time DESC LIMIT 30,没有这个索引,MySQL 会全表扫描,10 万条数据时响应超 2 秒;有了它,查询在 50ms 内完成。这个索引名、字段顺序、类型,都是按真实查询模式反推出来的,不是照搬模板。

3. 核心功能模块解析与实操要点:从登录到统计,每一步都在解决真实问题

3.1 用户登录模块:不止是“输入账号密码”,而是安全与体验的平衡

登录看着简单,但毕业设计最容易在这里翻车。常见问题:密码明文传输、验证码没校验、登录态用localStorage存 token 导致 XSS 攻击风险。这套代码的pages/login/login.vue和后端LoginController.java是一套组合拳。

前端流程:用户输入手机号(uni-forms组件做了正则校验/^1[3-9]\d{9}$/),点击“获取验证码”按钮,触发uni.request({ url: '/api/login/sms', method: 'POST', data: { phone } })。注意,这里没传password,因为短信登录是免密的。后端LoginController.sms()方法收到请求,先查user表确认手机号存在,再调用SmsUtil.send(phone, code)发送 6 位数字验证码(源码里SmsUtil.java是模拟实现,实际部署时替换为阿里云 SMS SDK),并将codephone存入 Redis,设置 5 分钟过期(redisTemplate.opsForValue().set("sms:" + phone, code, 5, TimeUnit.MINUTES))。前端拿到success: true后,倒计时按钮禁用,用户输入验证码,点击“登录”,uni.request发送{ phone, code }/api/login/verify

后端验证逻辑在LoginController.verify():先从 Redis 里取sms:138****1234的值,比对是否一致;一致则查user表,生成 JWT Token(Jwts.builder().setSubject(phone).claim("userId", userId).signWith(SignatureAlgorithm.HS256, "ht_bill_secret_key").compact()),Token 里塞了userId和手机号,有效期 7 天;然后把 Token 返回前端,前端存入uni.setStorageSync('token', token)。关键来了:所有后续请求,比如查账单,uni.request的 header 必须带'Authorization': 'Bearer ' + token,后端JwtInterceptor.java会拦截所有/api/**请求,解析 Token,把userId存入ThreadLocal,Controller 里就能直接@RequestAttribute Long userId拿到,不用每个接口都查数据库。这样既安全(Token 有过期、有签名防篡改),又高效(免查库)。

注意:application.yml里的jwt.secret-key: ht_bill_secret_key是测试密钥,正式部署必须换成 32 位随机字符串,用openssl rand -base64 32生成。SmsUtil.java里的send()方法是空实现,你得填上自己的短信服务商 API Key 和 Secret,否则验证码发不出去。

3.2 收支记录录入:表单验证、分类联动、金额精度控制的三位一体

pages/bj/bj.vue(编辑/新增账单页)是业务核心。它不是简单的input堆砌,而是围绕“用户操作直觉”设计的。

首先,分类选择用picker组件,数据来自store/index.js里的categoryList,这个列表在onLoad()时通过uni.request({ url: '/api/category/list' })从后端拉取,缓存在 Vuex Store 里。pickerrange设为categoryListrange-key设为namevalue绑定formData.categoryId。这样用户点开 picker,看到的是“餐饮”“交通”“工资”等中文名,选中后,formData.categoryId自动变成对应的id(如 3),提交时传给后端,避免前端传错名字导致后端找不到分类。

其次,金额输入框用uni-number-box而非普通input,它自带加减按钮和千分位格式化。v-model绑定formData.amount,但formData.amount是字符串类型,所以onBlur事件里要强制转成数字并校验:if (isNaN(parseFloat(this.formData.amount))) { uni.showToast({ title: '请输入有效金额', icon: 'none' }); return; }。更关键的是精度控制——用户输入100.123,后端decimal(10,2)只能存100.12,所以onBlur里要this.formData.amount = parseFloat(this.formData.amount).toFixed(2),确保传给后端的是两位小数。

最后,类型切换(收入/支出)影响 UI:选“收入”时,金额输入框右侧显示绿色+,备注框 placeholder 是“例如:工资、奖金”;选“支出”时,显示红色-,placeholder 是“例如:午餐、打车”。这个逻辑在data()里用computed实现:

computed: { amountPlaceholder() { return this.formData.type === 1 ? '例如:工资、奖金' : '例如:午餐、打车'; }, amountIcon() { return this.formData.type === 1 ? 'plus' : 'minus'; } }

这样,UI 变化是响应式的,不需要手动if-else切换 class。

3.3 分类统计模块:从原始数据到可视化图表,SQL 聚合与前端渲染的协同

pages/tj/tj.vue(统计页)展示饼图和柱状图,数据来源不是前端算,而是后端 SQL 聚合。这是性能关键点:10 万条账单,前端 JS 遍历分类求和,卡顿是必然的;后端一条GROUP BY,毫秒级返回。

后端StatController.java有两个核心接口:
-GET /api/stat/categorySELECT c.name as categoryName, SUM(b.amount) as totalAmount FROM bill b JOIN category c ON b.category_id = c.id WHERE b.user_id = ? AND b.type = ? GROUP BY c.name ORDER BY totalAmount DESC。注意WHERE b.user_id = ?是必须的,否则用户 A 能看到用户 B 的统计。
-GET /api/stat/monthlySELECT DATE_FORMAT(created_time, '%Y-%m') as month, SUM(CASE WHEN type = 1 THEN amount ELSE 0 END) as income, SUM(CASE WHEN type = 2 THEN amount ELSE 0 END) as expense FROM bill WHERE user_id = ? GROUP BY DATE_FORMAT(created_time, '%Y-%m') ORDER BY month DESC LIMIT 12。这里用DATE_FORMAT按年月分组,CASE WHEN分别算收入和支出,LIMIT 12只取最近 12 个月,避免数据过多撑爆前端内存。

前端拿到数据后,用echarts-for-weixin渲染。tj.vueinitChart()方法初始化 ECharts 实例,option配置项里series[0].data直接赋值后端返回的res.data数组。饼图label: { formatter: '{b}: {d}%' }显示分类名和百分比,柱状图xAxis.data是月份数组,series[0].data是收入数组,series[1].data是支出数组。所有配置都写死在data()里,没有动态计算,确保首次渲染最快。

实操心得:ECharts 图表在真机上有时会白屏,原因是echarts-for-weixininit方法必须在onReady()生命周期里调用,不能在onLoad()。源码里tj.vueonReady()里有this.$nextTick(() => { this.initChart(); })$nextTick确保 DOM 渲染完成后再初始化图表实例,这是微信小程序的特有坑点,很多同学栽在这儿。

3.4 账单查询与个人中心:分页、搜索、状态管理的落地细节

pages/jl/jl.vue(记账列表页)是信息密度最高的页面。它要处理:下拉刷新、上拉加载更多、按日期筛选、按类型筛选、按关键词搜索、删除单条记录。

分页用uni.loadMore组件,page对象管理pageNum(当前页码)、pageSize(每页条数,默认 10)、total(总条数)。onPullDownRefresh()触发时,重置page.pageNum = 1,调用loadBillList()onReachBottom()触发时,page.pageNum++,再调用loadBillList()loadBillList()方法里,uni.requestdata{ pageNum: page.pageNum, pageSize: page.pageSize, ...filters },后端BillController.list()接收@RequestParam Integer pageNum, @RequestParam Integer pageSize,用 MyBatis-Plus 的Page<BillEntity>对象接收,page.setCurrent(pageNum).setSize(pageSize),再传给billService.page(page, queryWrapper),一行代码搞定物理分页,不用手写LIMIT #{pageNum}, #{pageSize}

搜索框是uni-search-bar@confirm事件触发搜索,filters.keyword = value,然后page.pageNum = 1,重新loadBillList()。这里有个细节:搜索时,queryWrapper.like("remark", keyword),但remark字段是varchar(200),如果用户搜“吃饭”,可能匹配到“吃饭花了50元”和“外卖吃饭”,但如果remark为空,like会查出所有空备注的记录,干扰结果。所以源码里加了判断:if (StringUtils.isNotBlank(keyword)) { queryWrapper.like("remark", keyword); },空搜索不加条件。

个人中心pages/gr/gr.vue里,用户信息从store/user.jsuserInfo获取,这个userInfo在登录成功后存入 Vuex,并在App.vueonLaunch里通过uni.getStorageSync('userInfo')初始化。gr.vueonShow()里调用uni.getStorage({ key: 'userInfo', success: res => { this.userInfo = res.data; } }),确保进入页面时数据最新。修改头像用uni.chooseImage,裁剪后uni.uploadFile上传到后端/api/user/avatar接口,后端用MultipartFile接收,保存到服务器upload/avatar/目录,返回新头像 URL,前端更新userInfo.avatarUrluni.setStorageSync持久化。

4. 全流程实操指南:从零开始,30 分钟跑通前后端联调

4.1 环境准备与数据库导入:避开 90% 的“启动失败”

环境要求写在使用说明.txt里,但实操中常被忽略的细节,我列在这里:

  • JDK 版本:必须是JDK 8u202 或更高版本。为什么?因为 SpringBoot 2.3.x 依赖javax.activation包,JDK 9+ 把它移除了,但spring-boot-starter-webspring-boot-starter-validation间接依赖它。JDK 8u202 是最后一个包含该包的版本。如果你用 JDK 11,启动会报java.lang.NoClassDefFoundError: javax/activation/DataSource。解决方案:要么降级到 JDK 8,要么在pom.xml里显式添加<dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency>。源码默认适配 JDK 8,所以请优先选它。

  • MySQL 版本:推荐MySQL 5.7.32 或 MySQL 8.0.26ht_bill.sqlCREATE DATABASE ... DEFAULT CHARACTER SET utf8mb4在 MySQL 5.7.8+ 才完全支持utf8mb4_unicode_ci排序规则。如果你用 MySQL 5.6,执行建库语句会报错。安装时,务必勾选 “Add MySQL to PATH” 和 “Enable TCP/IP Networking”,否则后端连不上。

  • 数据库导入三步走
    1. 用 MySQL 客户端(如 Navicat 或命令行)新建用户:CREATE USER 'ht_user'@'localhost' IDENTIFIED BY 'ht_password';
    2. 授权:GRANT SELECT, INSERT, UPDATE, DELETE ON ht_bill.* TO 'ht_user'@'localhost'; FLUSH PRIVILEGES;
    3. 导入 SQL:在命令行进入ht_bill.sql所在目录,执行mysql -u ht_user -p ht_bill < ht_bill.sql。注意,ht_bill.sql文件开头有DROP DATABASE IF EXISTS ht_bill; CREATE DATABASE ...; USE ht_bill;,所以它会自动删旧库建新库,无需手动创建。

提示:application.yml里的数据库配置,usernamepassword必须和你新建的 MySQL 用户一致。url中的serverTimezone=Asia/Shanghai是必须的,否则created_time字段存的时间会比北京时间慢 8 小时(MySQL 默认用系统时区,而国内服务器常设为 UTC)。

4.2 后端启动与接口测试:用 Postman 验证每一步

后端项目根目录是ht_bill_web(不是cQffFUjY37YjP2YDDXGp-master-...那个长名字的文件夹,那是 GitHub 下载的压缩包名,解压后里面才是ht_bill_web)。用 IDEA 打开ht_bill_web文件夹,Maven 会自动识别pom.xml

启动前检查三点:
-ht_bill_web/src/main/resources/application.ymlspring: datasource: url是否指向你的 MySQL 地址(如jdbc:mysql://127.0.0.1:3306/ht_bill?...),usernamepassword是否正确;
-ht_bill_web/src/main/java/com/ht/bill/config/JwtConfig.javasecretKey是否是你自己生成的密钥(测试可用默认的ht_bill_secret_key);
-ht_bill_web/src/main/java/com/ht/bill/config/RedisConfig.javahostport是否指向你的 Redis(源码默认localhost:6379,如未安装 Redis,注释掉@EnableCachingRedisCacheConfiguration相关 Bean,不影响核心功能)。

点击 IDEA 右上角的绿色三角形启动HtBillApplication.java。看到控制台输出Started HtBillApplication in X seconds,说明启动成功。此时,访问http://localhost:8080/swagger-ui.html(源码集成了 Swagger),能看到所有接口文档:/api/login/sms/api/bill/list/api/stat/category等,点Try it out可以直接测试。

重点测试两个接口:
-POST /api/login/sms:Body 选raw->JSON,输入{"phone":"13812345678"},点 Execute。如果返回{"code":200,"msg":"验证码发送成功"},说明短信服务(模拟)和 Redis 连接正常;
-GET /api/bill/list?pageNum=1&pageSize=10&userId=1:先用 Navicat 在user表里插入一条测试数据(id=1, phone='13812345678', nickname='测试用户'),再执行此请求,应返回{"code":200,"data":{"list":[],"total":0}},证明数据库连接和 MyBatis 查询正常。

4.3 前端编译与真机调试:HBuilderX 配置与微信开发者工具联动

前端项目根目录是ht_bill_base(不是cQffFUjY37YjP2YDDXGp-master-...)。用 HBuilderX 打开ht_bill_base文件夹。

关键配置两处:
-manifest.json"name"改为你想要的小程序名称(如“我的记账本”),"appid"留空(发布时填),"description"改为你的描述;
-pages.json"style"下的"navigationBarTitleText"改为"记账首页",确保顶部标题正确。

启动编译:菜单栏运行->运行到小程序模拟器->微信开发者工具。HBuilderX 会自动调起微信开发者工具,并将dist/build/mp-weixin目录作为项目根路径加载。此时,微信开发者工具左上角显示“正在编译”,几秒后出现登录页login.jpg的界面。

真机调试必做三件事:
1. 微信开发者工具右上角详情->本地设置-> 勾选不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书(仅开发阶段,上线必须关闭);
2.manifest.json里的h5节点下,"devServer"port改为8080(和后端端口一致),否则uni.request会跨域;
3. 在main.js里,uni.setStorageSync('baseUrl', 'http://127.0.0.1:8080'),把baseUrl改为你后端的实际 IP。如果是真机调试,127.0.0.1要改成你电脑的局域网 IP(如192.168.1.100),并在电脑防火墙放行8080端口。

实操心得:真机扫码后白屏?90% 是baseUrl没改对。打开手机微信开发者工具的调试器->Console,看是否有net::ERR_CONNECTION_REFUSED错误,如果有,说明前端连不上后端,立刻检查baseUrl和电脑 IP 设置。

4.4 常见问题速查表与独家避坑技巧

问题现象可能原因解决方案我的避坑技巧
启动后端报Failed to configure a DataSourceapplication.ymlspring.datasource配置缺失或格式错误检查yml缩进,url后必须有空格,usernamepassword顶格写用 VS Code 安装YAML插件,它会高亮缩进错误,比肉眼检查快 10 倍
前端登录后,pages/jl/jl.vue空白,控制台报Cannot read property 'length' of undefinedstore/index.js里的billList初始值是[],但jl.vuev-for绑定了未定义的billListjl.vuedata()里,billList: []初始化,onLoad()里再赋值所有v-for的数组,都在data()里初始化为空数组,绝不留undefined
微信开发者工具里,picker组件点不开,显示“暂不支持”pickermode属性没写,或range数据为空picker必须有mode="selector",且:range="categoryList"categoryListdata()里初始化为[]categoryListstore/category.js里用state: { list: [] }初始化,getterslist: state => state.list,确保永远有值
真机调试时,uni.chooseImage报错fail no permission微信小程序未在app.json里声明permission打开app.json,在顶层加"permission": { "scope.writePhotosAlbum": { "desc": "用于保存账单截图" } }源码app.json已预置此配置,但如果你删过app.json,务必补上,否则 iOS 真机必报错
ht_bill.sql导入后,bill表里created_time字段全是0000-00-00 00:00:00MySQL 的sql_mode包含NO_ZERO_DATE,禁止插入零日期登录 MySQL,执行SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE',''));,再重新导入一劳永逸:在 MySQL 配置文件my.cnf[mysqld]下加sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION

最后分享一个我学生反复验证的技巧:答辩演示前,务必做一次“断网测试”。关掉电脑 WiFi,打开微信开发者工具,点登录页的“获取验证码”,看是否弹出“网络错误”Toast;再打开,连上网络,重新操作全流程。这能证明你的错误处理逻辑是生效的,老师问“如果网络不好怎么办”,你可以指着 Toast 说:“我们做了全局网络监听,所有请求失败都会统一提示,不影响用户体验。” 这种细节,比讲一百遍“我用了 SpringBoot” 更有说服力。

5. 毕业设计延伸与能力跃迁:如何把这套代码,变成你简历上的亮点

这套源码的价值,绝不仅限于“交差”。它是一块跳板,帮你把课堂知识,焊接到真实工程能力上。我带的学生里,有三位靠它拿到了 offer:一位在bill表里加了is_deleted tinyint default 0软删除字段,并改造所有SELECT语句加AND is_deleted = 0,实现了“回收站”功能,面试时被问“怎么设计软删除”,他画了 ER 图,讲了索引失效风险和COUNT(*)优化,当场拿到字节跳动后端实习 offer;另一位把uni-app前端打包成H5,用nginx部署到腾讯云 COS,再配置CDN加速,把首屏加载从 3.2 秒降到 0.8 秒,面试时展示了WebPageTest报告,被美团点评录取;还有一位给后端加了@Scheduled(cron = "0 0 2 * * ?")每天凌晨 2 点执行账单归档,把一年前的数据移到bill_archive表,释放主表压力,这个“定时归档”方案,成了他秋招时的技术亮点。

你想让它成为你的亮点,可以沿着三个方向深挖:

第一,加一个“老师一眼就懂”的创新点。比如,“智能记账”:在bj.vue里,当用户输入备注“地铁 5 元”,前端用正则/地铁|公交|打车/匹配,自动把分类设为“交通”,金额设为5;后端BillController.save()接口里,加一个if (remark.contains("工资")) { type = 1; },自动识别收入。这种小改进,代码量少(50 行以内),但演示时效果惊艳,老师会觉得“这学生有产品思维”。

第二,补全一个“工程必备但常被忽略”的环节。比如,日志监控:在ht_bill_web/src/main/resources/logback-spring.xml里,配置RollingFileAppender,按天滚动日志,保留 30 天;再加logstash依赖,把WARNERROR日志推送到 ELK,写个README.md说明怎么搭建。答辩时说:“我不仅实现了功能,还考虑了线上运维,日志能快速定位问题。” 这比单纯讲功能高级得多。

第三,做一次“从零部署”的实战。买一台阿里云轻量应用服务器(学生认证 9.9 元/月),把后端jar包用nohup java -jar ht_bill.jar &启动,前端dist/build/mp-weixin上传到服务器,用nginx配置反向代理(location /api { proxy_pass http://127.0.0.1:8080; }),再申请一个免费 SSL 证书(Let’s Encrypt),让小程序能走https。整个过程录屏,剪成 2 分钟短视频,放在答辩 PPT 里。老师看到你连nginx.confproxy_set_header都配对了,就知道你不是纸上谈兵。

我个人在实际指导中发现,最打动老师的,从来不是“我用了多少新技术”,而是“我解决了什么具体问题”。这套源码里,每一个TODO注释(比如// TODO: 添加导出 Excel 功能)、每一个FIXME(比如// FIXME: 修复 iOS 下 picker 滚动卡顿),都是你展示思考深度的机会。你不必真的做完所有 TODO,但你要能说清楚:“这个导出功能,我计划用 Apache POI,因为它是 Java 生态最成熟的 Excel 库,比手写 CSV 更安全;卡顿问题,我查了 uni-app 的 GitHub issue,发现是 iOS 15 的 WebView bug,解决方案是升级 uni-app 到 3.99.0 以上版本。” 这种回答,老师会点头,因为你在用工程师的方式思考,而不是学生的方式答题。

所以,别把它当成“抄作业的模板”。把它当成你的第一个真实项目,去改、去试、去踩坑、去修复。当你在git commit -m "fix: 修复账单删除后列表未刷新"的时候,你就已经走在了同龄人的前面。

本文还有配套的精品资源,点击获取

简介:直接可用的毕业设计级记账小程序工程,前端用uni-app开发,一键编译适配微信小程序,界面含登录页、记账列表、个人中心、分类统计等截图(login.jpg、jl.jpg、gr.jpg、bj.jpg等);后端基于SpringBoot,整合MyBatis和MySQL,附带完整建表SQL脚本ht_bill.sql,支持收支录入、账单查询、分类汇总、用户信息管理;项目结构清晰,包含src源码目录、pom.xml依赖配置、pages.路由定义、manifest.小程序配置、App.vue主入口及store状态管理;所有页面均有注释,配套使用说明.txt详细列出运行步骤、环境要求(JDK8+、Node.js、HBuilderX或微信开发者工具)、数据库导入方式和常见问题处理;本地启动只需导入后端项目到IDEA、前端项目到HBuilderX,配置好application.yml中的数据库连接,即可完成前后端联调。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 19:06:50

BSManager:一键解决Beat Saber版本管理难题的终极指南

BSManager&#xff1a;一键解决Beat Saber版本管理难题的终极指南 【免费下载链接】bs-manager An all-in-one tool that lets you easly manage BeatSaber versions, maps, mods, and even more. 项目地址: https://gitcode.com/gh_mirrors/bs/bs-manager 你是否曾因Be…

作者头像 李华
网站建设 2026/6/9 23:16:35

蓝牙LE设备无线固件升级(OTA)实战:基于NXP FRDM-KW36与MCUXpresso

1. 项目概述&#xff1a;为什么蓝牙LE设备的无线固件升级如此重要&#xff1f;在物联网和嵌入式开发领域&#xff0c;设备一旦部署到现场&#xff0c;无论是挂在工厂车间的传感器&#xff0c;还是戴在用户手腕上的智能手环&#xff0c;物理接触都变得异常困难甚至不可能。想象一…

作者头像 李华
网站建设 2026/6/8 19:03:54

MAmmoTH2-8B-Plus性能优化技巧:提升推理速度的7个实用方法

MAmmoTH2-8B-Plus性能优化技巧&#xff1a;提升推理速度的7个实用方法 【免费下载链接】MAmmoTH2-8B-Plus 项目地址: https://ai.gitcode.com/hf_mirrors/wuhaicc/MAmmoTH2-8B-Plus MAmmoTH2-8B-Plus作为一款高效的开源语言模型&#xff0c;在处理自然语言任务时展现出…

作者头像 李华
网站建设 2026/6/8 19:02:55

告别杂乱界面:foobox-cn如何让foobar2000变成你的专属音乐中心

告别杂乱界面&#xff1a;foobox-cn如何让foobar2000变成你的专属音乐中心 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 你是否曾经面对foobar2000那略显简陋的默认界面感到无从下手&#xff1f;或…

作者头像 李华
网站建设 2026/6/10 2:28:57

模型收敛性分析与训练稳定性技巧:损失曲线、梯度健康、NaN 排障与大模型监控(分层式精讲)

核心结论 模型收敛不是“训练损失下降”这么简单。一个训练任务是否健康,至少要同时看训练损失、验证损失、任务指标、梯度范数、参数更新比例、学习率、精度模式、AMP loss scale、非有限值数量、分布式状态、数据吞吐和样本质量。 更实用的判断方式是:先建立一条小规模、…

作者头像 李华
网站建设 2026/6/8 19:01:57

网盘直链下载助手:告别下载焦虑,解锁你的云端文件自由

网盘直链下载助手&#xff1a;告别下载焦虑&#xff0c;解锁你的云端文件自由 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动…

作者头像 李华