news 2026/10/19 13:41:14

SpringBoot2 + Vue3 大学生迎新系统:从数据库建模到权限设计实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot2 + Vue3 大学生迎新系统:从数据库建模到权限设计实战解析

每年八月底,各高校的迎新现场基本都是人数最多的场景。新生要查班级、找辅导员、办宿舍、领物资、核对缴费信息,志愿者和辅导员手上同时拿着好几张表格来回跑,数据稍微没对齐,报到效率就会明显下降。这类业务天然适合用 Web 系统来承接,前端做操作界面,后端管业务流程和权限,数据库存全校学生与床位数据。我最近整理了一套完整的大学生迎新系统源码,采用的是一套很经典也很适合学习的技术组合:SpringBoot2 做后端接口、Vue3 做前端页面、MyBatis-Plus 做持久层操作、MySQL8.0 做数据存储,配套文档也齐全,包括了数据库脚本、接口说明、部署步骤和演示数据。这篇文章我会从实际开发者的视角,把整个项目拆开讲清楚:业务模块怎么设计、数据库怎么建模、登录权限怎么做、核心接口怎么实现、本地怎么用最快速度跑起来,以及我前后踩过的一些坑和优化建议。不论你是准备交课程设计作业,还是在为毕业设计做技术选型,都可以把这套内容当作一份可以直接复用的经验清单。

1. 业务与架构:先把迎新这个流程想明白

1.1 迎新系统到底解决什么问题

很多人拿到“迎新系统”这个题目,第一反应是做一套新生注册页面,其实这是典型的把需求做窄了。真实的迎新场景,从学生拿到录取信息开始,到真正入校办理完手续,中间至少包含几个环节:查询录取结果、补全个人信息、找到自己的班级和辅导员、确认到校时间和交通方式、核对缴费状态、分配宿舍床位、领取开学物资、查看学校公告。这些步骤涉及的角色也不止新生一个,还包括院系管理员、辅导员、财务人员、宿管人员。

如果这些信息全部靠人工处理,最直观的问题就是数据碎片化。学生信息可能在 Excel 里、在群里、在纸质回执上各有一份,辅导员想统计某个专业已经报到了多少人,只能让班长挨个问,效率非常低。所以迎新系统的第一个核心价值是“统一入口”:新生只用一个账号登录,就能看到与自己相关的全部报到事项;管理端则提供名单查询、报到统计、宿舍调配、材料审核这些功能,把过去靠人工整理的报表交给程序自动完成。

第二个核心价值是“流程状态化”。新生报到不是一个瞬间动作,而是分阶段的,比如还未报到、已填报信息、已确认到校、已完成入住、已领取物资。系统里用状态字段维护这条主线,学生和管理员都能随时看到当前走到哪一步,哪个环节缺失就补哪个环节,不用再反复对着名单打钩划叉。

第三个价值是权限分离。普通学生只能查到自己的数据,辅导员能看本院系的汇总数据,系统管理员能维护基础字典和分配宿舍。这种角色边界如果不用系统控制,很容易出现学生登录后能看到别人身份证号这类隐私事故。所以做这个项目时,权限设计要从第一版就放进去,不要等页面都写完再补。

1.2 用户角色与功能模块划分

我在设计这套系统时,把用户角色收敛成了三类:新生、辅导员、系统管理员。没有做更复杂的多级自由度,因为对于课程设计和毕业设计来说,三类角色已经能完整表达权限层级关系,也能覆盖绝大部分答辩时会被问到的权限问题。

新生端的功能主要包括:登录修改密码、查看个人基本信息、填写网上报到信息、查看报到进度、查看宿舍分配结果、查看公告通知、上传证件照和录取材料。这些功能对应的是新生本人的核心链路。

辅导员端主要处理一个专业或一个学院范围内的数据:查看本院系新生名单、按条件筛选、导出报到统计表、审核学生提交的材料、查看本院系宿舍分配情况。辅导员能看到的数据范围是受控的,不能越权看到其他院系的信息。

系统管理员负责全局维护:新生信息导入、学院专业班级管理、宿舍和床位管理、公告发布、系统用户管理、报到统计看板。这个角色是项目的“上帝视角”,也是演示时最好展示的部分。

