1. 项目概述
这个线上选课系统采用了前后端分离的架构设计,前端使用Django框架,后端采用Java技术栈的SSM框架(Spring+SpringMVC+MyBatis)。系统实现了课程管理、选课管理、学生管理等功能模块,是一个典型的教务管理系统解决方案。
在实际开发过程中,我发现这种混合技术栈的选择其实很有讲究。Django作为Python生态中最成熟的Web框架,其自带的Admin后台和ORM系统可以快速搭建管理界面;而Java的SSM框架则为企业级应用提供了稳定的后端支持。这种组合既保证了开发效率,又确保了系统性能。
2. 技术架构解析
2.1 前端技术选型
Django框架在这个项目中主要承担了以下职责:
- 模板渲染:使用Django Template Language(DTL)实现动态页面
- 路由管理:通过urls.py配置前端路由
- 静态资源管理:内置的staticfiles应用处理CSS/JS等资源
特别值得一提的是Django的模板继承特性。我们定义了一个base.html作为基础模板,其他页面通过{% extends %}指令继承它。这种方式大幅减少了重复代码,比如导航栏和页脚只需要在base.html中定义一次。
python复制# 示例:Django模板继承
# base.html
<html>
<head>
<title>{% block title %}默认标题{% endblock %}</title>
</head>
<body>
{% include "navbar.html" %}
{% block content %}{% endblock %}
{% include "footer.html" %}
</body>
</html>
# course_list.html
{% extends "base.html" %}
{% block title %}课程列表{% endblock %}
{% block content %}
<h1>所有课程</h1>
<!-- 课程列表内容 -->
{% endblock %}
2.2 后端技术选型
SSM框架组合各司其职:
- Spring:负责依赖注入和事务管理
- SpringMVC:处理HTTP请求和响应
- MyBatis:数据库访问层
在实际开发中,我们采用了分层架构:
- Controller层:接收前端请求,参数校验
- Service层:业务逻辑处理
- Dao层:数据库操作
- Model层:数据实体
这种分层设计使得代码职责清晰,便于维护。例如课程查询功能的实现:
java复制// Controller
@RestController
@RequestMapping("/course")
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("/list")
public Result listCourses(@RequestParam Map<String, Object> params) {
PageUtils page = courseService.queryPage(params);
return Result.ok().put("data", page);
}
}
// Service
@Service
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseDao courseDao;
@Override
public PageUtils queryPage(Map<String, Object> params) {
Query query = new Query(params);
List<Course> list = courseDao.queryList(query);
int total = courseDao.queryTotal(query);
return new PageUtils(list, total, query.getLimit(), query.getPage());
}
}
3. 核心功能实现
3.1 课程管理模块
课程管理是整个系统的核心,主要包括:
- 课程CRUD操作
- 课程分类管理
- 课程搜索和筛选
数据库设计方面,课程表(course)主要字段包括:
- id:主键
- name:课程名称
- teacher_id:教师ID
- credit:学分
- max_student:最大选课人数
- current_student:当前选课人数
- status:状态(开放/关闭)
注意:在设计选课人数相关字段时,一定要考虑并发问题。我们使用了乐观锁机制来避免超选:
sql复制UPDATE course
SET current_student = current_student + 1
WHERE id = ? AND current_student < max_student
3.2 选课业务流程
选课流程的实现要点:
- 学生登录系统
- 查询可选课程列表
- 选择课程并提交
- 系统检查课程余量
- 创建选课记录
关键代码实现:
java复制@Transactional
public Result selectCourse(Integer studentId, Integer courseId) {
// 检查课程是否存在且开放
Course course = courseDao.selectById(courseId);
if (course == null || !course.getStatus().equals("OPEN")) {
return Result.error("课程不可选");
}
// 检查是否已选过该课程
if (selectionDao.exists(studentId, courseId)) {
return Result.error("已选过该课程");
}
// 乐观锁更新选课人数
int rows = courseDao.increaseCurrentStudent(courseId, course.getCurrentStudent());
if (rows == 0) {
return Result.error("选课人数已满");
}
// 创建选课记录
CourseSelection selection = new CourseSelection();
selection.setStudentId(studentId);
selection.setCourseId(courseId);
selection.setSelectTime(new Date());
selectionDao.insert(selection);
return Result.ok("选课成功");
}
4. 系统优化实践
4.1 性能优化措施
- 缓存策略:
- 使用Redis缓存热门课程信息
- 课程列表实现分页缓存
- 使用Spring Cache抽象简化缓存代码
java复制@Cacheable(value = "courses", key = "#params.toString()")
public PageUtils queryPage(Map<String, Object> params) {
// 查询逻辑
}
-
数据库优化:
- 为常用查询字段添加索引
- 使用MyBatis二级缓存
- 优化SQL语句,避免N+1查询问题
-
前端优化:
- 使用Django的缓存框架缓存静态页面
- 实现懒加载和无限滚动
- 压缩静态资源
4.2 安全防护措施
-
认证授权:
- 使用Spring Security实现RBAC
- 密码加密存储(BCrypt)
- CSRF防护
-
输入验证:
- 前端使用Django表单验证
- 后端使用JSR-303校验
- SQL注入防护(MyBatis参数化查询)
-
日志审计:
- 记录关键操作日志
- 使用AOP实现统一日志处理
- 异常监控告警
5. 常见问题与解决方案
5.1 选课并发问题
在高并发选课场景下,容易出现超选问题。我们最终采用了三种方案组合解决:
- 数据库乐观锁(如前所示)
- Redis分布式锁
- 消息队列削峰
Redis分布式锁实现示例:
java复制public boolean selectCourseWithLock(Integer studentId, Integer courseId) {
String lockKey = "course:lock:" + courseId;
String requestId = UUID.randomUUID().toString();
try {
// 获取锁
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
if (!locked) {
return false;
}
// 执行业务逻辑
return selectCourse(studentId, courseId).isSuccess();
} finally {
// 释放锁
if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
5.2 跨域问题处理
由于前端Django和后端Java服务分开部署,需要处理跨域问题。我们在Spring后端配置了CORS:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://django-frontend.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
5.3 事务管理问题
在复杂的选课业务中,需要确保多个数据库操作要么全部成功,要么全部失败。我们使用Spring的声明式事务管理:
java复制@Service
public class CourseSelectionServiceImpl implements CourseSelectionService {
@Transactional(rollbackFor = Exception.class)
public Result selectCourse(Integer studentId, Integer courseId) {
// 多个数据库操作
}
}
6. 部署与监控
6.1 系统部署方案
我们采用了Docker容器化部署:
- 前端Django应用一个容器
- 后端Java应用一个容器
- MySQL和Redis各一个容器
- 使用Docker Compose编排
yaml复制version: '3'
services:
frontend:
image: django-frontend
ports:
- "8000:8000"
backend:
image: java-backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
mysql_data:
6.2 系统监控
-
应用监控:
- Spring Boot Actuator提供健康检查
- Prometheus + Grafana监控JVM指标
- ELK收集分析日志
-
业务监控:
- 关键业务指标监控(如选课成功率)
- 异常交易告警
- 定时任务监控
-
性能监控:
- 接口响应时间监控
- 数据库慢查询监控
- 系统资源使用率监控
7. 项目总结与心得
在实际开发这个选课系统的过程中,有几个关键点值得特别注意:
-
技术选型要权衡:Django+SSM的组合虽然不常见,但对于需要快速开发前端管理界面又要求后端稳定性的场景很合适。Django Admin可以节省大量CRUD界面的开发时间。
-
并发控制要全面:选课这类场景的并发问题不能单靠数据库解决,需要结合缓存、队列等多种技术。我们在压力测试中发现,单纯依赖数据库乐观锁在极高并发下仍会出现问题,最终引入Redis分布式锁才彻底解决。
-
事务边界要明确:在涉及多个数据修改的操作中,一定要仔细设计事务范围。过大的事务会影响性能,过小的事务可能导致数据不一致。我们最终确定了以单个选课操作为一个事务单位的策略。
-
缓存策略要合理:课程信息的缓存需要特别考虑时效性。我们采用了课程变更时主动失效缓存的策略,并设置了适当的过期时间作为兜底。
这个项目让我深刻体会到,一个看似简单的选课系统,背后需要考虑的技术细节其实非常多。特别是在高并发场景下,很多在开发环境不会出现的问题,一到生产环境就会暴露出来。因此充分的压力测试和异常情况模拟非常重要。