基于Spring Boot的毕业审核系统设计与实现:从规则引擎到前后端分离

基于Spring Boot的毕业审核系统设计与实现:从规则引擎到前后端分离 简介本资源是一套面向计算机专业本科生的SpringBoot毕业设计实战项目聚焦高校学生毕业审核业务场景解决传统人工审核效率低、流程不透明、数据难追溯等痛点。压缩包共81个文件含74个Java核心业务类覆盖Controller、Service、Entity及Repository层、2个properties配置文件含数据库与安全配置、1个pom.xml依赖管理文件、1个mvnw.cmd启动脚本等结构完整可直接导入IDE运行总大小仅110KB轻量易学。已有57人下载学习适合SpringBoot入门到进阶者参考——不仅提供从数据库建模、RESTful接口开发、Thymeleaf页面渲染到Spring Security权限控制的全链路代码实现还包含清晰的模块划分用户管理、成绩录入、审核流程引擎、操作日志和标准化的Maven多模块组织方式便于理解毕业设计系统级开发规范与工程实践细节。1. 项目缘起从“毕业审核”这个痛点说起又到了一年一度的毕业季作为计算机专业的学生或者即将指导毕业设计的老师你是不是正在为“毕业设计做什么”而发愁选题既要体现技术深度又要贴合实际应用还得能讲出一个完整的故事。我当年也经历过这个阶段后来在参与学校信息化建设时发现“学生毕业审核”这个环节简直是天然的、完美的毕业设计课题素材。它不像电商、博客系统那样泛滥但又实实在在地存在于每一所高校流程清晰、业务规则明确技术栈可深可浅非常适合作为一个综合性的练手项目。这个系统的核心目标很简单自动化、准确地判断一个学生是否符合毕业要求。听起来简单但背后涉及的业务逻辑却相当复杂。它需要整合学生在校期间的所有关键数据课程成绩、学分、绩点、实习实践、论文答辩、奖惩记录甚至包括欠费情况。传统的人工审核靠教务老师对着Excel表格和纸质档案一条条核对效率低、易出错还经常因为规则理解不一致引发争议。一个设计良好的毕业审核系统就是要用代码把这些繁杂的规则固化下来实现一键审核、结果可追溯。为什么选择Spring Boot作为技术栈这几乎是当前Java后端开发的“事实标准”。对于毕业设计而言它的优势太明显了约定大于配置能让你快速搭建起一个可运行的项目骨架把精力集中在业务逻辑的实现上而不是没完没了的XML配置。它内嵌了Tomcat打包即是一个可执行的JAR部署演示极其方便。丰富的Starter依赖让你集成数据库、安全框架、缓存、消息队列等都变得轻而易举。用Spring Boot来做这个课题技术选型上既主流又稳妥能很好地展示你对现代Java开发框架的掌握程度。接下来我将以一个“过来人”和项目实践者的角度为你拆解如何从零开始设计并实现一个结构清晰、功能完整的学生毕业审核系统。我们会从需求分析、技术选型一直讲到数据库设计、核心业务实现以及那些容易踩坑的细节。你可以把这篇文章看作一份超详细的“毕设通关指南”。2. 系统核心需求与业务规则拆解在动手写代码之前我们必须把业务规则吃透。毕业审核不是一个简单的“是”或“否”而是一个由多项子规则组成的决策树。我们需要将这些规则清晰地定义出来这是整个系统的基石。2.1 毕业资格的多维度校验一个学生能否毕业通常需要满足以下几个硬性条件我们可以将其建模为不同的“审核项”学分与课程审核这是最核心的部分。学生必须修满培养方案规定的总学分并且各类别课程如公共必修课、专业必修课、专业选修课、通识选修课等的学分也必须分别达标。这里容易出现的问题是课程可能会有“先修课”要求或者某些课程有“最低分数”限制例如学位课要求70分以上。绩点GPA审核很多学校对毕业有最低平均绩点要求比如不低于2.0或70分。计算GPA时需要根据所有课程的成绩和学分进行加权平均。毕业设计论文审核学生必须完成毕业设计或论文的选题、开题、中期检查、答辩等一系列环节并且最终答辩成绩合格。这部分流程性强需要跟踪多个节点的状态。实习与实践环节审核部分专业要求学生完成一定时长的专业实习或社会实践并提交合格的报告。纪律与缴费审核学生在校期间不能有未解除的严重纪律处分如留校察看并且需要结清所有学费、住宿费等费用。关键设计点我们不能把这些规则硬编码在Java代码里。更好的做法是将其“配置化”。可以为每一条规则定义一个唯一的规则代码如CREDIT_TOTAL,GPA_MIN,DISSERTATION_PASS并关联一个执行该规则校验的处理器RuleHandler。这样当学校的毕业政策发生微调时比如总学分要求从160分调整为165分我们只需要在数据库或配置文件中修改规则参数而无需重新发布系统。2.2 系统功能性需求梳理除了核心的审核规则一个完整的系统还需要支撑以下功能学生信息管理作为基础数据需要与学生基本信息数据库同步或对接。审核任务触发支持按批次如“2025届毕业生”发起审核也支持针对单个学生进行手动审核。审核过程与结果展示审核不是瞬间完成的系统需要清晰地展示每个学生的每一条规则的审核状态通过/不通过/待审核、详细说明如“专业选修课学分不足还差2学分”以及最终结论。审核结果申诉与复核对于审核不通过的结果应提供申诉渠道并由教务老师进行人工复核复核后可以覆盖系统结果。这个流程需要被完整记录。统计与报表按学院、专业、班级统计毕业审核的通过率、不通过的主要原因分布等为教学管理提供数据支持。权限管理学生只能查看自己的审核状态辅导员可以查看所带班级的情况教务员拥有发起审核、复核等高级权限。注意在实际开发中务必与你的“客户”可能是你的导师或你假想的学校教务部门反复确认这些规则。一份清晰的、双方确认的《需求规格说明书》或功能清单能帮你省去后期大量的返工时间。3. 技术栈选型与项目初始化基于Spring Boot我们可以快速搭起一套稳健的后端技术组合。这里的选择兼顾了毕设的演示效果和实际的生产可用性。后端核心Spring Boot 3.x建议选择最新的稳定版如3.2.x。它基于Java 17性能和新特性支持更好。不用担心“版本太高”Spring Boot的兼容性做得很好且新版本教程丰富。Spring MVC用于构建RESTful API处理HTTP请求。Spring Data JPA作为ORM框架极大简化数据库操作。相比MyBatisJPA在定义复杂实体关系时更优雅适合业务模型相对固定的管理系统。Spring Security处理认证与授权。毕业审核系统涉及敏感数据权限控制是必须的。数据库MySQL 8.x或PostgreSQL两者都是优秀的选择。MySQL更常见资源更多PostgreSQL在复杂查询和JSON支持上更有优势。对于毕设任选其一即可。前端可选但强烈建议Vue 3 Element Plus前后端分离是主流。Vue 3组合式API开发体验好Element Plus组件库丰富能快速搭建出美观的管理界面。即使你后端更强学一点基础的前端也能让项目演示效果大幅提升。Thymeleaf如果你不想分离想用传统的服务端渲染Spring Boot官方集成的Thymeleaf模板引擎是个不错的选择。但对于需要丰富交互的审核页面前后端分离更合适。开发工具IntelliJ IDEAJava开发首选对Spring Boot的支持无与伦比。社区版就足够完成毕设。Maven项目管理工具。Spring Initializr生成的项目默认使用Maven生态成熟。项目初始化实战最推荐的方式是使用 Spring Initializr 网站或IDEA内建的Spring Initializr功能来生成项目骨架。访问 start.spring.io选择Project: MavenLanguage: JavaSpring Boot: 选择3.2.x或3.3.x的稳定版本。Project Metadata填写你的Group如com.yourname和Artifact如graduation-audit-system。Packaging: JarJava: 选择17或21。在Dependencies中添加Spring Web,Spring Data JPA,MySQL Driver,Spring Security,Lombok极大减少Getter/Setter等模板代码。点击Generate下载压缩包解压后用IDEA打开。一个关键的避坑点打开项目后首先检查pom.xml文件。有时因为网络问题依赖可能下载不完整。你可以尝试在IDEA中右键点击pom.xml选择Maven - Reload project。如果遇到依赖问题可以尝试将Maven仓库源改为国内镜像如阿里云镜像速度会快很多。4. 数据库设计与核心实体建模数据库设计是系统的“骨架”设计得好后续开发事半功倍。我们围绕核心业务设计以下几张主表4.1 学生信息 (student)这张表通常来自学校统一的学生数据库我们这里设计一个最小集用于演示。CREATE TABLE student ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, student_id varchar(20) NOT NULL COMMENT 学号, name varchar(50) NOT NULL COMMENT 姓名, college varchar(100) COMMENT 学院, major varchar(100) COMMENT 专业, class_name varchar(50) COMMENT 班级, enrollment_year int COMMENT 入学年份, status tinyint DEFAULT 1 COMMENT 在校状态1在读2休学3毕业4结业等, PRIMARY KEY (id), UNIQUE KEY uk_student_id (student_id) ) COMMENT学生基本信息;4.2 课程成绩 (course_grade)记录学生每一门课的成绩是学分和绩点计算的源头。CREATE TABLE course_grade ( id bigint NOT NULL AUTO_INCREMENT, student_id varchar(20) NOT NULL COMMENT 关联学号, course_code varchar(50) NOT NULL COMMENT 课程代码, course_name varchar(200) NOT NULL COMMENT 课程名称, course_type varchar(50) COMMENT 课程类型公共必修、专业选修等, credit decimal(5,2) NOT NULL COMMENT 学分, score decimal(5,2) COMMENT 成绩百分制, gpa_point decimal(3,2) COMMENT 绩点如90分对应4.0, academic_year varchar(20) COMMENT 学年如2023-2024, semester tinyint COMMENT 学期1或2, PRIMARY KEY (id), INDEX idx_student_id (student_id) ) COMMENT学生课程成绩表;4.3 毕业审核规则 (graduation_rule)将审核规则配置化存储。CREATE TABLE graduation_rule ( id bigint NOT NULL AUTO_INCREMENT, rule_code varchar(50) NOT NULL COMMENT 规则代码如CREDIT_TOTAL, rule_name varchar(100) NOT NULL COMMENT 规则名称如总学分要求, rule_description varchar(500) COMMENT 规则描述, rule_handler_bean varchar(200) COMMENT 对应Spring Bean的名称用于执行校验, is_active tinyint DEFAULT 1 COMMENT 是否启用, params json COMMENT 规则参数JSON格式。如{requiredCredits: 160}, priority int DEFAULT 0 COMMENT 规则执行优先级, PRIMARY KEY (id), UNIQUE KEY uk_rule_code (rule_code) ) COMMENT毕业审核规则定义表;4.4 审核任务与结果 (audit_task,audit_result)这是系统的核心业务表记录了每一次审核的执行情况。-- 审核任务表记录一次批量或单人的审核请求 CREATE TABLE audit_task ( id bigint NOT NULL AUTO_INCREMENT, task_name varchar(200) COMMENT 任务名称如2025届毕业资格初审, audit_type tinyint NOT NULL COMMENT 审核类型1批量2单人, target_student_ids text COMMENT 批量审核时的学生ID列表JSON数组, initiator_id bigint COMMENT 发起人用户ID, status tinyint DEFAULT 0 COMMENT 任务状态0待执行1执行中2已完成3失败, start_time datetime, end_time datetime, created_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) COMMENT审核任务表; -- 审核结果详情表记录每个学生每条规则的审核结果 CREATE TABLE audit_result ( id bigint NOT NULL AUTO_INCREMENT, task_id bigint NOT NULL COMMENT 关联审核任务ID, student_id varchar(20) NOT NULL COMMENT 学生学号, rule_code varchar(50) NOT NULL COMMENT 规则代码, rule_name varchar(100) COMMENT 规则名称冗余存储便于查询, pass_status tinyint NOT NULL COMMENT 是否通过1通过0不通过, details text COMMENT 审核详情说明如不通过原因, audit_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 审核时间, operator varchar(50) COMMENT 审核执行者系统或人工, PRIMARY KEY (id), INDEX idx_task_student (task_id, student_id), INDEX idx_student_rule (student_id, rule_code) ) COMMENT审核结果详情表;4.5 申诉与复核记录 (audit_appeal)CREATE TABLE audit_appeal ( id bigint NOT NULL AUTO_INCREMENT, result_id bigint NOT NULL COMMENT 关联的原始审核结果ID, student_id varchar(20) NOT NULL, appeal_reason text NOT NULL COMMENT 申诉理由, appeal_time datetime DEFAULT CURRENT_TIMESTAMP, status tinyint DEFAULT 0 COMMENT 申诉状态0待处理1已驳回2已通过, reviewer_id bigint COMMENT 复核人ID, review_comment text COMMENT 复核意见, review_time datetime, final_pass_status tinyint COMMENT 复核后的最终状态, PRIMARY KEY (id), INDEX idx_result_id (result_id) ) COMMENT审核结果申诉复核表;实体关系映射JPA Entity示例 以AuditResult为例展示如何在Java中映射Entity Table(name audit_result) Data // Lombok注解自动生成getter/setter等方法 public class AuditResult { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name task_id, nullable false) private Long taskId; Column(name student_id, nullable false, length 20) private String studentId; Column(name rule_code, nullable false, length 50) private String ruleCode; Column(name rule_name, length 100) private String ruleName; Column(name pass_status, nullable false) private Boolean passStatus; // 使用Boolean对应tinyint(1) Column(name details, columnDefinition TEXT) private String details; CreationTimestamp Column(name audit_time, updatable false) private LocalDateTime auditTime; // 这里省略了与其他实体的关联关系如ManyToOne到Student }经验之谈在设计字段时特别是状态字段如status,pass_status使用tinyint并在代码中用枚举Enum来管理比直接用字符串如‘PASS’更节省空间查询效率也更高。同时在AuditResult中冗余存储rule_name是一个常见的反范式设计它避免了每次查询结果时都要去关联graduation_rule表用空间换时间在读取频繁的场景下是值得的。5. 核心业务逻辑规则引擎与审核流程实现这是整个系统的“大脑”。我们如何让系统灵活地执行各种审核规则这里引入一个简单的“规则引擎”设计模式。5.1 定义规则接口与抽象类首先定义一个统一的规则执行接口public interface GraduationRuleHandler { /** * 规则代码必须与数据库中的rule_code一致 */ String getRuleCode(); /** * 执行规则审核 * param studentId 学生学号 * param ruleParams 该规则的配置参数从数据库的json字段解析而来 * return 审核结果 */ AuditResult execute(String studentId, MapString, Object ruleParams); }然后提供一个抽象基类来处理公共逻辑public abstract class AbstractRuleHandler implements GraduationRuleHandler { Autowired protected StudentService studentService; Autowired protected CourseGradeService courseGradeService; Override public AuditResult execute(String studentId, MapString, Object ruleParams) { AuditResult result new AuditResult(); result.setStudentId(studentId); result.setRuleCode(this.getRuleCode()); result.setRuleName(this.getRuleName()); // 从数据库或子类获取 result.setOperator(SYSTEM_AUDIT); result.setAuditTime(LocalDateTime.now()); try { boolean pass doAudit(studentId, ruleParams); result.setPassStatus(pass); result.setDetails(pass ? 审核通过 : generateFailDetails(studentId, ruleParams)); } catch (Exception e) { result.setPassStatus(false); result.setDetails(审核过程发生异常: e.getMessage()); } return result; } /** * 子类必须实现的真正审核逻辑 */ protected abstract boolean doAudit(String studentId, MapString, Object ruleParams); /** * 生成审核不通过的详细说明 */ protected abstract String generateFailDetails(String studentId, MapString, Object ruleParams); protected abstract String getRuleName(); }5.2 实现具体的规则处理器以“总学分审核规则”为例Service // 声明为Spring Bean public class TotalCreditRuleHandler extends AbstractRuleHandler { Override public String getRuleCode() { return CREDIT_TOTAL; } Override protected boolean doAudit(String studentId, MapString, Object ruleParams) { // 1. 从参数中获取毕业要求的总学分 Double requiredCredits (Double) ruleParams.get(requiredCredits); if (requiredCredits null) { throw new IllegalArgumentException(规则参数requiredCredits未配置); } // 2. 查询该学生所有有效课程的总学分这里假设有服务层方法 Double actualTotalCredits courseGradeService.calculateTotalCredits(studentId); // 3. 比较并返回结果 return actualTotalCredits ! null actualTotalCredits requiredCredits; } Override protected String generateFailDetails(String studentId, MapString, Object ruleParams) { Double requiredCredits (Double) ruleParams.get(requiredCredits); Double actualTotalCredits courseGradeService.calculateTotalCredits(studentId); return String.format(总学分不足。要求: %s 学分实际: %s 学分还差 %s 学分。, requiredCredits, actualTotalCredits, requiredCredits - actualTotalCredits); } Override protected String getRuleName() { return 总学分要求审核; } }同理我们可以实现GpaRuleHandler绩点审核、CourseTypeCreditRuleHandler分类学分审核、DissertationRuleHandler论文审核等。5.3 构建规则执行引擎我们需要一个“调度中心”能够根据数据库中的规则配置找到对应的处理器并执行。Service public class GraduationAuditEngine { // 通过Map自动注入所有实现了GraduationRuleHandler接口的Bean Autowired private MapString, GraduationRuleHandler ruleHandlerMap; Autowired private GraduationRuleRepository ruleRepository; // JPA Repository /** * 审核单个学生 */ public ListAuditResult auditStudent(String studentId) { ListAuditResult allResults new ArrayList(); // 1. 获取所有启用的审核规则 ListGraduationRule activeRules ruleRepository.findByIsActiveTrueOrderByPriority(); for (GraduationRule rule : activeRules) { GraduationRuleHandler handler ruleHandlerMap.get(rule.getRuleHandlerBean()); if (handler null) { // 记录日志规则未找到对应的处理器 continue; } // 2. 解析规则参数JSON格式 MapString, Object params parseRuleParams(rule.getParams()); // 3. 执行审核 AuditResult result handler.execute(studentId, params); result.setRuleName(rule.getRuleName()); // 设置规则名 allResults.add(result); } return allResults; } private MapString, Object parseRuleParams(String paramsJson) { if (StringUtils.isBlank(paramsJson)) { return new HashMap(); } // 使用Jackson或Gson解析JSON字符串为Map ObjectMapper mapper new ObjectMapper(); try { return mapper.readValue(paramsJson, new TypeReferenceMapString, Object() {}); } catch (Exception e) { throw new RuntimeException(解析规则参数失败: paramsJson, e); } } }这里的关键技巧Autowired private MapString, GraduationRuleHandler ruleHandlerMap;这行代码是Spring的“按类型自动注入Map”特性。Spring会将所有GraduationRuleHandler接口的实现类注入到这个Map中Key是Bean的名字默认是类名首字母小写如totalCreditRuleHandlerValue是Bean实例。这样我们就能通过规则配置中存储的Bean名称rule_handler_bean动态地获取到对应的处理器。5.4 审核流程的服务层封装最后在服务层AuditService中我们将引擎、任务记录、结果保存等流程串联起来。Service Transactional public class AuditService { Autowired private GraduationAuditEngine auditEngine; Autowired private AuditTaskRepository taskRepository; Autowired private AuditResultRepository resultRepository; public Long executeBatchAudit(ListString studentIds, String taskName, Long operatorId) { // 1. 创建审核任务记录 AuditTask task new AuditTask(); task.setTaskName(taskName); task.setAuditType(1); // 批量 task.setTargetStudentIds(JsonUtils.toJson(studentIds)); task.setInitiatorId(operatorId); task.setStatus(0); // 待执行 task taskRepository.save(task); final Long taskId task.getId(); // 2. 异步执行审核避免HTTP请求超时 CompletableFuture.runAsync(() - { task.setStatus(1); // 执行中 task.setStartTime(LocalDateTime.now()); taskRepository.save(task); try { for (String studentId : studentIds) { ListAuditResult results auditEngine.auditStudent(studentId); // 为每个结果关联任务ID results.forEach(r - r.setTaskId(taskId)); // 批量保存结果 resultRepository.saveAll(results); } task.setStatus(2); // 已完成 } catch (Exception e) { task.setStatus(3); // 失败 task.setDetails(批量审核失败: e.getMessage()); } finally { task.setEndTime(LocalDateTime.now()); taskRepository.save(task); } }); return taskId; // 立即返回任务ID前端可轮询状态 } }重要提示批量审核是一个耗时操作必须采用异步方式如Async注解或CompletableFuture来执行否则会阻塞HTTP线程导致前端请求超时。同时整个审核过程需要在一个数据库事务Transactional中确保任务状态和审核结果的原子性。这里为了简化将异步和事务放在了一起实际复杂场景下可能需要更精细的事务边界控制。6. RESTful API设计与前后端交互后端逻辑完成后我们需要通过API暴露给前端。设计清晰、规范的API是前后端高效协作的基础。6.1 审核相关API设计我们设计几个核心的控制器ControllerRestController RequestMapping(/api/audit) public class AuditController { Autowired private AuditService auditService; Autowired private AuditResultService resultService; /** * 发起批量毕业审核 * param request 包含学生ID列表和任务名称 * return 审核任务ID */ PostMapping(/batch) public ApiResponseLong launchBatchAudit(RequestBody Valid BatchAuditRequest request) { // 从Spring Security上下文获取当前登录用户ID Long currentUserId getCurrentUserId(); Long taskId auditService.executeBatchAudit(request.getStudentIds(), request.getTaskName(), currentUserId); return ApiResponse.success(taskId); } /** * 查询审核任务详情 */ GetMapping(/task/{taskId}) public ApiResponseAuditTaskVO getTaskDetail(PathVariable Long taskId) { AuditTask task auditService.getTaskById(taskId); // 将Entity转换为前端需要的VOView Object return ApiResponse.success(AuditTaskVO.from(task)); } /** * 分页查询某个任务的审核结果 */ GetMapping(/task/{taskId}/results) public ApiResponsePageResultAuditResultVO getTaskResults( PathVariable Long taskId, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 20) Integer size) { Pageable pageable PageRequest.of(page - 1, size, Sort.by(studentId)); PageAuditResult resultPage resultService.getResultsByTaskId(taskId, pageable); // 转换为VO并包装分页信息 return ApiResponse.success(PageResult.of(resultPage, AuditResultVO::from)); } /** * 查询单个学生的详细审核结果包含所有规则 */ GetMapping(/student/{studentId}/results) public ApiResponseListAuditResultVO getStudentResults(PathVariable String studentId) { ListAuditResult results resultService.getLatestResultsByStudent(studentId); return ApiResponse.success(results.stream().map(AuditResultVO::from).toList()); } }其中ApiResponse是一个统一的响应包装类包含code,message,data字段便于前端统一处理。BatchAuditRequest是请求参数的DTOData Transfer Object使用Valid注解配合JSR-303校验注解如NotEmpty进行参数校验。6.2 前端Vue3组件示例关键部分前端使用Vue 3 Element Plus。以“发起批量审核”页面为例template div el-card template #header span发起毕业审核/span /template el-form :modelform :rulesrules refformRef label-width100px el-form-item label任务名称 proptaskName el-input v-modelform.taskName placeholder例如计算机学院2025届毕业资格初审/el-input /el-form-item el-form-item label选择学生 propstudentIds !-- 这里可以使用选择器或者从Excel导入 -- el-select v-modelform.studentIds multiple filterable placeholder请选择或输入学号 stylewidth: 100%; el-option v-forstudent in studentList :keystudent.id :label${student.studentId} - ${student.name} :valuestudent.studentId/el-option /el-select div stylemargin-top: 10px; el-button clickhandleImportFromExcel从Excel导入学号列表/el-button /div /el-form-item el-form-item el-button typeprimary clicksubmitForm :loadingsubmitting发起审核/el-button /el-form-item /el-form /el-card !-- 任务进度展示 -- el-card v-ifcurrentTaskId stylemargin-top: 20px; template #header span审核任务进度/span /template div p任务ID: {{ currentTaskId }}/p p状态: {{ taskStatusMap[taskDetail.status] || 未知 }}/p el-progress :percentagecalculateProgress(taskDetail) v-iftaskDetail.status 1/el-progress el-button clickrefreshTaskDetail :loadingrefreshing刷新状态/el-button el-button typesuccess click$router.push(/audit/task/${currentTaskId}) v-iftaskDetail.status 2查看详细结果/el-button /div /el-card /div /template script setup import { ref, reactive } from vue import { ElMessage } from element-plus import { launchBatchAudit, getTaskDetail } from /api/audit const formRef ref() const form reactive({ taskName: , studentIds: [] }) const rules { taskName: [{ required: true, message: 请输入任务名称, trigger: blur }], studentIds: [{ type: array, required: true, message: 请至少选择一名学生, trigger: change }] } const submitting ref(false) const currentTaskId ref(null) const taskDetail ref({}) const refreshing ref(false) const submitForm async () { await formRef.value.validate() submitting.value true try { const { data: taskId } await launchBatchAudit(form) currentTaskId.value taskId ElMessage.success(审核任务已发起请等待完成) // 开始轮询任务状态 startPollingTaskStatus(taskId) } catch (error) { ElMessage.error(发起失败 error.message) } finally { submitting.value false } } const startPollingTaskStatus (taskId) { const intervalId setInterval(async () { const { data: detail } await getTaskDetail(taskId) taskDetail.value detail if (detail.status ! 0 detail.status ! 1) { // 非待执行和执行中状态 clearInterval(intervalId) ElMessage.info(任务${taskId}执行完毕) } }, 2000) // 每2秒轮询一次 } /script这个前端组件展示了完整的交互流程表单校验、调用API、异步处理、状态轮询。对于后端返回的任务ID前端通过定时器轮询任务状态直到审核完成然后引导用户查看结果。这种设计提供了良好的用户体验。7. 系统安全、部署与性能考量一个完整的系统除了核心功能还必须考虑安全、如何部署以及可能的性能瓶颈。7.1 使用Spring Security实现权限控制毕业审核系统的数据非常敏感必须做好权限隔离。Spring Security可以很好地实现基于角色的访问控制RBAC。Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() // 对于纯API项目可以考虑禁用CSRF或使用token方式 .authorizeHttpRequests(auth - auth .requestMatchers(/api/auth/**, /swagger-ui/**, /v3/api-docs/**).permitAll() // 放行登录、Swagger等 .requestMatchers(/api/audit/**).hasAnyRole(ADMIN, ACADEMIC_AFFAIRS) // 审核相关API需要教务角色 .requestMatchers(/api/student/self/**).hasRole(STUDENT) // 学生只能访问自己的信息 .anyRequest().authenticated() // 其他所有请求都需要认证 ) .sessionManagement(session - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 使用无状态JWT替代Session .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) // 添加JWT过滤器 .httpBasic(Customizer.withDefaults()); return http.build(); } // 配置密码加密器 Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }在实际项目中更推荐使用JWTJSON Web Token进行无状态认证。用户登录后后端颁发一个有时效的Token前端在后续请求的Header中携带Authorization: Bearer token。后端通过一个自定义的Filter来校验这个Token。7.2 项目打包与部署Spring Boot项目打包非常简单。在项目根目录下执行Maven命令mvn clean package -DskipTests命令执行后会在target目录下生成一个可执行的JAR文件如graduation-audit-system-0.0.1-SNAPSHOT.jar。这个JAR包内嵌了Tomcat服务器。部署到Linux服务器将JAR包和配置文件application-prod.yml上传到服务器。使用java -jar命令启动nohup java -Xms512m -Xmx1024m -jar graduation-audit-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 -Xms和-Xmx设置了JVM堆内存的初始大小和最大值。--spring.profiles.activeprod指定使用生产环境配置文件。nohup ... 让进程在后台运行并将日志输出到app.log。更生产化的方式是使用systemd来管理服务或者使用Docker容器化部署。Docker部署推荐 创建一个简单的DockerfileFROM openjdk:17-jdk-slim VOLUME /tmp COPY target/*.jar app.jar ENTRYPOINT [java,-jar,/app.jar]然后构建镜像并运行docker build -t graduation-audit . docker run -d -p 8080:8080 --name audit-app -v /path/to/config:/config graduation-audit --spring.config.location/config/application-prod.yml7.3 性能优化与缓存策略当学生数量庞大如数万人时批量审核可能成为性能瓶颈。以下是一些优化思路数据库查询优化审核中最耗时的往往是数据库查询。确保student_id,course_code等常用查询字段上有索引。对于复杂的学分、绩点统计可以考虑使用数据库的视图View或定期将聚合结果计算好存入缓存/汇总表。引入缓存使用Redis或Caffeine。学生基本信息缓存学生信息变动不频繁可以缓存起来。规则配置缓存审核规则一旦配置很少变动非常适合缓存。审核结果缓存对于已审核过的学生如果其成绩等数据未更新可以直接返回缓存结果避免重复计算。可以在AuditResult表中增加一个data_version字段如成绩表最后更新时间戳当版本未变时直接使用上次的审核结果。异步与批量处理正如我们在AuditService中做的将耗时的审核操作异步化。更进一步可以在审核引擎内部对数据库查询进行批量操作减少数据库连接次数。连接池配置在application.yml中合理配置数据库连接池如HikariCP的参数如最大连接数、最小空闲连接数等避免在高并发下出现连接等待。一个真实的踩坑案例在一次全年级的批量审核中系统运行缓慢最终超时。排查后发现在TotalCreditRuleHandler中courseGradeService.calculateTotalCredits(studentId)方法是为每个学生单独执行一条SELECT SUM(credit) FROM course_grade WHERE student_id ?。当有1万名学生时就是1万次数据库查询和1万次SUM计算。优化方案是改为在服务层一次性查询出所有学生的总学分SELECT student_id, SUM(credit) as total FROM course_grade GROUP BY student_id将结果放入一个MapString, Double然后在每个规则处理器中直接从这个Map里获取。这样只需要一次数据库查询性能提升数百倍。本文还有配套的精品资源点击获取