功能模块整理成下面这张表会更清楚。

模块核心功能使用角色说明
登录与用户管理账号密码登录、JWT 鉴权、修改密码全部学生数据从名单导入,初始密码统一
学生信息管理新生导入、列表查询、编辑、导出系统管理员、辅导员包括姓名、学号、身份证、电话等
报到信息管理填写到校时间、交通方式、陪同人数新生每个学生最多一条报到记录
学院专业班级管理三级树形结构维护系统管理员下拉联动时需要稳定排序
宿舍分配管理宿舍楼栋、房间、床位分配系统管理员、辅导员按性别自动过滤可选床位
公告通知发布、编辑、查看管理员、新生新生端按时间倒序展示
材料上传与审核上传图片材料、审核状态流转新生、辅导员文件大小和后缀要校验
统计看板报到率、性别比、学院分布系统管理员后端聚合数据,前端图表展示

模块不需要做得特别多,但每个模块都应该能回答“解决了什么业务问题”。比如宿舍管理模块,如果只是做一张表的增删改查,那和普通的 CRUD 练习没有区别;但加上“按性别过滤床位”和“分配后状态同步更新”之后,它就是一个有业务逻辑的功能模块,答辩时也能讲出内容。

1.3 前后端分离架构的取舍

我现在带人做项目,默认都会推荐前后端分离,即使只是一个中小型管理系统。原因有几条:第一,后端只暴露 RESTful 接口,不关心页面长什么样,以后想加小程序端或移动端,不需要改动业务代码;第二,前端工程用 Vue3 独立开发,组件化程度更高,代码结构比传统的 JSP + Thymeleaf 模板清晰得多;第三,把静态资源和后端逻辑拆开,更贴近真实项目的分工模式。

前后端分离不是没有代价。它引入了跨域问题、两个工程的启动顺序、环境配置不一致等新麻烦。但恰恰是这些“麻烦”,让项目有了更多可以展示的技术点。比如前端用 Vite 代理解决开发环境跨域,上线时把前端构建产物放到后端 static 目录实现单端口部署,这些都是可以在答辩时展开讲的内容。所以只要不是学校强制要求用 JSP,我都建议用前后端分离的方式去做。

2. 技术选型与版本搭配:为什么是这套组合

2.1 用 SpringBoot2.7 而不是 SpringBoot3

前一段时间总有同学问我,现在新项目都在用 SpringBoot3,为什么这套系统还用 SpringBoot2?答案其实很现实:技术选型不能只看“新不新”,还要看“稳不稳、配套全不全”。SpringBoot3 要求 JDK17 起步,而很多高校的课程和本地环境还是 JDK8,代码写出来老师也未必方便运行。相比之下,SpringBoot2.7.x 是 SpringBoot2 系列最后一个大版本,对 JDK8 的支持非常成熟,网上遇到问题一搜就有大量现成答案,MyBatis-Plus 的兼容性也最稳定。

还有一个原因:SpringBoot2.7 的配置体系和大多数存量项目一脉相承,很多公司里跑的老项目也还是这个版本。把 SpringBoot2 吃透了,再去理解 SpringBoot3 的自动装配、Starter 机制和配置属性,迁移成本并不高。反过来,如果一开始就在一个不熟悉的技术栈上叠加太多未知版本,出了问题很难判断是代码写错还是版本不兼容。

如果你坚持要用 SpringBoot3,需要注意同步升级 JDK17+、切换为mybatis-plus-spring-boot3-starter、重新验证部分旧依赖是否支持。改动量不算小,对课设和毕设来说没有必要冒这个险。

2.2 前端组合:Vue3 + Vite + Element Plus

前端选择 Vue3 的原因很直接:组合式 API 比 Vue2 的 Options API 更适合写业务逻辑。同样一块“文件上传”逻辑,在 Vue2 里可能要把 data、methods、computed 分开写好几处,而 Vue3 可以封装成一个useUpload函数,组件里按需调用,代码内聚性好很多。另外 Vue3 的响应式系统在性能上也有提升,虽然对学生项目来说感知不明显,但用新版本写项目,知识本身不会过时。

