news 2026/4/23 12:57:27

毕业设计实战:基于SpringBoot的入校申报审批系统,从需求到部署避坑全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
毕业设计实战:基于SpringBoot的入校申报审批系统,从需求到部署避坑全指南

毕业设计实战:基于SpringBoot的入校申报审批系统,从需求到部署避坑全指南

当初做入校申报审批系统时,我在“健康码、行程码双码上传校验”功能上卡了整整三天——一开始没做文件格式和大小限制,结果用户传了个100MB的视频文件,服务器直接崩了,导师看了直摇头😫 后来踩了无数坑,终于总结出这套完整开发流程。今天就把入校申报审批系统的实战经验全部分享出来,宝子们跟着做,毕设稳过!

一、需求分析别想当然!先搞懂“谁申请,谁审批”

最开始我以为做个简单的表单提交就行了,结果导师说“要考虑疫情常态化管理,要有健康检查流程”。后来才明白,入校申报系统的核心是“用户申报-管理员审批-门卫核验”的三级流程,必须抓住这三个环节的核心需求。

1. 核心用户 & 核心功能(踩坑后总结版)

入校申报审批系统有三类核心用户:普通用户(申报人)、管理员(审批人)、门卫(核验人)。千万别把“辅导员”、“院系领导”都加进去!我当初加了,审批流程变得极其复杂,最后简化成三级才顺畅。

  • 用户端(申报人,必须做的功能):

    • 个人信息管理:维护姓名、身份证号、联系方式、头像等基本信息。
    • 入校申报:这是核心中的核心
      • 填写入校时间、出校时间、入校事由。
      • 选择人员身份(学生、教职工、访客等)。
      • 提交后生成唯一的申报编号。
    • 申报记录查询:查看所有申报记录及审批状态。
    • 公告查看:浏览学校最新防疫政策和通知。
  • 管理员端(审批人,核心功能):

    • 申报审批
      • 查看待审批的入校申报列表。
      • 审核申报信息,可“通过”或“驳回”。
      • 驳回必须填写理由(这个很重要!)。
    • 用户管理:管理所有用户账号,可冻结异常账号。
    • 入校检查记录管理:查看所有入校检查记录。
    • 公告管理:发布、编辑、删除防疫公告。
  • 门卫端(核验人,简化但必要):

    • 入校检查登记
      • 扫描申报编号或输入身份证号查询申报信息。
      • 登记体温、上传健康码和行程码截图。
      • 记录是否去过风险地区。
    • 今日入校统计:查看当天已入校人员列表。

2. 需求分析避坑指南(血泪教训!)

  • 别空想流程,要画出来!用流程图工具画出完整的“申报-审批-入校检查”流程。我当初画出来后才发现,少了“驳回后用户重新提交”的环节,赶紧补上。
  • 一定要考虑异常情况
    • 用户填的出校时间比入校时间还早怎么办?(前端要做时间校验)
    • 健康码截图上传了假图怎么办?(虽然不能100%防伪,但可以做文件MD5校验)
    • 审批人长时间不审批怎么办?(可以加个“催办”功能,或者自动提醒)
  • 写清楚约束条件
    • “入校时间必须至少提前2小时申报”
    • “健康码必须为24小时内”
    • “体温超过37.3℃自动标记为异常”
    • “同一用户同一天只能申报一次入校”

3. 可行性分析(三句话说清楚)

  • 技术可行性:SpringBoot + MySQL + Vue,都是成熟技术。健康码识别可以用简单的颜色判断(绿码/黄码/红码),不需要复杂的AI识别。
  • 经济可行性:所有工具免费,部署到学校服务器或学生云服务器(学生优惠)成本极低。
  • 操作可行性:用户扫码就能申报,门卫用手机或平板就能核验,操作简单。

二、技术选型:SpringBoot是真香!

当初我看别人用传统的SSM,配置一堆XML文件。后来选择了SpringBoot 2.7 + MyBatis-Plus + Vue 2 + Element UI,开发效率提升了不止一倍!

技术栈详解与避坑

