SpringBoot3配合Vue3做在线捐赠系统,是我这几年带实训和帮同学改毕业设计时用得最顺手的项目类型之一。整条业务链从项目发布、用户浏览、模拟捐赠、记录回显到后台统计,正好把前后端分离、RESTful接口、数据库设计、权限控制、事务处理这些核心技能点串成了一条线。更难得的是,这个选题天然带公益属性,把“乡村儿童阅读角”“社区老人关怀”这类项目放进去,展示起来比常见的图书管理系统更有感染力。
针对零基础的同学,我的建议是把这套项目当成一条完整的练习链路,而不是一个只能“跑起来看效果”的模板。你不需要一上来就背语法,先跟着环境准备步骤把项目启动,再沿下面的功能拆解从头到尾走一遍,这套技术栈的常用套路基本就清楚了。它适合用来做毕业设计、课程作业,也适合放进自己的实训项目合集里做技术展示,后续想扩展成其它公益平台的原型也顺理成章。
1. 在线捐赠系统的整体设计与功能拆解
1.1 为什么这个选题值得做
说句实话,历年毕设和实训里最多的就是“某某管理系统”。图书管理、学生管理、仓库管理,代码逻辑大同小异,答辩时也容易被老师追问“你的系统核心难点在哪里”。在线捐赠系统不一样,它虽然还是典型的Web业务系统,但业务流程更接近真实产品:有用户注册登录,有公益项目状态流转,有金额精度控制,有模拟支付的交互状态,还有后台数据汇总。把这些细节讲清楚,就能证明你不只是在堆积增删改查一类的基础功能。
在线捐赠系统的业务闭环可以这样理解:公益团队发布一个需要筹款的项目,填写目标金额和项目介绍;普通用户注册登录后查看项目,在项目详情页填捐款金额、留言,选择模拟支付方式完成支付;系统记录这笔捐款,同时把项目已筹金额累加;用户可以在“我的捐赠”里看到历史记录,管理员在后台能看到全部记录和简单的统计数字。这个过程很像一个简化版的电商下单流程,但要处理的状态切换、并发问题少得多,非常适合零基础上手。
另外,项目的社会意义也是加分项。捐赠系统的数据天然适合讲故事:你可以用首页顶部的大数字展示“累计善款”“捐赠人次”“受益群体”,再配上项目卡片列表,视觉效果比传统管理系统好很多。演示和答辩时,老师关心的是你对业务的理解、代码架构的合理性、异常情况怎么处理,这套项目每个点都能接得住。
1.2 角色、功能模块与页面规划
整个系统按照使用角色可以拆成三块:游客、普通用户、管理员。
| 角色 | 核心功能 | 是否需要登录 |
|---|---|---|
| 游客 | 查看首页统计数据、项目列表、项目详情 | 不需要 |
| 普通用户 | 注册、登录、在线捐赠、我的捐赠记录、个人累计金额 | 需要 |
| 管理员 | 维护项目、查看全部捐赠记录、查看统计报表 | 管理员账号 |
用户端页面我建议这样规划:首页是项目概览加顶部统计卡片;项目详情页显示封面、目标金额、已筹金额、进度条和捐赠按钮;捐赠表单独立做成弹窗或单独页面;我的捐赠页展示捐赠列表和累计金额。管理端可以复用同一套前端,通过路由区分,页面是项目表格、捐赠记录表格、统计卡片三块。
这样的页面数量不多,但每一页都有明确的业务目的,能覆盖Vue3开发里最常用的列表渲染、表单校验、状态切换、组件通信等技能。我自己在实训中给学生的任务就是先把页面原型画出来,再对照功能表把接口名写出来,最后才动手写代码,整个过程会顺畅很多。
2. 技术选型与开发环境准备
2.1 SpringBoot3加Vue3为什么适合零基础
技术选型方面,这套项目用的是SpringBoot3、Vue3、MySQL,前端构建工具是Vite,UI组件库用开源生态里常见的组件库,后端持久层用MyBatis-Plus。选这个组合,一是因为它是目前前后端分离方向的主流搭配,资料多、社区活跃,遇到问题基本搜得到答案;二是因为它比老一套的SSM加Vue2更简洁,版本更新意味着语法更现代化,学完直接能衔接到企业级项目。
SpringBoot3这里要特别提醒:它要求JDK必须是17及以上。很多同学机子上装的是JDK8,启动项目会直接报出class文件主版本号不支持的错误。解决办法很简单:装一个JDK17,并且确认系统环境变量里的JAVA_HOME指向新版本。这个点我会在后面的环境清单里再强调一次。
前端选Vue3而不是Vue2,最大的变化是组合式API。用setup语法糖写组件,有点像在写互相调用的函数,逻辑复用很直观。再搭配Pinia做状态管理,登录信息、用户信息放在一个全局状态里,组件之间共享数据不需要一层层传props,代码干净不少。Vite比Webpack的启动速度快非常多,改代码之后页面几乎瞬时刷新,对零基础特别友好,省掉了大量等待时间。
2.2 需要准备的软件与版本清单
我整理了下面这张环境清单,照着装就行。版本号尽量别乱改,很多问题都出在版本不匹配上。
| 软件 | 推荐版本 | 用途 | 安装后验证命令 |
|---|---|---|---|
| JDK | 17或17以上 | 运行后端项目 | java -version |
| Maven | 3.6以上 | 管理后端依赖 | mvn -v |
| MySQL | 8.0 | 存储数据 | mysql --version |
| Node.js | 18以上 | 运行前端构建工具 | node -v |
| 前端包管理器 | npm(随Node自带) | 安装前端依赖 | npm -v |
| 数据库客户端 | 主流图形客户端或命令行 | 导入SQL、查看数据 | 无 |
安装完成后,强烈建议在命令行里逐条执行表格中的验证命令。如果你敲java -version显示的不是17,说明环境变量还没生效,先检查哪里装了旧版本,再修改JAVA_HOME和PATH。零基础阶段花十分钟完成工具验证,能省掉后面启动项目时一大半的报错。
2.3 前后端分离的目录结构规划
拿到项目先别急着启动,先看懂目录。我的习惯是让前后端完全分开,同一份仓库里放两个子目录。
online-donation ├─ backend │ ├─ pom.xml │ └─ src/main │ ├─ java/com/example/donation │ │ ├─ controller # 接口层 │ │ ├─ service # 业务逻辑层 │ │ ├─ mapper # 数据访问层 │ │ ├─ entity # 实体类 │ │ └─ config # 配置类 │ └─ resources │ └─ application.yml # 数据库等配置 └─ frontend ├─ vite.config.js # 构建与代理配置 ├─ package.json └─ src ├─ main.js ├─ router # 路由 ├─ store # Pinia状态 ├─ api # Axios请求封装 ├─ utils # 工具函数 └─ views # 页面组件这个结构对应的是典型的分层思想:后端Controller只负责接收请求、返回结果,业务逻辑集中在Service层,数据操作放在Mapper层;前端按路由和功能目录组织,api目录统一管理所有后端请求。零基础学习时,最大的好处是出了问题定位快:页面报错先看views,接口报错看api和后端Controller,数据不对看数据库和Mapper。
3. 数据库设计与后端核心接口实现
3.1 核心表结构与字段设计思路
在线捐赠系统最核心的就是三张表:捐赠人表donor、项目表project、捐赠记录表donation_record。表名刻意用donor而不是user,是为了避开数据库里的保留字和潜在冲突。先把建表SQL列出来。
CREATE TABLE donor ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE project ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, cover VARCHAR(255), description TEXT, target_amount DECIMAL(10,2) NOT NULL, raised_amount DECIMAL(10,2) NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 0 COMMENT '0筹集中 1已完成 2已下线', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE donation_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, donor_id BIGINT NOT NULL, project_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, message VARCHAR(200), pay_type VARCHAR(20), donate_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_donor (donor_id), INDEX idx_project (project_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有两个细节值得零基础同学记一下。第一,金额为什么用DECIMAL(10,2)而不是FLOAT或DOUBLE?因为浮点数在计算机里是二进制近似表示,累计多次之后可能出现精度误差,而捐款涉及金额,一分钱都不能差,DECIMAL是精确小数,适合存货币。第二,donation_record表里没有设计物理外键,只加了普通索引。项目中通常用逻辑关联,在代码里保证id的对应关系,这样删除项目或用户时不容易被数据库的约束卡住,适合学习项目,也方便以后扩展。
3.2 后端分层与捐赠业务的核心逻辑
后端的包结构已经写在目录规划里,这里直接看捐赠接口是怎么落地的。先说流程:用户提交捐赠请求,后端先校验项目和金额,然后模拟支付成功,接着在当前事务里完成两件事——插入捐赠记录、累加项目已筹金额。Service层核心代码如下。
@Service public class DonationServiceImpl implements DonationService { @Resource private DonationRecordMapper donationRecordMapper; @Resource private ProjectMapper projectMapper; @Override @Transactional(rollbackFor = Exception.class) public DonationRecord donate(DonationDTO dto) { Project project = projectMapper.selectById(dto.getProjectId()); if (project == null || project.getStatus() != 0) { throw new RuntimeException("项目不存在或已结束捐赠"); } if (dto.getAmount() == null || dto.getAmount().doubleValue() <= 0) { throw new RuntimeException("捐赠金额必须大于0"); } DonationRecord record = new DonationRecord(); record.setDonorId(dto.getDonorId()); record.setProjectId(dto.getProjectId()); record.setAmount(dto.getAmount()); record.setMessage(dto.getMessage()); record.setPayType(dto.getPayType()); donationRecordMapper.insert(record); project.setRaisedAmount(project.getRaisedAmount().add(dto.getAmount())); projectMapper.updateById(project); return record; } }这段代码的关键就是@Transactional注解。你可以把它理解成一个统一开关:方法内所有数据库操作,要么全部成功提交,要么遇到异常全部回滚。如果没有事务,一旦插入捐赠记录成功、更新项目金额失败,就会出现“用户捐了钱但项目进度没变化”的数据不一致,这在真实业务里是不能接受的。SpringBoot默认开启了事务管理,但业务方法上必须显式加这个注解。
Controller层相对简单,只做参数接收和结果返回。再用一个Result对象统一包一层code和message,前端判断code为200就说明成功。接口路径我习惯设计为POST /api/donation,符合RESTful风格,资源是捐赠记录,语义清晰。
3.3 模拟支付如何设计才显得完整
很多同学会纠结要不要接真实支付,我的建议是学习阶段完全不用。真实支付需要商户资质、密钥管理、回调验签、退款流程,个人基本办不下来,答辩时讲清楚也容易跑偏。模拟支付的目的是把“支付成功后的业务处理”完整走通。
常用模拟方案是:前端点击捐赠后,弹出一个模拟支付框,让用户选择一种支付方式,点击“确认支付”后按钮进入loading状态,延迟800毫秒再调后端生成捐赠记录接口。这样前端可以演示加载状态、防止重复提交,后端照常校验、写库、更新金额。整个流程在业务层面和真实支付几乎一致,只是没有真实扣款。代码里要处理好同一用户重复提交的限制,最简单的做法是前端在loading期间禁用按钮,后端再做一次幂等判断,防止用户手抖连点产生两条记录。
4. 前端Vue3页面实现与接口对接
4.1 路由设计与登录权限拦截
前端页面用Vue Router配合Pinia来实现。Pinia存两样东西:当前用户信息和token。token在登录成功后由后端返回,前端存到localStorage里,刷新页面后还能保持登录状态。
先看路由设计:
const routes = [ { path: '/', name: 'home', component: HomePage }, { path: '/login', name: 'login', component: LoginPage }, { path: '/project/:id', name: 'projectDetail', component: ProjectDetail, meta: { requiresAuth: true } }, { path: '/my', name: 'myDonations', component: MyDonations, meta: { requiresAuth: true } }, { path: '/admin', name: 'admin', component: AdminDashboard, meta: { requiresAuth: true, role: 'admin' } } ]路由守卫是零基础同学最容易忽略却又很重要的部分。没有守卫的话,游客直接输入地址也能进到项目详情页或者后台。用全局前置守卫控制:
router.beforeEach((to) => { const authStore = useAuthStore() if (to.meta.requiresAuth && !authStore.token) { return { name: 'login', query: { redirect: to.fullPath } } } if (to.meta.role && authStore.role !== 'admin') { return { name: 'home' } } })这样未登录用户访问捐赠详情、我的捐赠、管理后台时,会被统一踢回登录页,登录成功后再跳回原目标页面。逻辑简单,但答辩和作品展示时能体现出你对信息安全的意识。
4.2 Axios封装、环境变量与跨域代理
前后端分离开发最常遇到的一个坑就是跨域。前端端口5173、后端端口8080,浏览器会拦截跨域请求,解决办法是前端通过Vite的proxy把接口请求转发给后端。这样前端代码里请求地址只要写/api开头,本地开发完全不用在后端单独放开跨域配置。
// vite.config.js server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }同时把Axios封装成统一模块,加入请求拦截器和响应拦截器。
import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config }) request.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') location.href = '/login' } return Promise.reject(error) } )统一封装的好处是,以后所有接口都自动携带token,遇到登录过期也只需要改拦截器一处地方。零基础先不要急着把每个请求写一遍headers,用这种中央处理的方式最省事。
4.3 项目列表、进度条与捐赠表单实现
项目列表页是前端Vue3最典型的场景:请求接口拿数据,循环渲染卡片。核心逻辑如下:
<script setup> import { ref, onMounted } from 'vue' import { getProjectList } from '../api/project' const projects = ref([]) const loading = ref(false) const loadProjects = async () => { loading.value = true const res = await getProjectList() projects.value = res.data.records loading.value = false } onMounted(loadProjects) </script>每张卡片显示封面、标题、目标金额、已筹金额和进度条。进度百分比直接用raised / target计算,渲染成组件库里的进度条组件。这个计算逻辑本身很简单,但要注意后端返回的金额是字符串还是数字,如果用字符串直接除可能得到奇怪结果,前端建议先转成Number再算。
捐赠表单最关键的是校验规则。金额不能为空、必须大于0、不能超过单笔上限;留言不能超过200字。用组件库自带的表单校验加一条自定义规则,防止用户传负数或0。提交前再把按钮设成loading状态,提交成功后清空表单并跳转到我的捐赠页。整个过程既演示了表单交互,也演示了前端到后端的完整联调。
5. 部署运行与高频问题排查
5.1 从零把项目跑通的十二个步骤
这部分是零基础同学最容易卡住的地方,我直接按顺序写一遍可操作的清单,每步都给了判断标准。
- 安装JDK17,配置JAVA_HOME。命令行执行
java -version,确认显示的是17。 - 安装MySQL8,记住自己设置的root密码。启动MySQL服务后,用客户端工具连接测试一下。
- 创建数据库donation,字符集选utf8mb4,把项目SQL文件整体导入,确认三张表都建好了。
- 打开后端项目,修改application.yml里的数据库地址、用户名、密码,对齐本机环境。
- 启动后端主类。如果日志里看到Tomcat started on port 8080,说明后端启动成功。
- 在浏览器直接访问一个公共接口,例如
http://localhost:8080/api/project/list,能返回JSON数据就说明Controller和数据库关联正常。 - 安装Node.js18以上版本,执行
node -v和npm -v确认可用。 - 在frontend目录执行
npm install。这里如果慢,可以临时换成镜像源。 - 执行
npm run dev,看到Local: http://localhost:5173就完成了前端启动。 - 打开前端地址,注册一个测试账号并登录。
- 用一个管理员账号登录后台,在项目管理里新增一个测试项目,填标题、目标金额和介绍。
- 回到前台,选择这个项目进行一次模拟捐赠。到“我的捐赠”里确认记录出现,再到后台看累计金额是否更新。
这十二步里,任何一步失败的原因基本都能在下一节找到。零基础不要跳步,尤其建议每启动一个环节就先验证上一个环节的数据。
5.2 高频问题排查速查表
我把实际带项目时学生踩得最多的坑整理成了一张表,照着排查效率最高。
| 现象 | 常见原因 | 解决办法 |
|---|---|---|
| 后端启动报class文件主版本错误 | JDK版本低于17 | 升级JDK并修改JAVA_HOME |
| 前端请求接口返回网络错误 | 跨域或代理没配置 | 检查vite.config.js中的proxy配置 |
| 数据库连接失败 | URL、账号或密码写错 | 对照application.yml逐项检查,注意数据库名是否创建 |
| 启动后表不存在 | SQL没有导入当前数据库 | 重新执行建表SQL,确认在donation库中执行 |
| 捐赠后金额没有增加 | 扣了款但update没执行 | 检查service方法是否有@Transactional,以及updateById是否传入完整对象 |
| 前端页面样式全乱 | UI组件库未完整引入 | 按组件库文档确认全局引入或按需插件配置 |
| npm install报错 | Node版本过低或网络问题 | 升级Node;换镜像源后重试 |
| 登录后停留登录页 | 路由守卫逻辑写错或token没存上 | 在守卫里打印authStore.token,先确认localStorage是否写入 |
这里面最容易被忽略的是“donation表插入成功但金额没更新”,通常是项目表更新那一行代码没写,或者事务方法没有被Spring管理。排查思路是看看Controller调用的方法真的在被Spring扫描的包路径下,且ServiceImpl上有@Service注解。
5.3 作为毕业设计或实训项目答辩的加分细节
项目能跑通只是底线,答辩和作品展示的加分点其实是“思考深度”。建议提前准备下面几点。
文档方面,写一个简洁的README,把技术栈、功能清单、运行步骤、账号说明写清楚。数据库设计图建议单独画一张ER图,不需要复杂,三张表的主外键关系和字段说明列出来即可。演示时先展示项目启动,再从用户视角走一遍“浏览、登录、捐赠、查记录”,最后切到管理后台看统计。整个过程控制在五分钟以内,重点讲清你如何解决金额精度、事务一致性和登录权限这几个问题。
扩展方向上,可以做项目审核状态机,给项目增加待审核、审核通过、拒绝三种状态;也可以导出捐赠记录Excel;还可以把数据统计做成可视化图表。这些方向都能作为“后续规划”在答辩里提,表明你不是只会照着模板改代码。
这套项目完整走通之后,我的体会是:它真正训练的不是某个框架的API,而是拿到一个业务需求后怎么拆解、怎么建模、怎么保证数据不出错。捐赠流程里的金额校验、事务回滚、进度更新,放到电商、教育、办公系统里同样是那套思路。我把这个项目反复讲给实训的同学,最常说的就是一句话:别急着抄代码,先把“谁在用、流程怎么走、数据落在哪张表”三个问题想清楚。
最后再分享一个自己的小习惯:拿到任何项目源码,我第一步一定是先把建库SQL跑通,再去验证后端接口,最后才连前端页面。顺序对了,后面全是顺畅的验证;顺序乱了,前端报错时你根本分不清是接口问题、跨域问题还是数据库没导全。这套在线捐赠系统作为零基础入门项目,最大的价值就是把“环境、数据、接口、页面”这四个层次拆得清清楚楚,每走一层你都能明确知道自己到了哪一步。