1. 项目概述
这个选题管理系统是我去年为某高校计算机系开发的一个实战项目,主要解决课程设计选题环节的混乱问题。传统的手工登记方式效率低下,经常出现选题冲突、信息不同步等情况。系统采用前后端分离架构,后端基于SpringBoot+MyBatis+MySQL技术栈,前端使用Vue.js框架,实现了从选题发布、学生选择到教师审核的全流程管理。
提示:系统源码已通过Gitee开源,文末会提供获取方式。这个项目特别适合Java全栈开发学习者练手,涵盖了企业级应用开发的典型技术组合。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型解析
2.1 后端技术栈
选择SpringBoot作为后端框架主要考虑三点:
- 自动配置特性大幅减少XML配置
- 内嵌Tomcat简化部署流程
- 丰富的Starter依赖能快速集成MyBatis等组件
数据库选用MySQL 8.0版本,主要优势在于:
- 高校场景下数据规模可控(通常<10万条记录)
- 完善的事务支持和ACID特性
- 与Java生态的天然兼容性
持久层采用MyBatis而非Hibernate,因为:
- 需要编写复杂SQL处理多表关联查询
- 对SQL优化有较高要求
- 项目组成员更熟悉MyBatis的使用
2.2 前端技术栈
Vue.js 2.x版本的选择基于以下考量:
- 组件化开发适合管理系统类项目
- 相比React更轻量且学习曲线平缓
- 丰富的UI库(使用Element-UI)
- 双向数据绑定简化表单处理
3. 核心功能实现
3.1 数据库设计
系统包含6个核心表:
- 用户表(sys_user)
- 角色表(sys_role)
- 选题表(project)
- 学生选题表(student_project)
- 专业表(major)
- 公告表(notice)
关键关系说明:
- 用户-角色:多对多(通过中间表关联)
- 学生-选题:一对多(一个学生可选多个备选选题)
- 教师-选题:一对多(一个教师可发布多个选题)
sql复制CREATE TABLE `project` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`description` text,
`teacher_id` int NOT NULL,
`max_students` int DEFAULT '1',
`selected_count` int DEFAULT '0',
`status` tinyint DEFAULT '0' COMMENT '0-待审核 1-已发布 2-已截止',
PRIMARY KEY (`id`),
KEY `idx_teacher` (`teacher_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 后端关键实现
3.2.1 权限控制
采用RBAC模型,通过Spring Security实现:
- 定义4种角色:超级管理员、系主任、教师、学生
- 权限注解控制接口访问:
java复制@PreAuthorize("hasRole('TEACHER')")
@PostMapping("/projects")
public Result addProject(@RequestBody Project project) {
// 教师添加选题逻辑
}
3.2.2 选题业务流
核心状态机设计:
java复制public enum ProjectStatus {
PENDING(0, "待审核"),
PUBLISHED(1, "已发布"),
CLOSED(2, "已截止");
// 省略getter/setter
}
状态转换规则:
- 教师提交 → 待审核
- 系主任审核通过 → 已发布
- 截止日期到/名额满 → 已截止
3.3 前端关键实现
3.3.1 动态路由
根据用户角色加载不同菜单:
javascript复制// 路由配置示例
{
path: '/project',
component: Layout,
meta: { roles: ['teacher', 'dean'] },
children: [
{
path: 'list',
component: () => import('@/views/project/list'),
meta: { title: '选题管理', icon: 'list' }
}
]
}
3.3.2 选题列表页
关键技术点:
- 使用Element-UI的Table组件
- 自定义筛选条件(按专业/状态)
- 分页查询优化(后端分页)
vue复制<template>
<el-table :data="projectList" @row-click="handleRowClick">
<el-table-column prop="title" label="选题名称" />
<el-table-column prop="teacherName" label="指导教师" />
<el-table-column prop="status" label="状态">
<template #default="{row}">
<el-tag :type="statusTagType(row.status)">
{{ statusText(row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</template>
4. 开发中的典型问题
4.1 并发选课问题
当热门选题名额将满时,可能出现超选情况。解决方案:
- 数据库乐观锁:
java复制@Update("UPDATE project SET selected_count = selected_count + 1
WHERE id = #{id} AND selected_count < max_students")
int incrementSelectedCount(@Param("id") int id);
- 分布式锁(Redis实现):
java复制public boolean selectProject(int studentId, int projectId) {
String lockKey = "project:select:" + projectId;
try {
// 获取锁(设置3秒超时)
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS);
if (locked != null && locked) {
// 执行选课逻辑
}
} finally {
redisTemplate.delete(lockKey);
}
}
4.2 文件导出性能
当导出全年级选题结果时(约500条记录),发现内存溢出。优化方案:
- 使用POI的SXSSFWorkbook进行流式导出
- 分批查询数据(每次500条)
- 增加服务器内存配置
5. 部署实践
5.1 后端部署
采用Docker容器化部署:
dockerfile复制FROM openjdk:8-jdk
COPY target/selection-system.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
启动命令:
bash复制docker build -t selection-system .
docker run -d -p 8080:8080 --name selection selection-system
5.2 前端部署
Nginx配置示例:
nginx复制server {
listen 80;
server_name selection.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}
6. 项目优化方向
- 性能监控:集成Spring Boot Actuator + Prometheus
- 日志分析:接入ELK日志系统
- 消息通知:增加WebSocket实时通知
- 移动端适配:开发微信小程序版本
避坑指南:MyBatis的N+1查询问题在开发初期曾导致接口响应缓慢,建议:
- 使用
标签处理一对多关系 - 开启MyBatis二级缓存
- 复杂查询考虑使用@SelectProvider动态SQL
源码获取方式:Gitee搜索"course-selection-system",项目包含完整文档和SQL脚本。我在开发过程中积累的SpringBoot+Vue实战笔记也已同步更新到仓库Wiki中。
