<think>我们正在构建一个基于SpringBoot+Vue3+Mybatis的在线考试平台。根据之前的对话,用户已经了解了如何上传题目,现在需要了解整个平台的搭建思路。
我们将从技术栈选择、项目结构、核心模块设计和前后端交互等方面给出思路。
1. 技术栈
- 后端: SpringBoot 2.x + Mybatis Plus (简化CRUD) + MySQL
- 前端: Vue3 + Vue Router + Pinia (状态管理) + Element Plus (UI框架) + Axios
- 其他: Maven, Node.js
2. 项目结构(分层架构)
- 后端(SpringBoot):
- com.example.exam
- controller: 处理HTTP请求,调用服务层
- service: 业务逻辑层,包含接口和实现
- mapper: Mybatis的Mapper接口,与数据库交互
- entity: 数据库表对应的实体类
- dto: 数据传输对象,用于前后端数据交互
- config: 配置类(如跨域配置)
- utils: 工具类
- 前端(Vue3):
- src
- api: 封装后端接口请求
- assets: 静态资源
- components: 可复用组件
- router: 路由配置
- store: Pinia状态管理
- views: 页面视图(如登录页、考试列表页、考试页面、管理员后台等)
3. 核心模块设计
在线考试平台一般包含以下模块:
- 用户管理:学生、教师、管理员
- 权限管理:基于角色的访问控制(RBAC)
- 题库管理:题目的增删改查(支持多种题型:单选、多选、填空、简答等)
- 试卷管理:组卷(手动组卷、自动组卷)、试卷发布
- 考试管理:考试安排、在线考试(计时、防作弊)、交卷
- 成绩管理:自动批改(客观题)、教师批改(主观题)、成绩统计与导出
4. 数据库设计(关键表)
- 用户表(user): id, username, password, role(角色)
- 角色表(role): id, name(如admin, teacher, student)
- 权限表(permission): id, name, url
- 题目表(question): id, type(题型), content, options(选择题的选项,JSON格式), answer, analysis(解析), difficulty, create_time, creator_id
- 试卷表(exam): id, title, total_score, start_time, end_time, duration(分钟), creator_id
- 试卷题目关联表(exam_question): id, exam_id, question_id, score(该题在试卷中的分值)
- 考试记录表(exam_record): id, user_id, exam_id, start_time, submit_time, score
- 答题详情表(record_detail): id, record_id, question_id, answer, is_correct(客观题由系统判定,主观题为null), score(该题得分)
5. 前后端交互
- 使用RESTful API风格
- 前后端分离,通过Axios进行异步请求
- 使用JWT进行身份认证和权限控制
6. 关键功能实现思路
(1) 题库管理
- 题目上传:支持手动添加和批量导入(使用Excel模板)。
- 题目存储:选择题的选项可以存储为JSON字符串,例如:`{"A":"选项A的内容", "B":"选项B的内容"}`。
(2) 组卷
- 手动组卷:从题库中选择题目添加到试卷,并设置每道题的分值。
- 自动组卷:设置题型、难度、数量等规则,系统随机抽题。
(3) 在线考试
- 进入考试:学生选择考试,进入考试页面,开始计时(前端计时,但也要防止篡改,后端需校验考试时间)。
- 防作弊:全屏检测、切屏检测、题目乱序等(需要前端配合)。
- 自动保存:定时保存答案(如每30秒或切换题目时)。
- 交卷:提交答案后,客观题立即批改,主观题等待教师批改。
(4) 成绩管理
- 客观题:提交后系统根据正确答案自动批改。
- 主观题:教师登录后台,对每道题进行评分。
7. 注意事项
- 安全性:防止SQL注入、XSS攻击;密码加密存储;接口权限控制。
- 性能:考试页面可能同时有大量并发,考虑使用Redis缓存题目信息,数据库读写分离等。
- 事务管理:例如交卷操作,需要同时更新考试记录和答题详情,保证事务一致性。
8. 示例代码片段
后端:SpringBoot Controller示例(题目添加接口)
```java
@RestController
@RequestMapping("/api/question")
public class QuestionController {
@Autowired
private QuestionService questionService;
@PostMapping("/add")
public Result addQuestion(@RequestBody QuestionDTO questionDTO) {
// 将DTO转换为Entity,调用service保存
Question question = new Question();
BeanUtils.copyProperties(questionDTO, question);
// 设置创建人(从当前登录用户获取)
question.setCreatorId(SecurityUtils.getCurrentUserId());
boolean success = questionService.save(question);
return success ? Result.ok("添加成功") : Result.error("添加失败");
}
}
```
前端:Vue3 + Element Plus 添加题目表单
```vue
<template>
<el-form :model="form" label-width="120px">
<el-form-item label="题目类型">
<el-select v-model="form.type">
<el-option label="单选题" value="1"></el-option>
<el-option label="多选题" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="题目内容">
<el-input v-model="form.content" type="textarea"></el-input>
</el-form-item>
<!-- 动态添加选项(如果是选择题) -->
<div v-if="form.type==='1' || form.type==='2'">
<el-form-item v-for="(option, index) in form.options" :key="index" :label="'选项'+index">
<el-input v-model="option.value"></el-input>
</el-form-item>
<el-button @click="addOption">添加选项</el-button>
</div>
<el-form-item>
<el-button type="primary" @click="submit">提交</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive } from 'vue'
import axios from 'axios'
const form = reactive({
type: '1', // 默认单选题
content: '',
options: [{key:'A', value:''}], // 初始一个选项
})
const addOption = () => {
const keys = ['A','B','C','D','E','F']
if(form.options.length < keys.length) {
form.options.push({key: keys[form.options.length], value:''})
} else {
alert('最多添加6个选项')
}
}
const submit = async () => {
// 将form数据整理成后端需要的DTO格式
const data = {
type: form.type,
content: form.content,
// 将选项数组转换为对象(如 {A:'选项1', B:'选项2'})
options: form.options.reduce((obj, item) => {
obj[item.key] = item.value
return obj
}, {})
}
try {
await axios.post('/api/question/add', data)
// 处理成功
} catch (error) {
// 处理错误
}
}
</script>
```
9. 学习资源
- 参考引用[1]中的博客和视频,了解前后端分离项目的搭建流程[^1]。
- 引用[2]中给出了Mybatis的Mapper配置示例[^2],可参考其进行数据库操作。
以上即为使用SpringBoot+Vue3+Mybatis搭建在线考试平台的基本思路。接下来根据需求逐步实现各个模块即可。
</think>以下是使用 SpringBoot + Vue3 + Mybatis 搭建在线考试平台的系统架构思路,结合前后端分离的设计理念:
### 一、系统分层架构
```mermaid
graph TD
A[前端 Vue3] -->|Axios请求| B[后端 SpringBoot]
B --> C[业务逻辑层]
C --> D[Mybatis持久层]
D --> E[MySQL数据库]
```
### 二、后端设计(SpringBoot + Mybatis)
#### 1. 核心模块划分
| 模块 | 功能描述 | 核心接口示例 |
|-------------|----------------------------|---------------------------|
| 用户管理 | 角色权限控制(学生/教师/管理员) | `/api/user/login` |
| 题库管理 | 题目CRUD操作 | `/api/question/upload` |
| 试卷管理 | 组卷/发布/批改 | `/api/exam/create` |
| 考试管理 | 考试过程监控 | `/api/exam/start/{id}` |
| 成绩管理 | 成绩统计与分析 | `/api/score/analysis` |
#### 2. Mybatis 数据表设计
```sql
-- 题目表
CREATE TABLE question (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
type TINYINT COMMENT '题型:1单选/2多选/3填空',
content TEXT NOT NULL,
options JSON COMMENT '选择题选项(JSON数组)',
answer VARCHAR(500) NOT NULL,
difficulty TINYINT DEFAULT 1 COMMENT '难度系数1-5'
);
-- 试卷表
CREATE TABLE exam (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
start_time DATETIME,
duration INT COMMENT '考试时长(分钟)'
);
-- 试卷-题目关联表
CREATE TABLE exam_question (
exam_id BIGINT,
question_id BIGINT,
score DECIMAL(5,2) COMMENT '本题分值'
);
```
#### 3. SpringBoot 关键配置
```java
// Mybatis配置类
@MapperScan("com.exam.mapper")
public class MybatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
}
// 跨域配置
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // Vue3前端地址
.allowCredentials(true)
.allowedMethods("*");
}
}
```
### 三、前端设计(Vue3 + Element Plus)
#### 1. 前端模块结构
```
src/
├── api/ // 接口封装
├── router/ // 路由配置
├── store/ // Pinia状态管理
├── views/
│ ├── Teacher/ // 教师端
│ │ ├── QuestionUpload.vue // 题目上传
│ │ └── ExamManage.vue // 考试管理
│ └── Student/ // 学生端
│ ├── ExamList.vue // 考试列表
│ └── ExamPaper.vue // 考试界面
└── components/ // 公用组件
```
#### 2. 题目上传组件示例
```vue
<template>
<el-upload
action="/api/question/upload"
:before-upload="beforeUpload"
accept=".xlsx,.csv"
>
<el-button type="primary">批量上传题目</el-button>
<template #tip>
<div class="el-upload__tip">支持Excel/CSV格式模板</div>
</template>
</el-upload>
</template>
<script setup>
import { ElMessage } from 'element-plus'
const beforeUpload = (file) => {
const isLt10M = file.size / 1024 / 1024 < 10
if (!isLt10M) {
ElMessage.error('文件大小不能超过10MB')
return false
}
return true
}
</script>
```
### 四、核心功能实现
#### 1. 题目上传流程
```mermaid
sequenceDiagram
Vue前端->>SpringBoot后端: 上传Excel文件
SpringBoot后端->>POI解析器: 解析文件内容
POI解析器->>Mybatis: 生成批量插入SQL
Mybatis->>MySQL: 执行INSERT操作
MySQL-->>Vue前端: 返回上传结果
```
#### 2. 后端批量上传接口
```java
@PostMapping("/upload")
public ResponseEntity<?> uploadQuestions(@RequestParam("file") MultipartFile file) {
try {
InputStream inputStream = file.getInputStream();
List<Question> questions = ExcelParser.parse(inputStream); // 自定义Excel解析
questionMapper.batchInsert(questions); // Mybatis批量插入
return ResponseEntity.ok("成功导入"+questions.size()+"道题目");
} catch (IOException e) {
return ResponseEntity.status(500).body("文件处理失败");
}
}
```
### 五、关键技术点
1. **文件解析**:
- 使用 Apache POI 解析 Excel
- 使用 OpenCSV 解析 CSV 文件
2. **批量操作优化**:
```xml
<!-- Mybatis 批量插入配置 -->
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO question (type, content, options, answer)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.type}, #{item.content}, #{item.options}, #{item.answer})
</foreach>
</insert>
```
3. **安全控制**:
- JWT 身份验证
- Spring Security 权限控制
- 文件类型白名单校验
### 六、部署方案
```
Nginx(前端部署)
│
├── Vue3静态资源
│
└── 反向代理 → SpringBoot (Tomcat)
│
└── MySQL 数据库
```
### 学习资源推荐
1. Vue3+Element Plus 后台管理系统实战[^1]
2. Mybatis 批量操作优化技巧[^2]
3. SpringBoot 文件上传最佳实践:https://spring.io/guides/gs/uploading-files/