ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于Spring Boot与Vue的高校班级事务管理系统设计与实现

基于Spring Boot与Vue的高校班级事务管理系统设计与实现 1. 先把这个系统的边界划清楚1.1 班级事务管理系统到底管什么带过毕业设计的人都有个共识题目里带“管理系统”四个字的十个有八个最后做成了一张表加增删改查。班级事务管理系统尤其容易滑向这个坑因为“班级事务”这个说法太宽了——考勤算不算事务奖助学金评定算不算班费收支算不算宿舍卫生检查算不算如果一开始不划边界做到中期你会发现表越加越多功能越铺越散最后交上去的东西自己都说不清主线是什么。我的理解是班级事务管理系统的核心不是“记录”而是“流转”。一次请假申请从学生提交、班长初审、辅导员终审到销假归档这条链路上的状态变化才是系统的价值所在一次班级活动的报名、名额分配、签到确认、活动总结也是一条链路。把这些链路抽象成统一的“事务单”模型配上角色权限和状态机系统就有了骨架剩下的考勤、通知、统计都是挂在这个骨架上的枝干。所以这个项目适合谁来参考如果你是计算机相关专业正在做毕设的同学这套东西能直接给你提供需求分析、表结构、核心代码和开题报告写作思路如果你是刚接触前后端分离开发、想找一个业务不算复杂但五脏俱全的练手项目它也挺合适毕竟班级场景你天天在经历需求不用编。不适合的是想找高并发、分布式练手的人这个系统的量级撑死几百人别硬套微服务。前100字必须把关键词落地那我就直说高校班级事务管理系统本质上是一个带审批流的轻量级业务中台服务对象是辅导员、班委和普通学生三类角色核心价值是把散落在聊天群里的通知、接龙、表格收敛成结构化数据。1.2 三类角色决定了整个系统的权限骨架角色划分这件事很多人画用例图的时候画得很漂亮一写代码就开始偷懒搞一个role字段存个 0/1/2 就完事结果做到后面发现“班长能审批本班但不能审批别班”这种带数据范围的权限根本兜不住。我的做法是角色和数据范围分开设计。角色表里存三类身份学生、班委含班长、团支书等、辅导员。数据范围单独用一个字段或者一张关联表来描述比如班委的scope_type是CLASSscope_id是本班 ID辅导员的scope_type是GRADE或者MAJORscope_id是对应的年级或专业 ID。这样判断权限的时候就是两步先看角色能不能做这个动作再看这条数据在不在他的范围内。这种设计的直接好处是后面如果要加一个“学院管理员”角色不用改业务代码只要插一条角色记录和一个范围记录就行。坏处是实现上比单纯的 if-else 麻烦一点但这点麻烦在后期联调阶段会成倍地还给你。1.3 三个最容易把项目做废的坑第一个坑是功能贪多。开题报告里雄心壮志写了十二个模块做到第五个就开始复制粘贴最后交上去的代码里有一半是没跑通的僵尸接口。我的建议是主功能控制在五到六个每个都跑通全流程剩下的用“预留扩展”在报告里说明反而显得你有规划。第二个坑是权限校验只做前端。菜单藏起来就以为安全了后端接口裸奔。答辩的时候老师随手用接口工具一调就穿了这个场面很难看。校验必须在后端做前端隐藏只是体验优化。第三个坑是不做数据初始化。交上去的系统打开一片空白老师第一眼就扣分。至少准备一份种子数据脚本造三个班级、几十个学生、十几条不同类型的事务记录让系统打开就是“活”的。2. 技术选型为什么最后落到这套组合2.1 后端为什么选 Spring Boot 而不是别的毕设场景下选后端的逻辑跟企业里不太一样企业考虑的是团队栈、运维成本、生态成熟度毕设考虑的是“能不能在两个月内跑通并且答辩时讲得清楚”。Spring Boot 在这两点上都是稳妥解自动装配省掉了大量 XML 配置起步一个main方法就能跑生态足够大你遇到的九成问题网上都有现成答案分层结构清晰Controller-Service-Mapper 三层摆出来老师一看就知道你懂分层。也有人问能不能用 Python 的 Flask 或者 Node 的 Express当然可以代码量还更少。但答辩的时候有个现实问题国内大部分院校的评审老师对 Java 技术栈更熟悉你讲 Spring Boot 的拦截器、AOP、事务管理他能接得上话你讲 Express 的中间件洋葱模型他可能要愣一下。这不是技术优劣是沟通成本。具体版本上我倾向于 JDK 8 或 JDK 11 配 Spring Boot 2.7.x。JDK 17 配 Spring Boot 3.x 也不是不行但 Spring Boot 3 把javax.*全换成了jakarta.*很多老教程的代码直接抄会报错对新手不友好。MyBatis-Plus 用来省掉基础 CRUD 的样板代码分页插件直接用它的省得自己写。MySQL 5.7 或 8.0 都行字符集统一utf8mb4这个别省不然学生姓名里的生僻字会变成问号。JWT 做无状态认证配一个拦截器统一校验。文件上传单独抽一个工具类本地存upload/目录下数据库只存相对路径别把二进制往库里塞。2.2 前端和部署方案的取舍前端选 Vue 3 加 Element Plus理由很实际组件库现成表格、表单、弹窗、分页全都有省下来的时间可以放在业务逻辑上。用 Vue 2 也行生态更老更稳但 Vue 3 的组合式 API 在写权限判断这种复用逻辑的时候确实清爽。前后端分离的部署方式有两种。一种是前端npm run build出静态文件丢到 Nginx 里Nginx 反向代理/api到后端端口这种方式最接近生产环境答辩演示也稳另一种是偷懒把打包产物塞进 Spring Boot 的static目录打成一个大 jar双击就能跑演示方便但不够规范。我的建议是开发期用第一种练手交付的时候额外准备一个“一键启动包”作为兜底万一现场网络或者 Nginx 出问题不至于手忙脚乱。数据库连接池用 HikariCPSpring Boot 默认就是它配置里把maximum-pool-size设成 10 就够了别照抄网上的 50、100几百人的系统用不上反而容易把本地 MySQL 的连接数打满。2.3 项目目录结构建议这么切src/main/java/com/example/classaffair ├── common # 统一返回、异常、常量、工具 ├── config # 拦截器、跨域、MyBatis 配置 ├── controller ├── service │ └── impl ├── mapper ├── entity ├── dto # 入参 ├── vo # 出参 └── handler # 全局异常处理DTO 和 VO 分开这件事很多新手会嫌麻烦直接用实体类接收和返回。小项目确实能跑但一旦涉及到“创建事务单时不需要传 id 和创建时间”或者“返回列表时不希望把密码字段带出去”你就得回头改。一开始分开后面省事。3. 数据库设计这个系统真正的骨架3.1 核心表与字段设计数据库设计是这类项目里最能体现功底的部分。我不建议一上来就画 E-R 图先把几个问题在纸上写清楚有哪几类“东西”需要独立存在它们之间是一对多还是多对多哪些状态是会变化的这个系统的核心实体大致是这些表名作用关键字段sys_user统一用户表id, username, password, real_name, role, class_id, statussys_class班级id, class_name, grade, major, counselor_idbiz_affair_type事务类型字典id, type_name, need_approve, sortbiz_affair事务主表id, type_id, title, content, creator_id, class_id, status, create_timebiz_affair_flow审批流转记录id, affair_id, operator_id, action, remark, create_timebiz_notice通知公告id, title, content, publisher_id, scope, publish_timebiz_notice_read通知已读记录id, notice_id, user_id, read_timebiz_attachment附件id, biz_type, biz_id, file_name, file_path用户表用一张sys_user统管所有角色靠role字段区分而不是建三张表。理由是登录入口只有一个三张表会逼你在登录逻辑里写三段查询维护起来烦。代价是学生和辅导员的字段差异比如学生有学号、辅导员有工号得放在同一张表里用字段冗余解决。这点冗余可以接受。biz_affair表的设计是重点。它把请假、活动报名、班费申请、设备借用这些业务都收进一张表靠type_id区分公共字段标题、内容、状态、创建人、所属班级统一差异化字段用ext_json存 JSON 字符串。这种“宽表 JSON 扩展”的做法在业务类型不多、字段差异不大的场景下非常划算省掉了为每种事务建一张表的麻烦。缺点是 JSON 里的字段没法直接建索引查询如果某个类型的事务需要按扩展字段筛选那就得在建表的时候单独拆出去。3.2 状态机的设计要提前想明白状态机是审批流的核心。我见过太多项目把状态存成status字段值是 0、1、2、3然后代码里到处写if (status 1)三个月后自己都忘了 2 代表什么。我的做法是用一个枚举类把状态和流转规则都固定下来public enum AffairStatus { DRAFT(草稿, 0), PENDING(待审批, 1), APPROVED(已通过, 2), REJECTED(已驳回, 3), FINISHED(已完成, 4), CANCELED(已撤销, 5); private final String label; private final int code; // 构造、getter 省略 public static boolean canTransfer(int from, int to) { // 状态流转白名单 MapInteger, ListInteger rules new HashMap(); rules.put(0, Arrays.asList(1, 5)); rules.put(1, Arrays.asList(2, 3, 5)); rules.put(2, Arrays.asList(4)); return rules.getOrDefault(from, Collections.emptyList()).contains(to); } }有了这个白名单Service 层每次改状态前先调一次canTransfer非法流转直接抛业务异常。这一步能拦掉大量脏数据比如“已驳回的单子被改成已通过”或者“已完成的单子又被重新审批”。biz_affair_flow这张流转记录表也别省。每一次状态变化都插一条记录记录操作人、动作、备注和时间。这既是审计日志也是给学生端展示“审批进度条”的数据来源。答辩时老师问“你的审批流怎么实现的”你把这个表一展示比讲十分钟代码都管用。3.3 索引和查询上的几个实操细节数据量不大不代表不用管索引。biz_affair表上至少要有这几个索引idx_class_status (class_id, status)用于班委查看本班待办idx_creator (creator_id)用于学生查看自己提交的记录idx_create_time (create_time)用于列表倒序分页。分页这里有个容易踩的坑MySQL 的LIMIT offset, size在 offset 很大时会变慢。虽然这个系统的数据量根本到不了那种程度但写代码的时候顺手用 ID 游标分页或者干脆限制最大页数是个好习惯。至少别把size参数直接透传给 SQL用户传个 100000 进来一次查询就把内存拉爆了。在 Controller 层做一次参数校验size超过 100 就强制改成 100。还有一个细节是逻辑删除。sys_user和biz_affair都建议加一个deleted字段用 MyBatis-Plus 的TableLogic注解删除操作自动变成更新。物理删除在演示阶段看不出问题但一旦误删了数据要恢复就只能重装数据库。4. 核心功能落地一条完整链路的实现4.1 登录认证与权限拦截登录流程本身不复杂前端传用户名密码后端查库比对比对用的是 BCrypt 加密后的密文别用 MD5更别明文存。校验通过后生成 JWTpayload 里放userId、role、classId有效期设成 2 小时前端存 localStorage每次请求带在Authorization头里。真正的活在于拦截器。我写了一个AuthInterceptor实现HandlerInterceptor在preHandle里做三件事解析 token、把用户信息塞进ThreadLocal、校验当前请求路径是否在白名单里。Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BizException(401, 未登录); } Claims claims JwtUtil.parse(token); UserContext.set(new LoginUser( claims.get(userId, Long.class), claims.get(role, Integer.class), claims.get(classId, Long.class) )); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.remove(); // 必须清理否则线程池复用会串数据 }afterCompletion里的remove()是必须的。Tomcat 的线程是复用的你不清理下一个请求可能拿到上一个用户的身份这个 bug 诡异到能让人查一整天。更细粒度的权限用一个自定义注解RequireRole配合 AOP 实现标在 Controller 方法上切面里比对当前用户角色。数据范围校验放在 Service 层做因为只有 Service 才知道这条数据属于哪个班。4.2 事务申报与审批流的实现这是整个系统的主干我按一次请假申请来拆。学生端填表单选事务类型请假填开始时间、结束时间、事由上传证明材料点提交。后端收到后做三件事校验时间区间合法性结束时间必须晚于开始时间且不能早于当前时间、校验该学生在此时间段内没有其他未完结的请假、插入biz_affair记录并把状态置为PENDING。这里有个业务细节值得说请假这种事务往往有“时长阈值”的概念比如三天以内班长批就行三天以上要辅导员批。这个规则不要硬编码在 if-else 里做成一张biz_approve_rule配置表字段是type_id、min_days、max_days、approver_role。Service 里根据申请时长去查对应的审批角色把待办推给对应的人。这样以后规则变了改数据就行不用改代码。当然毕设层面用配置类写死也可以接受但报告里提一句“预留了规则可配置化能力”是加分项。审批人登录后看到待办列表列表查询条件是status PENDING且class_id在自己的数据范围内。点同意后端先校验当前状态确实是PENDING再校验当前用户确实有这个权限然后更新状态、插入流转记录、给学生发一条站内通知。三个操作放在同一个Transactional方法里保证要么全成要么全败。驳回的逻辑要多一个字段驳回原因。这个原因要写进流转记录的remark里学生端能看到否则学生只知道被拒了但不知道为什么体验极差。4.3 通知与已读状态的处理通知模块看着简单但有个经典问题怎么知道谁读了、谁没读。笨办法是biz_notice表加一个read_user_ids字段存逗号拼接的 ID查询的时候用FIND_IN_SET。数据量小的时候能跑但这是典型的反范式设计一旦要统计已读率或者做“未读红点”就会很难受。正规做法是单独一张biz_notice_read关联表(notice_id, user_id)建唯一索引。前端拉通知列表的时候用LEFT JOIN关联这张表read_time为 null 的就是未读。红点数量就是未读条数一个count查询解决。通知的可见范围也要设计。scope字段存ALL、CLASS、GRADE配合scope_id。查询时拼条件ALL的直接可见CLASS的要求scope_id等于当前用户班级GRADE的要求年级匹配。这个条件建议写成一个 MyBatis 的sql片段复用避免每个查询都手写一遍拼错。4.4 统计看板与数据导出统计模块是答辩时最容易出彩的地方因为它能直观展示“系统产生了什么价值”。我做了三块统计事务类型分布饼图、近三十天事务提交趋势折线图、各班级事务处理及时率柱状图。前两个用一条GROUP BY查询就够第三个稍微复杂点需要计算每条事务从PENDING到终态的时间差再按班级聚合。SELECT a.class_id, c.class_name, ROUND(AVG(TIMESTAMPDIFF(HOUR, a.create_time, f.create_time)), 1) AS avg_hours, COUNT(*) AS total FROM biz_affair a JOIN biz_affair_flow f ON f.affair_id a.id AND f.action IN (APPROVE, REJECT) JOIN sys_class c ON c.id a.class_id WHERE a.create_time DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY a.class_id, c.class_name ORDER BY avg_hours ASC;导出功能用 EasyExcel别用 POI 原生 API那个写起来太啰嗦。导出的时候注意两点一是大数据量要分页查再写别一次selectList全捞出来二是字段名用中文表头日期格式化好导出的表格能直接给人看才有意义。5. 开题报告怎么写才不像模板5.1 研究背景和意义别写成套话开题报告的第一段十份里有八份是“随着信息技术的飞速发展高校信息化建设不断深入……”。这种开头评审老师一天看几十份直接跳过。我建议从具体问题切入写你观察到的真实场景。比如班级事务长期依赖即时通讯群一条通知发出去回复“收到”的人刷屏真正重要的信息被淹没请假靠私聊辅导员手机里几百条记录没法归档班费收支用表格记录学期末对账费时费力。这些都是可以写进报告的具体痛点比空泛的“提高效率”有说服力得多。研究意义分两层写就够了实践层面是解决上述具体问题理论层面是探索轻量级审批流在班级这种小规模组织中的适配方案。别硬扯“填补领域空白”一个班几十人的系统填补不了什么空白老老实实写“具备一定的工程实践参考价值”反而得体。5.2 国内外研究现状的写法这部分最容易被写废因为大部分同学根本没读过外文文献硬编会露馅。我的建议是分三类来写通用办公协作平台这类产品功能全但重班级场景用不上那么多、校园信息化系统这类系统通常由学校统一建设颗粒度到院系班级层面的个性化需求覆盖不到、开源的学生管理系统这类项目往往功能齐全但技术栈陈旧或者文档缺失二次开发成本高。每一类写清楚“它做了什么、它没解决什么”最后自然过渡到“所以需要一个轻量、聚焦班级场景、可自主部署的方案”。这个逻辑链条是站得住的而且不需要你捏造文献。参考文献里放几篇国内期刊的班级管理系统论文再放两三本 Spring Boot 和 Vue 的技术书加上官方文档链接足够了。5.3 研究内容和技术路线要能对上很多开题报告的毛病是前面说要做智能推荐、大数据分析后面的技术路线里全是 CRUD前后对不上。评审老师一眼就能看出是凑字数。我的原则是研究内容写几条技术路线就对应几条功能模块和表结构也能对应上。比如研究内容第一条是“班级事务申报与多级审批流程设计”那技术路线里就要有状态机设计、流转记录表设计、审批规则配置这几项数据库设计里就要有biz_affair、biz_affair_flow这些表。三处能对上说明你真的想清楚了。进度安排按周来排别排得太满。留出两周机动时间给联调和 Bug 修复这个预留不是浪费时间是给自己留活路。答辩前一周专门用来准备演示数据和 PPT这个环节的重要性不亚于写代码。可行性分析分技术、经济、操作三个维度写。技术可行性讲技术栈成熟、资料丰富经济可行性讲全部使用开源免费组件一台普通电脑就能跑操作可行性讲界面遵循常见交互习惯用户不需要培训。三个维度各写一小段别写太长这部分不是重点。6. 踩过的坑和排查实录6.1 环境与依赖类问题中文乱码是最常见的。三个地方要统一数据库建库时指定utf8mb4JDBC 连接的 URL 里加characterEncodingutf8前端请求头设置Content-Type: application/json;charsetUTF-8。三处都对了还乱码就检查 Nginx 有没有配置charset utf-8。跨域问题在前后端分离开发阶段必现。开发期在配置类里加一个CorsConfig放行本地端口生产环境用 Nginx 同源代理就不存在跨域了。注意allowedOrigins不要图省事写*同时开allowCredentials浏览器会直接拒绝。时间字段差八小时这是时区问题。JDBC URL 里加serverTimezoneAsia/Shanghai实体类用LocalDateTime而不是DateJackson 序列化配置spring.jackson.time-zoneGMT8。这三步做完基本就不会错了。6.2 业务逻辑类问题并发提交重复审批。两个辅导员同时点同意可能都会通过。解决办法是在更新状态时加乐观锁SQL 写成UPDATE biz_affair SET status ? WHERE id ? AND status ?用影响行数判断是否更新成功返回 0 就说明被别人抢先了提示“该事务已被处理”。分页总数不对。用 MyBatis-Plus 的分页插件时如果 SQL 里自己写了UNION或者复杂的子查询总数统计可能出错。遇到这种情况就把 count 查询单独写一个 SQL别依赖自动生成。文件上传后访问 404。Spring Boot 默认不允许直接访问项目外的目录需要配置WebMvcConfigurer把上传目录映射成静态资源路径。或者干脆把文件存到resources/static下的子目录但这种做法在打成 jar 之后会有问题不推荐。6.3 答辩时可能被问到的问题整理了一张表提前准备比临场发挥靠谱问题回答思路为什么不用 Redis系统并发量在百人级别本地缓存加数据库索引足够引入 Redis 会增加部署复杂度不符合轻量定位审批流怎么支持多级当前实现了两级班委、辅导员流转记录表已按多级设计扩展时只需在规则表里增加层级配置数据安全怎么保证密码 BCrypt 加盐存储接口层做 JWT 校验和角色鉴权敏感操作记流转日志如果班级合并了怎么办班级表设计了年级和专业字段合并时批量更新用户的class_id历史事务记录保留原class_id不受影响系统的瓶颈在哪在统计报表的聚合查询上数据量增长后可以通过加汇总表或者定时预计算解决这些问题背后的逻辑其实就是让你证明自己不是照着教程抄的。答不上来不可怕胡编才可怕坦诚说“这部分目前是简化处理后续可以这样扩展”是完全能接受的答案。7. 交付与演示的最后一步7.1 打包部署的完整流程前端在项目根目录执行npm run build产物在dist目录。把dist整个拷到服务器Nginx 配置这样写server { listen 80; server_name localhost; location / { root /opt/app/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files那一行不能少否则前端路由刷新会 404。后端用mvn clean package -DskipTests打包产出的 jar 用nohup java -jar xxx.jar --spring.profiles.activeprod app.log 21 启动。生产配置里把数据库密码、JWT 密钥放到单独的application-prod.yml不要提交到代码仓库。7.2 演示前的检查清单演示当天出问题的项目八成倒在下面这几条上提前挨个过一遍数据库连接池能不能正常拿到连接重启一次 MySQL 试试种子数据是否完整学生账号能不能登录待办列表里有没有数据上传附件的目录是否有写权限浏览器缓存是否清过前端静态资源版本对不对关键流程是否准备了第二条数据第一条演示失败还有备份另外准备一个纯静态的演示视频作为终极兜底五分钟把主流程录一遍。现场投影仪分辨率不对、网络断了、电脑蓝屏这些事都发生过有视频至少不会全场尬住。我个人在带这类项目时有个习惯交稿前一周让完全没参与开发的同学按着使用手册独立操作一遍。他卡住的地方就是文档要补的地方也是答辩时老师可能问的地方。这个笨办法屡试不爽比自己对着一遍遍点点点有效得多。这个系统后续真要扩展方向其实挺清楚把审批规则做成可视化配置界面、给事务类型加自定义表单设计器、接入消息推送让待办不再依赖用户主动登录查看。但这些都是后话先把主干跑通、把报告写实比什么都重要。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表