Vite 替代 Webpack 带来的体验提升是肉眼可见的。开发环境下 Vite 启动一个前端工程只需要一两秒,热更新也很快,改完代码浏览器立刻能反映。Webpack 那种几十秒起步的启动速度,在改页面的时候确实有点消耗耐心。

UI 组件库我选的是 Element Plus。后台管理系统的页面形态高度统一,无非是表格、表单、弹窗、菜单、标签页,Element Plus 这些组件都覆盖了,而且默认风格比较清爽,不需要额外设计 CSS。状态管理用 Pinia,目前项目里只存 token 和用户角色这些最小信息,没有做复杂的状态设计。

2.3 MyBatis-Plus 不只是少写代码

很多刚开始接触 MyBatis-Plus 的人会觉得它只是一个“少写 SQL”的工具,实际上它最重要的价值可能是查询构造器的安全性。传统的字符串拼接 SQL 容易写错字段名,也容易埋下注入隐患。用LambdaQueryWrapper以后,查询条件直接通过方法引用指定字段,比如LambdaQueryWrapper<Student>().eq(Student::getCollegeId, 1),字段名如果写错了,在编译阶段就能发现,而不是等运行时数据库报错。

在我的项目里,持久层大概分几种用法:

  • BaseMapper自带的selectById、insert、updateById处理单表基础操作;
  • Page<T>配合分页插件处理列表分页;
  • LambdaQueryWrapper处理动态条件筛选;
  • 自定义 XML 处理多表关联、聚合统计这类复杂查询。

这里要提醒一句:代码生成器能用,但不要滥用。简单的单表 CRUD 用它很省时间,涉及多表 join 的报表和复杂统计,还是得自己在 XML 里写 SQL。把这两者的边界分清楚,项目质量和开发效率才有保障。

2.4 MySQL8.0 的几个关键细节

MySQL8.0 默认字符集是 utf8mb4,可以完整支持中文、emoji 和各种 Unicode 字符。老项目里常见的 utf8 实际上是 utf8mb3,遇到特殊字符会报错,新项目直接用 utf8mb4 是最稳妥的。

用 MySQL8.0 还容易踩两个和驱动相关的坑。第一,JDBC 连接 URL 里要加serverTimezone=Asia/Shanghai,否则驱动会拿本机默认时区和数据库时区做换算,导致时间字段差 8 小时。第二,MySQL8.0 默认认证插件是caching_sha2_password,如果用老版本的驱动会提示连接失败,要使用mysql-connector-j8.x 版本。这个问题在本地第一次启动项目时经常出现,后面部署章节还会提到。

3. 核心功能实现与关键代码

3.1 数据库建模:先有关系,才有逻辑

数据库是整套系统的基础。我建议把表结构设计成下面这样,既清楚又不过度设计:

  • sys_user:登录用户表,包含 username、password、role、关联的学生或辅导员 id;
  • student:学生档案表,包含学号、姓名、性别、身份证、电话、学院、专业、班级等;
  • college、major、class_info:学院、专业、班级三级基础表;
  • registration_info:报到信息表,字段有到校时间、交通方式、到达车站、陪同人数;
  • dormitory、bed:宿舍表与床位表,床位包含性别属性和是否已分配状态;
  • notice:公告表;
  • material_record:物资领用记录表,记录每个学生领取了哪些物资;
  • material_item:物资条目表。

学生信息表建表语句可以参考下面这段,真实项目中还会再加少量冗余字段。

