☰
校园爱心捐赠互助管理系统:从设计到答辩完整指南
2026/10/22 10:56:32 网站建设 项目流程

简介:面向计算机相关专业毕业生的校园爱心捐赠互助管理系统毕业设计资料包,基于Web技术实现,覆盖需求分析、数据库设计、系统实现与测试等完整开发流程。系统包含用户注册、在线捐赠、义卖商品购买、物品申请、个人后台、系统后台管理、义卖商品管理与购买管理等核心模块,契合高校公益互助场景。压缩包整体约43.99MB,以系统源码、数据库脚本及说明文档为主要内容;说明文档详细记录了需求调研、技术/经济/操作可行性分析、数据库表结构设计、各功能模块实现与系统测试的完整过程。该资料目前已有309人浏览学习,适合需要快速完成毕业设计或了解同类管理系统开发流程的同学,也可直接用于系统二次开发与论文撰写参考。读者可从中获得完整项目源码、数据库设计思路及遵循软件工程规范的文档模板,对提升动手实践能力和毕业论文质量均有切实帮助。

1. 校园爱心捐赠互助管理系统到底在做什么:先看一个完整业务闭环

很多同学拿到“基于Web的校园爱心捐赠互助管理系统”这个题目时,第一反应是“做个发帖网站”,结果导师一句“业务逻辑不完整”就打了回来。这个标题真正要解决的是三件事:学生可以发布闲置物品捐赠、困难同学可以发起互助申请、管理员需要审核并统计全过程。我见过最典型的翻车案例是只做了“发布展示”和“后台管理”两块,互助申请完全没做,整个系统看起来像二手交易平台而不是爱心捐赠平台。这个项目的核心价值在于“捐赠—匹配—审核—去向反馈”的完整闭环,适合那些想用一套常规技术栈完成一个业务逻辑完整、能演示能答辩的毕设课题。接下来我会按照“模块设计→技术选型→代码实现→踩坑排查→答辩优化”的顺序,把这个系统从头到尾拆给你看。

2. 核心模块与数据库设计:先把业务边界画清楚,再动手写代码

2.1 三个角色:学生、管理员、审核员的权限边界

这个系统最常见的角色划分是三类,而不是网上很多Demo里只有“用户和管理员”两类。学生角色负责捐赠发布、互助申请、查看自己相关的记录;审核员负责审核捐赠物品和互助申请是否真实、信息是否完整;管理员负责用户管理、数据统计、把捐赠去向做最终确认。我建议你不要把审核员和管理员合并成一个角色,因为合并之后,你很难在答辩时解释“捐赠物品由谁来把关”这个问题,而审核员正是这个系统和普通论坛帖子的核心区别。

权限边界可以用一张表固定下来:学生能看自己的捐赠单和申请单,能修改未审核状态的单子;审核员能审所有捐赠单和申请单,但不能改用户密码;管理员能分配审核员、查看全局统计、处理异常状态。实现时用拦截器加角色判断即可,不用引入Spring Security那种重型权限框架,但角色字段必须进数据库。我在评审过的一些毕设中看到过把角色写死在登录代码里的做法,管理员从配置文件里读,结果换台电脑启动就找不到角色,这种设计在答辩现场非常容易被追问。

2.2 数据模型:捐赠记录、互助申请、物品明细的关联关系

这个系统至少需要七张表:用户表、角色表、捐赠表、捐赠物品明细表、互助申请单表、审核记录表、系统日志表。很多新手只做“一张用户表、一张捐赠表、一张申请单表”,等做到统计“每月捐赠物品分类占比”时,发现物品类型只能塞在一列里,聚合SQL写得痛苦不堪。我这里说的物品明细表是指一件捐赠可能包含多件物品,例如“一箱旧书加一个书包”,那么捐赠表和物品明细表是一对多关系;互助申请单则是学生申请获取某一样或几样物品,和物品明细表是弱关联,用需求描述字段即可。

