1. 项目概述与技术栈选型
这个在线课程管理系统采用了当前企业级开发中最主流的全栈技术组合:SpringBoot+Vue3+MyBatis+MySQL。这种架构设计完美体现了前后端分离的开发模式,前端使用Vue3构建用户界面,后端采用SpringBoot提供RESTful API,MyBatis作为ORM框架操作MySQL数据库。
为什么选择这个技术栈?从实际开发经验来看:
- SpringBoot的自动配置特性让后端服务搭建变得极其高效,内置Tomcat容器和starter依赖可以快速构建生产级应用
- Vue3的Composition API相比Options API更适合大型项目管理状态,配合TypeScript使用能显著提升前端代码质量
- MyBatis在复杂SQL场景下比JPA更灵活,特别是需要编写复杂查询或调用存储过程时
- MySQL作为最流行的开源关系型数据库,在事务处理和数据结构化存储方面表现优异
提示:在实际项目开发中,建议使用SpringBoot 2.7.x + Vue3.2.x + MyBatis 3.5.x + MySQL 8.0的组合版本,这是目前最稳定的版本搭配。
2. 系统架构设计与模块划分
2.1 前后端分离架构实现
系统采用经典的前后端分离架构,通过清晰的职责划分实现高效协作:
code复制前端层(Vue3)
├── 用户界面
├── 状态管理(Pinia)
└── API调用(axios)
后端层(SpringBoot)
├── 控制层(Controller)
├── 服务层(Service)
└── 持久层(MyBatis)
数据层(MySQL)
├── 课程数据表
├── 用户数据表
└── 关联关系表
前后端通过RESTful API进行通信,使用JWT进行身份认证。这种架构的优势在于:
- 前端可以独立部署,不依赖后端环境
- 后端API可以被多种客户端复用(Web/App/小程序)
- 开发团队可以并行工作,提高交付效率
2.2 核心功能模块设计
系统主要包含以下功能模块:
-
用户管理模块
- 角色权限控制(RBAC模型)
- JWT认证与鉴权
- 用户信息CRUD
-
课程管理模块
- 课程发布与下架
- 课程分类管理
- 课程搜索与筛选
-
学习管理模块
- 学习进度跟踪
- 课程评价系统
- 学习数据统计
-
系统管理模块
- 数据备份恢复
- 系统日志监控
- 参数配置管理
3. 数据库设计与优化
3.1 主要数据表结构
sql复制-- 用户表
CREATE TABLE `sys_user` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '登录账号',
`password` varchar(100) NOT NULL COMMENT '密码',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint DEFAULT '1' COMMENT '状态 0:禁用 1:正常',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户';
-- 课程表
CREATE TABLE `edu_course` (
`course_id` bigint NOT NULL AUTO_INCREMENT,
`teacher_id` bigint NOT NULL COMMENT '讲师ID',
`subject_id` bigint DEFAULT NULL COMMENT '课程分类ID',
`title` varchar(100) NOT NULL COMMENT '课程标题',
`cover` varchar(255) DEFAULT NULL COMMENT '课程封面',
`description` text COMMENT '课程描述',
`price` decimal(10,2) DEFAULT '0.00' COMMENT '课程价格',
`status` tinyint DEFAULT '0' COMMENT '课程状态 0:未发布 1:已发布',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`course_id`),
KEY `idx_teacher_id` (`teacher_id`),
KEY `idx_subject_id` (`subject_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程信息';
3.2 数据库性能优化实践
-
索引优化策略
- 为所有外键字段添加普通索引
- 高频查询条件建立组合索引
- 使用EXPLAIN分析SQL执行计划
-
事务隔离级别选择
- 默认使用REPEATABLE_READ
- 对账务相关操作使用SERIALIZABLE
- 读多写少场景考虑READ_COMMITTED
-
分库分表设计
- 用户数据按ID范围分表
- 课程数据按分类垂直拆分
- 学习记录按时间水平分表
注意:MySQL 8.0推荐使用utf8mb4字符集以支持完整的Unicode字符,包括emoji表情符号的存储。
4. 后端核心实现细节
4.1 SpringBoot应用配置
java复制@SpringBootApplication
@MapperScan("com.education.mapper")
@EnableTransactionManagement
@EnableCaching
public class EducationApplication {
public static void main(String[] args) {
SpringApplication.run(EducationApplication.class, args);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
关键配置说明:
@MapperScan指定MyBatis mapper接口路径@EnableTransactionManagement启用声明式事务@EnableCaching开启Spring Cache抽象- 使用BCryptPasswordEncoder进行密码加密
4.2 RESTful API设计规范
系统API遵循以下设计原则:
-
资源命名
- 使用名词复数形式:/api/courses
- 层级关系:/api/courses/{id}/lessons
-
HTTP方法
- GET:获取资源
- POST:创建资源
- PUT:全量更新
- PATCH:部分更新
- DELETE:删除资源
-
状态码
- 200 OK - 成功响应
- 201 Created - 资源创建成功
- 400 Bad Request - 客户端错误
- 401 Unauthorized - 未认证
- 403 Forbidden - 无权限
- 404 Not Found - 资源不存在
- 500 Internal Server Error - 服务端错误
4.3 MyBatis高级应用技巧
- 动态SQL编写
xml复制<select id="selectCourses" resultType="Course">
SELECT * FROM edu_course
<where>
<if test="title != null and title != ''">
AND title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY create_time DESC
</select>
- 批量操作优化
java复制@Insert("<script>" +
"INSERT INTO edu_course_lesson (course_id, title, duration) VALUES " +
"<foreach collection='lessons' item='lesson' separator=','>" +
"(#{lesson.courseId}, #{lesson.title}, #{lesson.duration})" +
"</foreach>" +
"</script>")
int batchInsertLessons(@Param("lessons") List<Lesson> lessons);
- 二级缓存配置
xml复制<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
5. 前端Vue3实现要点
5.1 项目初始化与配置
使用Vite创建Vue3项目:
bash复制npm create vite@latest education-frontend --template vue-ts
关键依赖:
pinia:状态管理axios:HTTP客户端vue-router:路由管理element-plus:UI组件库sass:CSS预处理器
5.2 组件化开发实践
课程卡片组件示例:
vue复制<script setup lang="ts">
defineProps<{
id: number
title: string
cover: string
price: number
studentCount: number
}>()
</script>
<template>
<div class="course-card">
<img :src="cover" :alt="title" class="cover">
<h3>{{ title }}</h3>
<div class="meta">
<span class="price">¥{{ price.toFixed(2) }}</span>
<span class="students">{{ studentCount }}人学习</span>
</div>
</div>
</template>
<style scoped lang="scss">
.course-card {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
.cover {
width: 100%;
height: 160px;
object-fit: cover;
}
.meta {
display: flex;
justify-content: space-between;
padding: 12px;
.price {
color: #f56c6c;
font-weight: bold;
}
}
}
</style>
5.3 状态管理与API调用
使用Pinia管理用户状态:
ts复制// stores/user.ts
import { defineStore } from 'pinia'
interface UserState {
token: string | null
userInfo: UserInfo | null
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
token: localStorage.getItem('token'),
userInfo: null
}),
actions: {
async login(username: string, password: string) {
const { data } = await api.post('/auth/login', { username, password })
this.token = data.token
this.userInfo = data.user
localStorage.setItem('token', data.token)
},
logout() {
this.token = null
this.userInfo = null
localStorage.removeItem('token')
}
}
})
API请求封装:
ts复制// utils/api.ts
import axios from 'axios'
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
})
// 请求拦截器
api.interceptors.request.use(config => {
const userStore = useUserStore()
if (userStore.token) {
config.headers.Authorization = `Bearer ${userStore.token}`
}
return config
})
// 响应拦截器
api.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
useUserStore().logout()
router.push('/login')
}
return Promise.reject(error)
}
)
export default api
6. 系统安全与性能优化
6.1 安全防护措施
-
认证与授权
- JWT签名使用HS256算法
- Token设置合理过期时间(如2小时)
- 敏感操作需要二次验证
-
输入验证
- 前端使用Vuelidate进行表单验证
- 后端使用Spring Validation注解
java复制@PostMapping("/courses") public Result createCourse(@Valid @RequestBody CourseDTO dto) { // ... } -
SQL注入防护
- 使用MyBatis参数绑定
- 禁止拼接SQL语句
- 定期进行安全扫描
6.2 性能优化方案
-
缓存策略
- Redis缓存热点数据
- Spring Cache抽象统一缓存接口
java复制@Cacheable(value = "courses", key = "#id") public Course getById(Long id) { return courseMapper.selectById(id); } -
异步处理
- 耗时操作使用@Async
- 邮件发送走消息队列
java复制@Async public void sendWelcomeEmail(User user) { // 发送邮件逻辑 } -
前端性能优化
- 路由懒加载
js复制const routes = [ { path: '/courses', component: () => import('./views/CourseList.vue') } ]- 图片懒加载
vue复制<img v-lazy="course.cover" alt="course cover">
7. 项目部署与运维
7.1 生产环境部署
后端部署方案:
- 使用Docker容器化部署
dockerfile复制FROM openjdk:17-jdk-slim
COPY target/education-backend.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
- Nginx配置示例:
nginx复制server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
前端部署方案:
- 静态资源构建
bash复制npm run build
- Nginx配置:
nginx复制server {
listen 80;
server_name yourdomain.com;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
7.2 监控与日志
- SpringBoot Actuator
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
- ELK日志收集
- Filebeat收集日志
- Logstash处理日志
- Elasticsearch存储日志
- Kibana可视化展示
- Prometheus监控
yaml复制spring:
application:
name: education-service
management:
metrics:
export:
prometheus:
enabled: true
8. 开发中的常见问题与解决方案
8.1 跨域问题处理
SpringBoot配置CORS:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
Vue开发环境代理配置:
js复制// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
8.2 数据一致性保障
- 分布式事务
- 使用Seata框架
- 最终一致性方案
- 补偿事务机制
- 乐观锁实现
java复制@Update("UPDATE edu_course SET stock = stock - 1, version = version + 1 " +
"WHERE course_id = #{courseId} AND version = #{version}")
int reduceStockWithVersion(@Param("courseId") Long courseId,
@Param("version") Integer version);
8.3 第三方服务集成
- 支付接口集成
java复制public class PaymentService {
@Async
public CompletableFuture<PaymentResult> createPayment(Order order) {
// 调用支付宝/微信支付接口
}
}
- 短信服务集成
java复制@RestController
@RequestMapping("/api/sms")
public class SmsController {
@PostMapping("/send")
public Result sendVerifyCode(@RequestParam String mobile) {
String code = generateCode();
smsClient.send(mobile, "您的验证码是:" + code);
redisTemplate.opsForValue().set(
"sms:" + mobile, code, 5, TimeUnit.MINUTES);
return Result.success();
}
}
在实际开发这套系统时,有几个关键点需要特别注意:首先是在设计数据库时要充分考虑课程与用户之间的多对多关系,建议使用中间表处理选课记录;其次是文件上传功能要做好安全过滤,防止上传恶意文件;最后是分页查询一定要做好性能优化,特别是当数据量变大时。我在实际项目中遇到过当课程数量超过10万条时,简单的LIMIT分页会导致性能急剧下降,后来改用游标分页方案解决了这个问题。