CREATE TABLE `student` ( `id` bigint NOT NULL AUTO_INCREMENT, `student_no` varchar(20) NOT NULL COMMENT '学号', `name` varchar(50) NOT NULL COMMENT '姓名', `gender` tinyint DEFAULT NULL COMMENT '1男 2女', `id_card` varchar(18) DEFAULT NULL COMMENT '身份证号', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `college_id` bigint DEFAULT NULL COMMENT '学院id', `major_id` bigint DEFAULT NULL COMMENT '专业id', `class_id` bigint DEFAULT NULL COMMENT '班级id', `bed_id` bigint DEFAULT NULL COMMENT '床位id', `status` tinyint DEFAULT 0 COMMENT '报到状态:0未报到 1已填报 2已到校 3已完成', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_student_no` (`student_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';

有两个建模上的经验值得分享。第一,报到状态不要散落在多个表里,最好在学生表里维护一个status,同时在报到信息表里存具体数据。这样列表页查状态只需要查主表,不用每次 join 报到表。第二,学院、专业、班级三级表保留sort排序字段,否则前端下拉框只能按 id 排,看起来顺序很乱。索引方面,status和college_id是高频查询字段,可以建联合索引;id_card和phone属于隐私字段,虽然业务上有唯一性要求,但前端展示时要做脱敏处理,接口也不要一次性把整个表拖回去。

3.2 登录认证:JWT + 拦截器

登录认证是答辩时逃不开的问题,建议每个项目都认真写一遍。我的做法是登录成功后生成一个 JWT 返回给前端,前端存到 localStorage 里,后续每次请求在Authorization请求头上带这个 token。后端有一个拦截器统一校验 token,放行登录接口、静态资源和图片访问路径,其他接口都必须通过校验。

后端生成的逻辑大概是这样的:

String token = Jwts.builder() .setSubject(String.valueOf(user.getId())) .claim("username", user.getUsername()) .claim("role", user.getRole()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000L)) .signWith(SignatureAlgorithm.HS256, jwtSecret) .compact();

拦截器校验的时候,核心代码类似下面:

String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(401); return false; } try { Claims claims = Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token.substring(7)) .getBody(); request.setAttribute("currentUserId", claims.getSubject()); request.setAttribute("currentUserRole", claims.get("role", String.class)); } catch (Exception e) { response.setStatus(401); return false; } return true;