另外,审核记录表不要省略。它保存审核员、审核时间、审核结论和备注。后期做“最近一周待审核数量”“平均审核时长”这些统计时,你会发现这张表的价值极大。如果按“一个状态字段”来跟踪,只能看最终结果,看不到过程数据。我自己的习惯是:状态字段照留,但所有状态变化都插一条审核记录,这样既简单又能回答“为什么这个捐赠被退回”的用户疑问。

2.3 数据库脚本:建库、建表与初始化数据,直接可执行

下面是这套系统最基础的建表SQL,我按“先主表后明细表”的顺序写。注意用户表里加了一个role字段,但我建议把role作为简单字符串,而不是用关联表,原因很简单:毕设规模下,关联表带来的灵活度提高远不及代码复杂度增加的成本。

-- 创建数据库 CREATE DATABASE IF NOT EXISTS campus_donation DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE campus_donation; -- 用户表(学生、审核员、管理员统一存这里) CREATE TABLE `user` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(100) NOT NULL, `real_name` VARCHAR(50) NOT NULL, `student_no` VARCHAR(20) DEFAULT NULL, `phone` VARCHAR(20) DEFAULT NULL, `role` VARCHAR(20) NOT NULL DEFAULT 'student', `status` TINYINT NOT NULL DEFAULT 1, `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 捐赠主表 CREATE TABLE `donation` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL, `title` VARCHAR(100) NOT NULL, `description` TEXT, `status` VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending/approved/rejected/finished `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 捐赠物品明细表 CREATE TABLE `donation_item` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `donation_id` INT NOT NULL, `item_name` VARCHAR(100) NOT NULL, `item_category` VARCHAR(50) NOT NULL, `quantity` INT NOT NULL DEFAULT 1, FOREIGN KEY (`donation_id`) REFERENCES `donation`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 互助申请表 CREATE TABLE `help_request` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `user_id` INT NOT NULL, `donation_id` INT DEFAULT NULL, `reason` TEXT NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'pending', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (`user_id`) REFERENCES `user`(`id`), FOREIGN KEY (`donation_id`) REFERENCES `donation`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 审核记录表 CREATE TABLE `audit_log` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `target_type` VARCHAR(20) NOT NULL, -- donation/help_request `target_id` INT NOT NULL, `auditor_id` INT NOT NULL, `action` VARCHAR(20) NOT NULL, -- approve/reject/finish `comment` VARCHAR(255), `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这段SQL里的几个设计点要说明:第一,status字段用字符串而不用数字,因为代码里可读性好,比如"pending"一眼就知道是待审核,不需要再写注释映射。第二,用户表密码字段建议存BCrypt加密后的字符串,长度100是因为BCrypt哈希有60位左右,给点余量。第三,捐赠表与物品明细表之间用了外键,但MySQL里如果业务量不大,外键可以保留;如果你担心删捐赠单时外键报错,可以在明细表上加上ON DELETE CASCADE,我在上面已经写了。

初始化数据时,至少准备一个管理员账号、一个审核员账号、五个学生账号以及十条左右的捐赠记录,这样演示分页时不用临时造数。特别提醒:密码不要用明文123456,你可以在SQL里写一个BCrypt加密后的密文,或者启动时用Java代码加密插入,否则答辩时评委一眼看出密码明文存储,会追问安全问题。

3. 技术栈与项目搭建:为什么我推荐Spring Boot + Thymeleaf这套保守组合

3.1 技术栈对比:前后端分离与服务端渲染,毕业设计怎么选

你可能会纠结用Vue + Spring Boot前后端分离,还是直接用Spring Boot + Thymeleaf服务端渲染。我做过两个版本的模拟项目,结论很直接:如果一个月能搞定,选前后端分离;如果只有两到三周调试时间,选Thymeleaf。前后端分离意味着你要额外处理跨域、Token刷新、前端打包,任何一个环节卡住,都会挤压写文档和准备答辩的时间。而Thymeleaf的服务端渲染只需要一个Java进程,页面模板和Controller直接对应,部署也只需要打一个jar包,非常适合“源码+数据库+说明文档”这种交付形式。

这不是说前后端分离不好,而是“性价比”问题。在毕设答辩里,评委更关心你的业务逻辑是否完整、数据库设计是否合理、能否讲清楚请求如何流转,而不是你是否用了某种热门框架。如果你想体现“我会前后端分离”,完全可以在项目里加一个Vue管理的后台页面,但核心功能不要依赖它。我一般建议主链路用Thymeleaf,统计图表页面用ECharts,这样既保留服务端渲染的稳定性,又展示了前端可视化能力。

3.2 项目骨架:创建工程到跑通第一个页面的配置要点

我用的组合是Spring Boot + MyBatis-Plus + MySQL + Thymeleaf + Layui(或Bootstrap)。不用MyBatis-Plus的话,写分页和聚合SQL会多出不少样板代码;引入它只要注意分页插件的配置就好。下面是pom.xml中比较关键的一组依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>

注意MyBatis-Plus的版本,不要选太新的,有些新版本要求Spring Boot版本匹配。我在一个模拟项目里用3.5.3加Spring Boot 2.7,稳定。application.yml里最容易被忽略的是update-strategy和logic-delete,如果你不需要逻辑删除,就别开,否则每次查询都会自动加where deleted=0,一旦表里没有该字段直接报错。

spring: datasource: url: jdbc:mysql://localhost:3306/campus_donation?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 8080

这里的map-underscore-to-camel-case要设为true,否则create_time映射不到createTime,你会看到所有时间字段都是null,这是新手最容易踩的坑。server.port我写8080,如果你本机被占用,改成8088。另外开发时把spring.thymeleaf.cache设为false,不然改了页面模板不刷新,还得重启项目。

3.3 登录与权限拦截:写一个拦截器,把未登录请求挡在门外

权限这块,我建议写一个HandlerInterceptor实现登录校验,不要依赖每一个Controller里重复判断Session。

@Component public class LoginInterceptor implements HandlerInterceptor { private final List<String> allowUrls = Arrays.asList("/login", "/register", "/css/**", "/js/**", "/images/**"); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); for (String allow : allowUrls) { if (uri.startsWith(allow)) { return true; } } Object user = request.getSession().getAttribute("loginUser"); if (user == null) { response.sendRedirect("/login"); return false; } return true; } }

然后在配置类里注册拦截器,并规定拦截路径。注意这里有个隐藏坑:静态资源路径必须放行,否则Bootstrap的CSS全部无法加载,页面会变得“裸奔”。你可以把登录请求、注册请求、静态资源目录都放进白名单。角色权限的拦截建议在校验登录之后继续做,比如判断user.role是否为auditor,否则对/audit/**返回403页面。

另外,Session过期时间建议保持默认,但要在前端页面上通过Thymeleaf判断$ {session.loginUser}是否为空,如果为空则跳转登录页,否则页面上的用户名位置会报错。我习惯在每个需要登录的页面上加一个公共的header.html,里面写判断逻辑,比在每个页面复制一遍好得多。

4. 核心功能落地:捐赠发布、互助申请、审核与统计的代码实现

4.1 捐赠发布:Controller、Service、Mapper三层的完整写法

捐赠发布是学生端最核心的操作。前端表单会提交标题、描述、物品名称、分类、数量,后端要做的第一件事是取Session里的登录用户ID,而不是让用户传一个userId,这是防止伪造请求的关键。

先看实体类和一个Mapper方法:

@Data @TableName("donation") public class Donation { @TableId(type = IdType.AUTO) private Integer id; private Integer userId; private String title; private String description; private String status; private LocalDateTime createTime; } @Mapper public interface DonationMapper extends BaseMapper<Donation> { // 自定义分页查询:关联用户姓名和物品名称聚合 IPage<DonationVO> selectDonationPage(Page<?> page, @Param("status") String status); }

DonationVO是一个视图对象,用于在列表页显示“发布人姓名”“物品摘要”。我的做法是写一个自定义SQL,而不是用MyBatis-Plus的Wrapper去join,因为join的结果没法直接映射到实体里的itemNames字段。查询SQL大致是:

<select id="selectDonationPage" resultType="com.example.vo.DonationVO"> SELECT d.*, u.real_name AS publisherName, (SELECT GROUP_CONCAT(CONCAT(item_name, ' x', quantity) SEPARATOR ',') FROM donation_item di WHERE di.donation_id = d.id) AS itemBrief FROM donation d JOIN user u ON d.user_id = u.id <where> <if test="status != null and status != ''"> AND d.status = #{status} </if> </where> ORDER BY d.create_time DESC </select>

这里用GROUP_CONCAT把多件物品拼成一个字符串,这样列表页不用再做多表查询,性能够用而且代码简单。需要注意的是GROUP_CONCAT默认长度限制是1024字节,如果你拼接的内容长,可以在MySQL执行SET SESSION group_concat_max_len = 10240;,不过一般捐赠标题不会那么长,可以不设置。

发布保存时,我采用事务控制:

@Transactional public Long createDonation(DonationForm form, Integer userId) { Donation donation = new Donation(); donation.setUserId(userId); donation.setTitle(form.getTitle()); donation.setDescription(form.getDescription()); donation.setStatus("pending"); donationMapper.insert(donation); for (ItemForm item : form.getItems()) { DonationItem di = new DonationItem(); di.setDonationId(donation.getId()); di.setItemName(item.getItemName()); di.setItemCategory(item.getItemCategory()); di.setQuantity(item.getQuantity()); donationItemMapper.insert(di); } return donation.getId(); }

这个@Transactional很重要。如果没有事务,当捐赠主表插入成功而物品明细插入失败时,数据库会残留一条没有明细的捐赠记录,列表页用GROUP_CONCAT查出来itemBrief为null,页面显示空白,排查时很难定位。我在真实开发中遇到过同类问题,所以强烈建议所有“主表+明细表”的写入都加上事务。

4.2 互助申请与状态流转:防止重复提交与非法状态流转的校验

互助申请是学生针对某一条已审核通过的捐赠发起的。前端页面需要先确认捐赠状态是approved,但是后端的校验绝对不能只依赖前端。常见做法是后端再查一次捐赠状态,并且判断该用户是否已经对这条捐赠发起过申请。

@Transactional public String applyHelp(HelpRequestForm form, Integer userId) { Donation donation = donationMapper.selectById(form.getDonationId()); if (donation == null || !"approved".equals(donation.getStatus())) { return "该捐赠不在可申请状态"; } // 查当前用户是否已申请过该捐赠 LambdaQueryWrapper<HelpRequest> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(HelpRequest::getUserId, userId) .eq(HelpRequest::getDonationId, form.getDonationId()) .ne(HelpRequest::getStatus, "rejected"); // 被驳回后允许重新申请 Long count = helpRequestMapper.selectCount(wrapper); if (count > 0) { return "你已经申请过这条捐赠,请勿重复提交"; } HelpRequest hr = new HelpRequest(); hr.setUserId(userId); hr.setDonationId(form.getDonationId()); hr.setReason(form.getReason()); hr.setStatus("pending"); helpRequestMapper.insert(hr); return "success"; }

这段代码里需要解释两个判断:一是"approved"状态限制,防止用户在捐赠被驳回或已经完成时还能申请;二是ne(..., "rejected")条件,意味着如果上一次申请被驳回,用户还可以重新申请。如果不加这个条件,一个用户对同一条捐赠只能申请一次且永远无法重试,业务上不合理。还有更隐蔽的问题:捐赠状态是finished时,应当把该捐赠对应的所有pending申请自动改为rejected,你可以在状态流转的地方加上这个连带操作,避免审核员完成捐赠后还看到一堆待处理申请。

审核员审核时,操作也很简单:更新捐赠状态为approved或rejected,同时插入一条audit_log。但要注意,更新必须使用条件更新,例如UPDATE donation SET status = 'approved' WHERE id = ? AND status = 'pending'。这样可以避免两个审核员同时审核一条记录时,后一个把前一个的结果覆盖成自己的结论。

4.3 管理员审核与统计图表:聚合SQL到JSON接口

统计图表是很多人不敢做的地方,其实只要会聚合SQL就不难。我提供两个最常用的统计指标:每月捐赠数量、物品分类占比。

@GetMapping("/admin/stats/donation-category") @ResponseBody public Result categoryStats() { List<Map<String, Object>> list = donationItemMapper.selectCategoryCount(); return Result.success(list); }

Mapper方法用注解SQL实现:

@Select("SELECT item_category AS name, COUNT(*) AS value FROM donation_item " + "JOIN donation ON donation.id = donation_item.donation_id " + "WHERE donation.status != 'rejected' " + "GROUP BY item_category ORDER BY value DESC") List<Map<String, Object>> selectCategoryCount();

返回的JSON格式是[{name:"书籍", value:12}, {name:"衣物", value:8}],前端用ECharts的饼图直接传入即可。这里要注意:不要把SQL直接写在Java代码里,如果项目有大量统计需求,建议把SQL放到XML里统一管理。另外统计SQL中一定要关联donation表并过滤掉rejected状态,否则被驳回的捐赠也会统计进去,数据会失真。我在模拟项目中曾经因为没过滤状态,导致“衣物类捐赠”比实际情况多出接近一倍,被答辩评委追问得下不来台。

前端图表页我建议用Thymeleaf渲染一个div,然后用Thymeleaf内联脚本把数据塞给ECharts。具体做法是在Controller里生成一个data字段放入模型,前端<script th:inline="javascript">中读取。如果你不想用内联脚本,也可以单独提供一个/admin/stats/data接口用Ajax请求,这样更解耦。

5. 避坑指南:毕设最容易翻车的5个地方

5.1 刷新页面重复提交捐赠单,数据库里出现两条相同记录

现象:学生填写捐赠表单后点击提交,由于网络或自己手抖点了两次,MySQL里产生了两条完全相同的捐赠记录。

原因:前端按钮没有做防重复提交,后端也没有幂等校验。表单提交接口每次都执行insert,自然插入两条。

解决:前端提交按钮在submit后立刻disabled,并修改文本为“提交中”;后端在Service层根据用户ID和表单标题在短时间内查重,比如“同一用户10分钟内存在同样标题的pending捐赠”则拒绝提交。更彻底的办法是加一个token,每次页面加载时生成随机串存入Session,提交时校验并一次性销毁,但毕设做到按钮禁用加后端查重基本就够了。

5.2 删除用户时报外键错误,页面提示MySQLIntegrityConstraintViolationException

现象:管理员删除某个已发布过捐赠的学生账号时,页面直接报错,日志里有“Cannot delete or update a parent row: a foreign key constraint fails”。

原因:用户表被捐赠表、互助申请表外键引用。直接删除主表记录,而子表还有关联数据,关系型数据库会拒绝删除。

解决:不要物理删除用户,使用逻辑删除。在用户表加deleted字段,MyBatis-Plus配置@TableLogic,然后所有查询自动过滤已删除数据。如果确实要物理删除,则需要先删除该用户的捐赠、互助申请、审核记录,再删用户,这个顺序容易漏。我的经验是:毕设答辩里说“用户不允许物理删除”反而显得你考虑到了数据完整性问题,是加分项。

5.3 页面中文乱码,审核员备注里的“同意”变成“???”

现象:用MySQL命令行导入数据库脚本后,浏览器页面正常,但在后台输入中文并保存后,查询出来是问号。

原因:数据库连接URL没加characterEncoding=utf8,或者建表时的字符集不是utf8mb4。注意MySQL 8.0的驱动是com.mysql.cj.jdbc.Driver,URL里至少要有useUnicode=true&characterEncoding=utf8mb4。

解决:在application.yml里检查datasource url,并确认数据库本身字符集是utf8mb4。如果已经乱了,修改数据库字符集后重建数据表再导入脚本。我还有一个习惯:在Spring Boot启动类里加一个CharacterEncodingFilter配置,防止Tomcat接收请求时把POST参数按ISO-8859-1解码。

5.4 分页查询不生效,返回全部数据或缺少total参数

现象:使用MyBatis-Plus的selectPage后,前端分页条显示总记录数为0,或者查询结果一直是全量数据。

原因:大概率是MyBatis-Plus的分页插件没有配置。很多人以为引入依赖就自动分页,实际上要显式配置PaginationInnerInterceptor,否则Page对象只会作为参数传入,SQL没有拼接limit。

解决:写一个MybatisPlusConfig配置类:

@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }

配置完成后,需要重启项目再测试。我见过同学在配置类里忘了@Configuration,结果插件始终没注册,这种错误特别难发现,检查日志又看不到,所以优先确认注解是否存在。

5.5 上传的捐赠图片不显示,用地址打开是404

现象:学生上传物品照片后,浏览器图片区域显示裂图,访问图片路径是404。

原因:图片保存到了本机某个磁盘目录,但Spring Boot默认只处理classpath下的静态资源,不会把D盘某个路径映射为可访问URL。

解决:使用一个本地磁盘映射配置,在WebMvcConfigurer里重写addResourceHandlers:

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + uploadDir); }

uploadDir是在配置里指定的绝对路径,比如D:/campus-donation-upload/。这样前端上传的图片就能通过http://localhost:8080/upload/xxx.jpg访问。注意路径最后一定要加/,否则映射不生效。还有一个细节:图片目录需要事先存在,程序不会自动创建,启动前手动mkdir -p或代码里判断后创建。

6. 从能跑变成能答辩:三个加分优化与验证技巧

6.1 一键启动脚本和自动初始化数据

很多毕设项目直接扔给别人,别人要自己创建数据库、导入SQL、改密码、启动项目,光环境问题就卡半天。我建议写一个start.bat(Windows)和start.sh(Linux/Mac),里面先检查MySQL是否启动,再执行mysql -u root -p你的密码 < init.sql,然后运行mvn spring-boot:run。为了演示方便,可以把数据库连接密码写进启动脚本的环境变量里,并在Spring Boot配置中读取环境变量,这样评委在答辩现场换电脑也能快速跑起来。这个细节经常被忽略,但“一键启动”在演示时非常加分。

6.2 用定时任务自动清理过期未审核的捐赠

如果你想让系统显得更“智能”,可以给捐赠表增加一个expect_finish_time或把创建时间当作判断依据。在Spring Boot中开一个定时任务,每5分钟扫描一次超过24小时仍处于pending状态的捐赠,自动置为expired。同时给审核员页面上增加一个“过期未审”的红色标记。这个功能不算复杂,但它能让答辩评委看到你处理业务流程异常状态的能力,比单纯“CRUD”高一个档次。

6.3 答辩前必须走一遍的功能验证清单

我建议在答辩前一天按下面这个“用户故事”完整走一遍:新学生注册→登录→发布一条包含两件物品的捐赠→审核员登录审核通过→另一个学生申请互助→审核员审核互助通过→管理员查看统计图表并完成捐赠→学生确认物品已收到。在这个过程中重点检查:重复提交是否被拦截、中文是否正常显示、分页是否准确、退出登录后能否直接访问后台审核链接。每走完一步就截图保存,作为论文的测试依据。

我自己的习惯是把验证清单写在说明文档的最后一页,并在答辩现场照着这个清单演示,而不是随机乱点。这样整个流程有逻辑、有节奏,不会被评委带乱。这个系统真正难的不是写代码,而是把每一步业务流转想明白。希望帮到你。

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

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

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

立即咨询