高校学生信息管理一直是教务工作的核心环节。记得我大三那年,系里还在用Excel表格管理几百名学生的选课信息,每到选课季办公室就排起长队,老师们不得不加班处理各种数据冲突。这种传统管理模式存在几个典型痛点:
这个基于SpringBoot的学生信息管理系统,正是为解决这些问题而设计。系统采用B/S架构,整合了学生从入学到毕业的全周期信息管理,包含三大核心模块:
选择SpringBoot+MySQL组合主要基于以下考量:
后端技术栈:
前端技术栈:
数据库选型:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20 # 根据JMeter测试,20连接可支持300并发
connection-timeout: 30000
idle-timeout: 600000
采用典型的分层架构,各层职责明确:
code复制┌───────────────────────────────────────┐
│ 表现层 │
│ (Thymeleaf+Controller层异常处理) │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 业务逻辑层 │
│ (Service层+自定义业务异常) │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 数据持久层 │
│ (MyBatis-Plus+动态SQL) │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 基础设施层 │
│ (MySQL+Redis缓存+文件存储) │
└───────────────────────────────────────┘
关键设计决策:
权限管理是系统的安全基石,我们实现了三级权限体系:
java复制// 典型权限注解使用示例
@RequiresRoles("admin")
public class AdminController {
@RequiresPermissions("student:delete")
public void deleteStudent(String id) {
// 管理员删除学生逻辑
}
}
权限树设计:
code复制系统权限
├─ 学生权限
│ ├─ 个人信息管理(R/W)
│ ├─ 选课申请(C)
│ └─ 成绩查询(R)
├─ 教师权限
│ ├─ 授课班级管理(R/W)
│ ├─ 成绩录入(W)
│ └─ 考勤记录(C/U)
└─ 管理员权限
├─ 用户管理(C/R/U/D)
├─ 系统配置(W)
└─ 数据导出(R)
避坑指南:
资源:操作格式,便于后期维护java复制public void clearPermissionCache(String username) {
AuthorizationInfo info = shiroRealm.doGetAuthorizationInfo(
new SimplePrincipalCollection(username, shiroRealm.getName()));
if(info != null) {
cacheManager.getCache("authorizationCache").remove(info);
}
}
选课模块是系统中最复杂的业务场景,核心难点在于解决并发冲突。我们采用数据库乐观锁方案:
sql复制CREATE TABLE course_selection (
id BIGINT PRIMARY KEY,
course_id BIGINT,
student_id BIGINT,
version INT DEFAULT 0, -- 乐观锁版本号
UNIQUE KEY (course_id, student_id)
);
选课关键代码:
java复制@Transactional
public boolean selectCourse(Long courseId, Long studentId) {
// 检查课程容量
Course course = courseMapper.selectById(courseId);
if(course.getSelected() >= course.getCapacity()) {
throw new BusinessException("该课程已满");
}
// 检查是否重复选课
if(selectionMapper.existsByCourseAndStudent(courseId, studentId)) {
throw new BusinessException("不能重复选择同一课程");
}
// 乐观锁更新
int updated = courseMapper.updateSelectedNumber(
courseId, course.getVersion(), course.getVersion()+1);
if(updated == 0) {
throw new ConcurrentModificationException("选课冲突,请重试");
}
// 记录选课关系
CourseSelection selection = new CourseSelection();
selection.setCourseId(courseId);
selection.setStudentId(studentId);
return selectionMapper.insert(selection) > 0;
}
性能优化点:
成绩处理涉及多种业务规则,我们采用策略模式实现:
java复制public interface GradeCalculationStrategy {
BigDecimal calculate(List<GradeItem> items);
}
@Component("weightedAverage")
public class WeightedAverageStrategy implements GradeCalculationStrategy {
@Override
public BigDecimal calculate(List<GradeItem> items) {
// 实现加权平均算法
}
}
@Service
public class GradeService {
@Autowired
private Map<String, GradeCalculationStrategy> strategies;
public BigDecimal calculateFinalGrade(String strategyName, List<GradeItem> items) {
return strategies.get(strategyName).calculate(items);
}
}
成绩录入流程:
重要提示:成绩数据必须保留修改日志,我们设计了专门的审计表记录每次修改的操作人和时间。
请假审批采用状态机模式实现:
java复制public enum LeaveStatus {
PENDING, // 待审批
APPROVED, // 已批准
REJECTED, // 已拒绝
CANCELLED // 已取消
}
public class LeaveApplication {
@Transient
private StateMachine<LeaveStatus, LeaveEvent> stateMachine;
public void submit() {
stateMachine.sendEvent(LeaveEvent.SUBMIT);
}
public void approve() {
stateMachine.sendEvent(LeaveEvent.APPROVE);
}
}
状态转换规则:
code复制 SUBMIT APPROVE REJECT
CREATED ──────────> PENDING ────────> APPROVED
│ │
└───────────────┘
CANCELLED
业务规则:
推荐服务器配置:
关键JVM参数:
bash复制-server -Xms4g -Xmx4g -XX:MaxMetaspaceSize=512m
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
通过JMeter测试发现的性能瓶颈及解决方案:
成绩查询接口响应慢(平均1200ms)
(student_id, semester)选课高峰期数据库CPU飙升
大批量导出时内存溢出
java复制public void exportStudents(OutputStream out) {
int page = 1;
while(true) {
Page<Student> students = studentMapper.selectPage(
new Page<>(page, 100), null);
if(students.getRecords().isEmpty()) break;
// 写入到输出流
page++;
}
}
问题1:MyBatis-Plus逻辑删除与唯一索引冲突
现象:已删除的学生学号无法重复使用
解决:修改唯一索引为联合索引:
sql复制ALTER TABLE student
ADD UNIQUE idx_username_deleted (student_id, is_deleted);
问题2:Thymeleaf缓存导致页面修改不生效
配置:
yaml复制spring:
thymeleaf:
cache: false # 开发环境关闭
prefix: classpath:/templates/
suffix: .html
问题3:日期类型JSON序列化问题
方案:统一配置Jackson:
java复制@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
builder.timeZone(TimeZone.getDefault());
};
}
单元测试覆盖率:核心业务类达到80%+
java复制@SpringBootTest
class CourseSelectionServiceTest {
@Autowired
private CourseSelectionService service;
@Test
void testSelectCourseConflict() {
assertThrows(ConcurrentModificationException.class,
() -> service.selectCourse(1L, 1001L));
}
}
API文档生成:使用Swagger UI
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sims.controller"))
.build();
}
}
代码规范检查:集成Checkstyle插件
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<configLocation>google_checks.xml</configLocation>
</configuration>
</plugin>
这个项目从技术选型到架构设计都充分考虑了高校实际需求,在多个学校的试点运行中表现稳定。特别是选课模块的并发处理方案,在春季选课高峰期成功支撑了单日超过2万次的选课操作。建议在实际部署时,根据学校规模适当调整数据库连接池和Redis缓存配置参数。