有两个细节很容易被忽略。一是密钥不要写死在代码里,放到application.yml中,用@Value注入;二是拦截器排除路径要写完整,否则你连登录接口都调不通。常见的写法就是用registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/**").excludePathPatterns("/api/auth/login", "/uploads/**")。

再说密码问题。即使这只是课程设计,也不能把密码明文存进数据库。我建议用 BCrypt 算法加密,Spring Security 的 crypto 工具包单独引用也可以。学生批量导入时给一个统一初始密码(比如身份证后六位),用户登录后强制修改,这个流程演示起来很加分。

3.3 统一返回结果与全局异常处理

前后端分离项目里,接口返回结构最好统一,否则前端很难处理错误场景。我定义了一个最简单的Result<T>结构:

public class Result<T> { private Integer code; private String message; private T data; }

成功时code=200,业务异常时code=500,未登录时code=401。配合一个全局异常处理器,把参数校验错误、业务异常、未知异常都转成统一结构返回,前端 axios 拦截器里只需要判断code就能弹出统一提示。这个设计看起来基础,但很多学生项目都没有做,导致前端每个接口要单独处理错误,代码非常冗余。

3.4 学生端报到接口的实现要点

报到接口的业务核心是“一个学生最多只有一条报到记录”。如果第一次填报时插入,后面修改时更新,就需要先查后写。最简单的实现是:

public RegistrationInfo saveRegistration(RegistrationRequest req, Long studentId) { RegistrationInfo exist = registrationMapper.selectOne(new LambdaQueryWrapper<RegistrationInfo>() .eq(RegistrationInfo::getStudentId, studentId)); if (exist == null) { RegistrationInfo info = new RegistrationInfo(); BeanUtils.copyProperties(req, info); info.setStudentId(studentId); info.setRegTime(LocalDateTime.now()); registrationMapper.insert(info); return info; } exist.setArriveDate(req.getArriveDate()); exist.setTransport(req.getTransport()); exist.setArriveStation(req.getArriveStation()); exist.setCompanionNum(req.getCompanionNum()); registrationMapper.updateById(exist); return exist; }

这段代码在课设场景里足够用。但如果考虑并发,比如同一个学生同时提交两次请求,理论上可能插出两条记录。更稳妥的做法是在registration_info表的student_id字段上建唯一索引,失败时让数据库兜底。讲项目的时候如果能主动提到这个并发细节,会很有加分效果。

另外,到校时间、交通方式、陪同人数这些信息不能由前端随意填字符串,最好用字典值控制。比如交通方式只能是“火车、高铁、飞机、汽车、自驾”这几个枚举,否则统计报表里会出现一堆不规范的文字。

3.5 前端 axios 封装与路由守卫

前端的基础设施里,axios 封装和路由守卫必须写。axios 封装的核心是请求拦截器统一加 token,响应拦截器统一处理 401 跳登录页。代码比较简单:

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 = `Bearer ${token}` } return config }) request.interceptors.response.use( res => res.data, err => { if (err.response?.status === 401) { localStorage.removeItem('token') window.location.href = '/login' } return Promise.reject(err) } ) export default request

路由守卫部分,我最想提醒的是meta.roles的使用。很多新人的做法是进入组件后再判断当前用户角色,如果没权限就弹个提示,但这样已经打开了页面框架,用户体验不好。更好的方式是在路由配置里给每个页面声明允许访问的角色,然后在全局前置守卫里拦截:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.public) { return next() } if (!token) { return next('/login') } if (to.meta.roles && !to.meta.roles.includes(store.user.role)) { return next('/403') } next() })

这样做的好处是用户即使手动在浏览器地址栏输入一个未授权的 URL,也会被路由守卫挡在外面,而不是等到数据请求失败才报错。

3.6 文件上传:学生材料处理

迎新系统里通常需要上传录取通知书、证件照、体检报告等材料。简化版的上传逻辑是保存到本地磁盘目录,返回相对路径:

@PostMapping("/upload") public Result upload(MultipartFile file, @RequestParam String type) { if (file.isEmpty()) { return Result.error("请选择文件"); } String originalFileName = file.getOriginalFilename(); String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); String newFileName = UUID.randomUUID().toString().replace("-", "") + suffix; Path dir = Paths.get(uploadDir, type); Files.createDirectories(dir); file.transferTo(dir.resolve(newFileName)); return Result.success("/uploads/" + type + "/" + newFileName); }

这里有两个必须做的校验:一是文件大小限制,建议不超过 5MB;二是后缀名白名单校验,只允许 jpg、png、pdf 等有限类型,不然有人传一个超大文件或者危险文件,服务器性能和安全都会出问题。本地磁盘存储适合演示和课程设计,如果以后想正式上线,替换成对象存储服务即可,接口逻辑几乎不用变。

4. 把项目从源码跑起来:本地部署完整流程

4.1 环境准备清单

在本地跑通前后端之前,先检查下面这几个环境组件:

软件建议版本用途
JDK8 以上运行 SpringBoot 后端
Maven3.6 以上构建后端工程
MySQL8.0.x保存业务数据
Node.js16 以上运行前端工程,建议 LTS
IDEA任何较新版本导入和调试项目

Node 版本这里单独说一下。Vite 3 和部分依赖对旧版 Node 不友好,如果 Node 版本过老,npm install时会报引擎不兼容的错误。如果机器上已经有多个 Node 版本,建议用版本管理工具切换后再开始。

4.2 数据库初始化与配置修改

第一步创建数据库:

CREATE DATABASE welcome_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

然后执行项目里提供的init.sql或database.sql脚本,表结构和演示数据会一次性建好。接着找到后端配置application.yml,修改数据库连接信息:

spring: datasource: url: jdbc:mysql://localhost:3306/welcome_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 改成你自己的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml configuration: map-underscore-to-camel-case: true

这里最容易出错的环节就是忘记把密码改成本地数据库的实际密码。很多人第一次启动项目,等了一两分钟才发现日志里报的是Access denied for user 'root'@'localhost',原因就是配置文件和本机环境对不上。

4.3 启动后端工程

后端工程用 IDEA 导入以后,等待 Maven 把依赖下载完成,然后找到主启动类直接运行。也可以用命令行:

cd backend mvn spring-boot:run

启动成功后,默认服务端口是 8080。建议先用 Postman 或浏览器调用一下登录接口,确认能正常返回 token 后再打开前端页面。这样如果后续页面里看不到数据,至少能判断不是后端问题。

4.4 启动前端工程

前端目录下执行:

cd frontend npm install npm run dev

如果网络环境比较慢,建议先配置npm registry使用国内镜像,否则依赖安装阶段可能卡很久。启动成功后会看到类似http://localhost:5173的地址。前端开发环境的跨域代理配置在vite.config.js里:

export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

配置代理后,前端请求/api/login会自动转发到后端的http://localhost:8080/api/login,页面里不需要写完整域名,避免了手动处理跨域。

4.5 常见启动失败排查速查

报错现象可能原因解决办法
Access denied for user数据库账号或密码不对检查 application.yml 的 username 和 password
Unknown database数据库不存在或库名写错先手动创建数据库并执行 SQL 脚本
Connection refusedMySQL 服务没启动启动本地 MySQL 服务
端口 8080 被占用电脑上已有服务占用端口换一个端口启动,或结束占用进程
前端 npm install 报 ERESOLVENode 版本或依赖冲突使用 Node 16 LTS,删除 node_modules 重装
页面登录后接口 401token 没带上或格式不对检查请求头是否带 Authorization: Bearer token
LocalDateTime 显示乱码前后端时间格式不一致后端配置 Jackson 时间格式,前端用 dayjs 格式化

5. 开发过程中踩过的坑与优化建议

5.1 五个值得记住的坑

第一个坑是 MyBatis-Plus 分页插件不生效。很多人把分页插件加到配置里以后,发现page.getRecords()还是返回所有数据,或者 SQL 里没有 limit 语句。原因是新版 MyBatis-Plus 的插件机制变了,需要创建MybatisPlusInterceptor并注册PaginationInnerInterceptor,而且拦截器顺序有讲究。如果分页没效果,优先检查插件配置。

第二个坑是时区问题。后端存的是LocalDateTime,前端拿到后显示出来要么相差 8 小时,要么带着一个难看的英文字母。我建议在后端做全局 Jackson 配置:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

前端如果还需要更精细的格式,用 dayjs 或直接格式化即可。

第三个坑是权限按钮不隐藏。很多项目做了菜单权限,却没有控制页面内部的“删除”“修改”按钮。这会导致角色绕过页面路由直接调后端接口。正确的做法是前端按角色控制按钮显示,后端接口再校验一次权限,双保险。

第四个坑是列表查询 N+1 问题。比如查学生列表时,如果每行的专业名称都要单独调一次selectById,数据量一大接口就会很慢。更好的方式是一次 left join 把专业名称、班级名称带出来,或者用@TableField(exist = false)放展示字段,在自定义 SQL 里一次性查出。

第五个坑是 MySQL 的sql_mode问题。统计报表里用GROUP BY时,如果数据库开着ONLY_FULL_GROUP_BY,会报“Expression #1 of SELECT list is not in GROUP BY clause”。开发和部署环境的数据可能不一样,所以要在建库后统一确认sql_mode,或者写 SQL 时用ANY_VALUE()处理非聚合字段。

5.2 让项目在答辩中更抢眼的三个优化

如果时间允许,我建议在基础功能之外做三个提升体验的优化。

第一个是 Excel 导入导出。学院里的新生名单本来就是 Excel 文件,系统如果能支持一键导入、导出报到统计表,演示效果会非常贴近真实场景。用 EasyExcel 或 Apache POI 实现都行,重点是要提供一个导入模板下载,并在导入时给出错误行提示。

第二个是统计看板。用 ECharts 画报到趋势、各学院报到率、男女比例、宿舍入住率。这些图表的数据必须来自后端接口,不能在前端写死。后端可以用自定义 SQL 做聚合统计,例如按学院分组统计已报到人数。这个看板放在首页,一打开页面就是满满的“项目成果感”。

第三个是报到二维码。新生完成网上报到后,系统生成一个二维码,现场报到时工作人员扫描即可确认到校。前端用 qrcode 库生成二维码,后端提供一个按学号查询报到状态的接口就能跑通。这个功能实现成本不高,但演示时很有视觉冲击力。

5.3 文档与代码交付时怎么组织

标题里写着“含文档”,所以配套文档的质量也值得认真整理。一份合格的课程设计或毕业设计文档,至少应该包含这几部分:需求分析与功能清单、数据库设计说明与 ER 图、接口文档、部署说明、演示数据说明。我不建议把所有内容塞进一个上百页的 Word 文档里,而是拆成几个文件,比如 README 说明项目结构和启动方式,database.md说明表结构,api.md说明接口参数和返回值。

代码注释不要写得太密,重点注释业务逻辑和容易混淆的地方。演示数据不要包含真实身份证号、手机号,用一批完全虚构的数据就可以。这套系统的预置账号和初始密码也要写清楚,方便评审老师快速登录体验。

6. 最后一点个人体会

我把这套迎新系统完整重写过两轮,第一轮为了赶时间大量使用代码生成器,很多底层逻辑没有真正理解,后来维护的时候反而更吃力。第二轮我刻意手写了登录认证、报到流程、统计分析这三个核心模块,才把整个链路从数据库表到前端页面的逻辑彻底串通。所以我拿到一套新源码之后的习惯是,先把它完整跑起来,再挑几个核心模块删掉自己重新写一遍。能不看教程就写出来的部分,说明已经掌握了;写不出来的那部分,才是真正需要花时间啃的内容。等你能够不看代码讲清楚 JWT 的交互流程、MyBatis-Plus 分页插件的工作原理、Vue 路由守卫的执行顺序,再去答辩或找工作聊项目经历,心里就会非常踏实。

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

开源代码审查CLI工具:离线、上下文感知、可嵌入CI的生产级方案

1. 项目概述&#xff1a;一个真正能落地的开源代码审查 CLI 工具到底长什么样&#xff1f;“open-code-review”这四个字乍看像一句口号&#xff0c;但在我过去三年深度参与十几个中大型研发团队代码质量体系建设的过程中&#xff0c;它其实指向一个非常具体、非常痛的现实问题…

作者头像 李华
网站建设 2026/10/19 13:38:29

自建开源Code Review工具链:从零搭建到团队落地全指南

代码审查这活儿&#xff0c;干得好是质量防线&#xff0c;干不好就是全员打卡现场。我在团队里负责推 Code Review 机制的时候&#xff0c;试过 GitHub 自带的 PR 评审、用过 GitLab 的 Merge Request 流程&#xff0c;也踩过商业平台拖慢节奏的坑&#xff0c;最后在开源组件堆…

作者头像 李华
网站建设 2026/10/19 13:37:34

STM32L431低功耗设计:Stop模式+LPUART与RTC双路唤醒实现

简介&#xff1a;面向STM32L431低功耗开发者的工程资源包&#xff0c;聚焦Stop模式休眠与LPUARTRTC唤醒的实际调试场景&#xff0c;解决物联网终端长时间待机与快速唤醒的功耗优化问题。压缩包共包含1377个文件&#xff0c;以C源码和头文件为主&#xff0c;另有汇编、链接脚本、…

作者头像 李华
网站建设 2026/10/19 13:36:48

开源式代码审查:CLI+Git+LLM的可审计实践

1. 项目概述&#xff1a;这不是一个工具&#xff0c;而是一套可落地的开源代码审查方法论“open-code-review”这个标题乍看像某个 GitHub 仓库名&#xff0c;或是某款新 CLI 工具的代号&#xff0c;但实际它指向的是一种正在快速成型的工程实践范式——以开源精神重构代码审查…

作者头像 李华
网站建设 2026/10/19 13:36:43

从问答到干活:Claude Agent SDK 实战与生产化改造指南

拿大模型 API 写一个问答机器人&#xff0c;这件事我闭着眼睛都能完成&#xff1a;一个 HTTP 请求、一段 system prompt、一条用户消息&#xff0c;回答就返回了。但一旦要让 AI 真正“干活”——查数据库、改文件、跑测试、调外部服务——普通 API 调用立刻顶不住。原因很简单…

作者头像 李华
网站建设 2026/10/19 13:35:14

AI编写教材如何降低查重率?从初稿到精修的全流程实战指南

1. AI编教材的真实痛点&#xff1a;AI初稿的"重灾区"长什么样先问大家一个问题&#xff1a;你第一次让AI帮你写教材章节的时候&#xff0c;是不是也遇到过这种情况——看起来结构工整、语言通顺、知识点好像也没大毛病&#xff0c;但交上去一查重&#xff0c;重复率直…

作者头像 李华