技术选择理由避坑提醒
SpringBoot 2.7.x自动配置,内嵌Tomcat,快速启动。别用3.x,部分依赖还没适配好。
MyBatis-Plus强大的CRUD操作,代码生成器好用。好好用它的LambdaQueryWrapper,写查询条件超方便。
Vue 2 + Element UI组件丰富,做管理后台界面很快。Vue 2够用了,别追求Vue 3增加学习成本。
MySQL 8.0JSON字段支持,存健康码路径方便。一定用utf8mb4字符集,否则Emoji表情会乱码。
Redis(可选)缓存申报编号,提高查询速度。如果数据量不大,可以不用,简化部署。

开发环境一步到位

# 1. 用Spring Initializr创建项目# 勾选:Web、MyBatis、MySQL、Redis(可选)# 2. 配置application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/ruxiao_system?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password:123456driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB# 限制上传文件大小max-request-size: 10MB# 3. 集成MyBatis-Plusmybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三、数据库设计:状态流转是关键!

我当初的坑:把审批状态和检查状态混在一个表里,结果逻辑混乱。后来分成了入校申报表(管审批)和入校检查表(管核验),清晰多了。

核心表结构设计(重点!)

-- 入校申报表(核心表)CREATETABLE`ruxiaoshenbao`(`id`intNOTNULLAUTO_INCREMENT,`ruxiaoshenbao_uuid_number`varchar(50)NOTNULLCOMMENT'申报编号:RX+年月日+6位随机数',`yonghu_id`intNOTNULLCOMMENT'申报用户',`zhuanye_types`intDEFAULT1COMMENT'人员身份:1学生,2教职工,3访客',`shiyou`textCOMMENT'入校事由',`ruxiaoshenbao_time`datetimeNOTNULLCOMMENT'计划入校时间',`cuxiao_time`datetimeCOMMENT'计划出校时间',`ruxiaoshenbao_yesno_types`intDEFAULT1COMMENT'审批状态:1待审批,2通过,3驳回',`ruxiaoshenbao_yesno_text`textCOMMENT'审批意见',`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),UNIQUEKEY`uk_uuid`(`ruxiaoshenbao_uuid_number`),KEY`idx_user`(`yonghu_id`),KEY`idx_time`(`ruxiaoshenbao_time`)-- 按时间查询加索引)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='入校申报表';-- 入校检查表(核验记录)CREATETABLE`ruxiaojiancha`(`id`intNOTNULLAUTO_INCREMENT,`ruxiaoshenbao_id`intNOTNULLCOMMENT'关联的申报记录',`tiwen`decimal(3,1)COMMENT'体温',`ruxiaojiancha_photo`varchar(500)COMMENT'健康码图片路径',`xingcheng_photo`varchar(500)COMMENT'行程码图片路径',`jiancha_result`intDEFAULT1COMMENT'检查结果:1正常,2异常',`ruxiaojiancha_content`textCOMMENT'检查详情',`create_time`datetimeDEFAULTCURRENT_TIMESTAMPCOMMENT'检查时间',PRIMARYKEY(`id`),KEY`fk_shenbao`(`ruxiaoshenbao_id`),CONSTRAINT`fk_shenbao`FOREIGNKEY(`ruxiaoshenbao_id`)REFERENCES`ruxiaoshenbao`(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='入校检查表';-- 用户表CREATETABLE`yonghu`(`id`intNOTNULLAUTO_INCREMENT,`yonghu_name`varchar(100)NOTNULLCOMMENT'姓名',`yonghu_phone`varchar(20)NOTNULLCOMMENT'手机号',`yonghu_id_number`varchar(18)NOTNULLCOMMENT'身份证号',`xueyuan`varchar(100)COMMENT'学院/部门',`banji`varchar(50)COMMENT'班级',`yonghu_delete`intDEFAULT0COMMENT'0正常,1已删除',PRIMARYKEY(`id`),UNIQUEKEY`uk_phone`(`yonghu_phone`),UNIQUEKEY`uk_idcard`(`yonghu_id_number`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='用户表';

设计亮点

  1. 申报编号生成规则RX20240520001234(RX+年月日+6位随机数),唯一且有意义。
  2. 状态分离:审批状态在申报表,检查结果在检查表,逻辑清晰。
  3. 索引优化:给常用的查询字段加索引,提高查询速度。

四、功能实现:抓住核心流程,做出亮点

1. 用户端:入校申报(核心体验)

关键逻辑:时间校验、重复申报校验、生成唯一编号。

前端实现要点(Vue + Element UI)

<template> <div class="shenbao-form"> <el-form :model="form" :rules="rules" ref="formRef"> <el-form-item label="计划入校时间" prop="ruxiaoshenbao_time"> <el-date-picker v-model="form.ruxiaoshenbao_time" type="datetime" :picker-options="timeOptions" placeholder="选择入校时间" /> <div class="tip">需至少提前2小时申报</div> </el-form-item> <el-form-item label="人员身份" prop="zhuanye_types"> <el-select v-model="form.zhuanye_types" placeholder="请选择"> <el-option label="学生" :value="1"></el-option> <el-option label="教职工" :value="2"></el-option> <el-option label="访客" :value="3"></el-option> </el-select> </el-form-item> <el-form-item label="入校事由" prop="shiyou"> <el-input type="textarea" v-model="form.shiyou" :rows="4" placeholder="请详细说明入校事由" maxlength="500" show-word-limit /> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm" :loading="submitting"> 提交申报 </el-button> </el-form-item> </el-form> </div> </template> <script> export default { data() { // 时间校验:只能选择未来时间,且至少提前2小时 const validateTime = (rule, value, callback) => { if (!value) { callback(new Error('请选择入校时间')); } const now = new Date(); const twoHoursLater = new Date(now.getTime() + 2 * 60 * 60 * 1000); if (value < twoHoursLater) { callback(new Error('入校时间需至少提前2小时')); } callback(); }; return { form: { ruxiaoshenbao_time: '', zhuanye_types: '', shiyou: '' }, rules: { ruxiaoshenbao_time: [ { required: true, validator: validateTime, trigger: 'change' } ], zhuanye_types: [ { required: true, message: '请选择人员身份', trigger: 'change' } ], shiyou: [ { required: true, message: '请输入入校事由', trigger: 'blur' }, { min: 10, message: '事由描述至少10个字', trigger: 'blur' } ] }, timeOptions: { disabledDate(time) { // 不能选择今天之前的日期 return time.getTime() < Date.now() - 24 * 60 * 60 * 1000; } }, submitting: false }; }, methods: { async submitForm() { try { await this.$refs.formRef.validate(); this.submitting = true; // 检查今天是否已申报过 const hasToday = await this.checkTodayApplication(); if (hasToday) { this.$message.warning('您今天已提交过入校申报,请勿重复提交'); return; } // 提交申报 const res = await this.$api.shenbao.submit(this.form); this.$message.success(`申报成功!您的申报编号:${res.data.uuidNumber}`); this.$router.push('/my-applications'); } catch (error) { console.error('提交失败:', error); } finally { this.submitting = false; } }, async checkTodayApplication() { const today = new Date().toISOString().split('T')[0]; const res = await this.$api.shenbao.checkToday({ date: today }); return res.data.hasApplication; } } }; </script>

后端关键代码(SpringBoot)

@ServicepublicclassRuxiaoshenbaoServiceImplimplementsRuxiaoshenbaoService{@AutowiredprivateRuxiaoshenbaoMapperruxiaoshenbaoMapper;@Transactional@OverridepublicResultsubmitShenbao(RuxiaoshenbaoFormform,IntegeruserId){// 1. 检查今天是否已申报LocalDatetoday=LocalDate.now();LambdaQueryWrapper<Ruxiaoshenbao>wrapper=newLambdaQueryWrapper<>();wrapper.eq(Ruxiaoshenbao::getYonghuId,userId).ge(Ruxiaoshenbao::getCreateTime,today.atStartOfDay()).lt(Ruxiaoshenbao::getCreateTime,today.plusDays(1).atStartOfDay());longcount=this.count(wrapper);if(count>0){returnResult.error("您今天已提交过入校申报");}// 2. 生成唯一编号StringuuidNumber=generateUuidNumber();// 3. 保存申报记录Ruxiaoshenbaoentity=newRuxiaoshenbao();BeanUtils.copyProperties(form,entity);entity.setYonghuId(userId);entity.setRuxiaoshenbaoUuidNumber(uuidNumber);entity.setRuxiaoshenbaoYesnoTypes(1);// 待审批this.save(entity);// 4. 记录操作日志(可选)logService.addLog(userId,"提交入校申报","编号:"+uuidNumber);returnResult.success("申报提交成功",uuidNumber);}privateStringgenerateUuidNumber(){// RX + 年月日 + 6位随机数StringdateStr=LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));StringrandomStr=String.format("%06d",newRandom().nextInt(999999));return"RX"+dateStr+randomStr;}}

2. 管理员端:申报审批(核心业务)

关键逻辑:批量审批、驳回必须填理由、审批记录可追溯。

审批页面设计要点

  • 待审批列表:表格显示所有待审批记录,可多选批量审批。
  • 审批操作弹窗
    • 显示申报详情(用户信息、入校时间、事由)。
    • 单选框:“通过”或“驳回”。
    • 文本域:“审批意见”(驳回时必须填写)。
    • “提交审批”按钮。
  • 审批历史:可查看每条申报的审批记录。

批量审批后端代码

@PostMapping("/batchApprove")@ResponseBody@TransactionalpublicResultbatchApprove(@RequestBodyBatchApproveRequestrequest){// request包含:申报ID列表、审批结果、审批意见if(CollectionUtils.isEmpty(request.getShenbaoIds())){returnResult.error("请选择要审批的申报");}if(request.getResult()==3&&StringUtils.isBlank(request.getReason())){returnResult.error("驳回必须填写理由");}List<Ruxiaoshenbao>updateList=newArrayList<>();for(IntegershenbaoId:request.getShenbaoIds()){Ruxiaoshenbaoshenbao=ruxiaoshenbaoService.getById(shenbaoId);if(shenbao!=null&&shenbao.getRuxiaoshenbaoYesnoTypes()==1){shenbao.setRuxiaoshenbaoYesnoTypes(request.getResult());shenbao.setRuxiaoshenbaoYesnoText(request.getReason());updateList.add(shenbao);// 发送审批结果通知(微信/短信)noticeService.sendApproveResult(shenbao.getYonghuId(),request.getResult());}}if(!updateList.isEmpty()){ruxiaoshenbaoService.updateBatchById(updateList);}returnResult.success("批量审批完成");}

3. 门卫端:入校检查(移动端友好)

关键逻辑:扫码核验、双码上传、体温异常预警。

移动端核验页面

<!-- 简洁的核验页面,适合手机/PAD操作 --><divclass="check-page"><divclass="scan-section"><button@click="startScan">扫码核验</button><divclass="or"></div><inputv-model="searchText"placeholder="输入申报编号/身份证号"><button@click="search">查询</button></div><divv-if="shenbaoInfo"class="info-section"><h3>{{ shenbaoInfo.yonghuName }}</h3><p>申报编号:{{ shenbaoInfo.uuidNumber }}</p><p>计划入校:{{ shenbaoInfo.planTime }}</p><divclass="check-form"><inputtype="number"v-model="tiwen"placeholder="测量体温(℃)"step="0.1"><divclass="upload-section"><label>健康码:</label><inputtype="file"accept="image/*"@change="uploadHealthCode"><imgv-if="healthCodeUrl":src="healthCodeUrl"class="preview"></div><divclass="upload-section"><label>行程码:</label><inputtype="file"accept="image/*"@change="uploadTravelCode"><imgv-if="travelCodeUrl":src="travelCodeUrl"class="preview"></div><button@click="submitCheck":disabled="!canSubmit">完成核验</button></div></div></div>




五、系统测试:重点测这些场景

核心测试用例

测试场景测试步骤预期结果重要性
重复申报同一用户同一天提交两次申报第二次提示“今天已申报过”
时间校验选择1小时后入校的时间提示“需至少提前2小时”
双码上传上传非图片文件(如PDF)提示“请上传图片文件”
体温异常输入体温37.5℃自动标记为“异常”,需要额外确认
批量审批选择多条记录,批量通过所有选中记录状态更新为“通过”
扫码核验用已过期的申报编号扫码提示“申报已过期”

压力测试(简单做)

用JMeter模拟50个用户同时提交申报,看系统响应时间。目标:95%的请求在2秒内响应。

六、部署与上线

1. 服务器准备

  • 学生优惠:阿里云/腾讯云学生服务器,¥10/月。
  • 配置:1核2G,CentOS 7.6。
  • 必备软件:JDK 1.8、MySQL 8.0、Nginx(反向代理)。

2. 一键部署脚本

#!/bin/bash# deploy.shecho"开始部署入校申报系统..."# 1. 备份旧版本if[-d"/app/ruxiao"];thenmv/app/ruxiao /app/ruxiao_backup_$(date+%Y%m%d)fi# 2. 创建新目录mkdir-p /app/ruxiaocptarget/ruxiao-system.jar /app/ruxiao/# 3. 复制配置文件cpapplication-prod.yml /app/ruxiao/# 4. 启动应用cd/app/ruxiaonohupjava -jar ruxiao-system.jar --spring.profiles.active=prod>app.log2>&1&echo"部署完成!"

3. Nginx配置

server { listen 80; server_name ruxiao.yourschool.edu.cn; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 静态文件缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 30d; add_header Cache-Control "public, immutable"; } }

七、答辩准备:讲好这个故事

  1. 演示流程要完整
    “大家好,我演示一个完整的入校申报流程。首先,学生张三登录系统(展示),填写明天上午9点入校的申请,事由是‘实验室做实验’(展示表单校验)。提交后生成申报编号。接着,管理员李老师登录,在待审批列表看到这条申请(展示),审核后通过。最后,门卫王师傅用平板电脑扫描张三的申报二维码(展示移动端页面),登记体温36.5℃,上传双码,完成入校核验。”

  2. 重点讲“你的设计亮点”

    • “我设计了‘申报编号’规则,方便门卫快速核验。”
    • “做了严格的时间校验,必须提前2小时申报,避免临时申请。”
    • “双码上传做了文件类型和大小限制,防止恶意上传。”
    • “体温异常自动预警,需要门卫额外确认。”
  3. 准备好问答

    • Q:如果用户造假怎么办?A:系统不能100%防止造假,但我们可以记录所有操作日志,事后可追溯。另外可以和学校统一身份认证系统对接,提高可信度。
    • Q:数据量大怎么办?A:申报记录可以按月份分表存储,历史数据可以归档。查询时通过索引优化。
    • Q:系统安全性如何?A:所有密码MD5加密存储,接口有防重复提交和SQL注入防护,文件上传做了安全限制。

最后:一点真心话

入校申报系统看起来简单,但要把疫情管理的严谨性和用户体验的便捷性平衡好,需要很多细节考虑。关键是把“申报-审批-核验”这个核心流程做顺畅,把异常情况考虑周全。

需要完整源码数据库脚本部署文档的宝子,可以在评论区留言。

觉得这篇干货有帮助,记得点赞收藏!祝大家毕设顺利,轻松毕业!🎓

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

详解自动化测试框架特点和原理

今天捡一些比较典型的工具型自动化框架来介绍&#xff0c;主要围绕历史、特点和原理来讲解&#xff0c;至于使用教程&#xff0c;网络上已经有很多资料&#xff0c;这里就不加以展开。 Quick Test Professional 如果时光倒流二十年&#xff0c;QTP 在自动化领域绝对是王者一般…

作者头像 李华
网站建设 2026/4/23 12:11:55

一文详解 requests 库中 json 参数和 data 参数的用法

在requests库当中&#xff0c;requests请求方法&#xff0c;当发送post/put/delete等带有请求体的请求时&#xff0c;有json和data2个参数可选。众所周知&#xff0c;http请求的请求体格式主要有以下4种&#xff1a;application/jsonapplicaiton/x-www-from-urlencodedmultipar…

作者头像 李华
网站建设 2026/4/23 10:44:19

java <T> 是什么?

public static <T> T createDefault(Class<T> clazz) {try {return clazz.getDeclaredConstructor().newInstance();} catch (Exception e) {throw new RuntimeException(e);} }看这个方法&#xff0c;总共3个T&#xff0c;返回的又是一个实例对象。如果是一个新手…

作者头像 李华
网站建设 2026/4/23 10:42:41

③【openFuyao 】以开放社区构建算力生态

③【openFuyao 】以开放社区构建算力生态写在最前面一、 引言&#xff1a;AI 时代的“生态鸿沟”二、 核心主张与定位&#xff1a;以“开放”赋能“生态”2.1 核心主张&#xff1a;构建“算力亲和”的开放生态2.2 愿景与使命&#xff1a;提供多样化算力集群“软件生态”2.3 为生…

作者头像 李华
网站建设 2026/4/23 12:10:31

Steam Headless Docker 无头模式游戏流媒体服务器配置指南

Steam Headless Docker 无头模式游戏流媒体服务器配置指南 【免费下载链接】docker-steam-headless A Headless Steam Docker image supporting NVIDIA GPU and accessible via Web UI 项目地址: https://gitcode.com/gh_mirrors/do/docker-steam-headless Steam Headle…

作者头像 李华