feat: 初始化考培练系统项目

- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
111
2026-01-24 19:33:28 +08:00
commit 998211c483
1197 changed files with 228429 additions and 0 deletions

View File

@@ -0,0 +1,590 @@
openapi: 3.0.0
info:
title: 考试模块 API
version: 1.0.0
description: 考培练系统考试模块的 API 接口定义
servers:
- url: http://localhost:8000/api/v1
description: 本地开发服务器
paths:
/exams/start:
post:
summary: 开始考试(动态组卷)
description: 根据指定参数动态生成试卷并开始考试
tags:
- 考试管理
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ExamStartRequest'
responses:
'200':
description: 考试开始成功
content:
application/json:
schema:
$ref: '#/components/schemas/ExamSessionResponse'
'400':
description: 请求参数错误或存在未完成的考试
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
/exams/{examId}/submit:
post:
summary: 提交考试
description: 提交考试答案并生成成绩
tags:
- 考试管理
security:
- bearerAuth: []
parameters:
- name: examId
in: path
required: true
schema:
type: integer
description: 考试ID
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ExamSubmitRequest'
responses:
'200':
description: 考试提交成功
content:
application/json:
schema:
$ref: '#/components/schemas/ExamResultResponse'
'400':
description: 考试状态不正确或已超时
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
description: 无权访问此考试
'404':
description: 考试不存在
/exams/{examId}:
get:
summary: 获取考试详情
description: 获取考试会话信息和题目列表(不包含答案)
tags:
- 考试管理
security:
- bearerAuth: []
parameters:
- name: examId
in: path
required: true
schema:
type: integer
description: 考试ID
responses:
'200':
description: 获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/ExamSessionResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
'404':
description: 考试不存在
/exams/records:
get:
summary: 获取考试记录列表
description: 分页获取用户的考试记录
tags:
- 考试管理
security:
- bearerAuth: []
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
description: 页码
- name: page_size
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
description: 每页数量
- name: status
in: query
schema:
$ref: '#/components/schemas/ExamStatus'
description: 考试状态筛选
responses:
'200':
description: 获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/ExamRecordListResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
/exams/{examId}/result:
get:
summary: 获取考试结果
description: 获取详细的考试成绩、统计信息和答案详情
tags:
- 考试管理
security:
- bearerAuth: []
parameters:
- name: examId
in: path
required: true
schema:
type: integer
description: 考试ID
responses:
'200':
description: 获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/ExamResultResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
'404':
description: 考试结果不存在
/exams/mistakes:
get:
summary: 获取错题列表
description: 分页获取用户的错题记录
tags:
- 错题管理
security:
- bearerAuth: []
parameters:
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
description: 页码
- name: page_size
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
description: 每页数量
- name: is_mastered
in: query
schema:
type: boolean
description: 是否已掌握
responses:
'200':
description: 获取成功
content:
application/json:
schema:
$ref: '#/components/schemas/MistakeListResponse'
'401':
$ref: '#/components/responses/UnauthorizedError'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
ExamStartRequest:
type: object
required:
- exam_name
properties:
course_id:
type: integer
description: 课程ID
exam_name:
type: string
description: 考试名称
question_count:
type: integer
minimum: 1
maximum: 100
default: 20
description: 题目数量
time_limit:
type: integer
minimum: 1
maximum: 300
description: 考试时长(分钟)
difficulty:
type: integer
minimum: 1
maximum: 5
description: 难度等级
knowledge_points:
type: array
items:
type: string
description: 知识点范围
question_types:
type: array
items:
$ref: '#/components/schemas/QuestionType'
description: 题型范围
ExamSubmitRequest:
type: object
required:
- answers
properties:
answers:
type: array
items:
$ref: '#/components/schemas/AnswerSubmitRequest'
description: 答案列表
force_submit:
type: boolean
default: false
description: 是否强制提交
AnswerSubmitRequest:
type: object
required:
- question_id
- user_answer
properties:
question_id:
type: integer
description: 题目ID
user_answer:
type: string
description: 用户答案
time_spent:
type: integer
minimum: 0
description: 答题用时(秒)
ExamStatus:
type: string
enum:
- created
- in_progress
- submitted
- graded
- expired
description: 考试状态
QuestionType:
type: string
enum:
- single_choice
- multiple_choice
- true_false
- fill_blank
- short_answer
- essay
description: 题目类型
ExamSessionResponse:
type: object
properties:
code:
type: integer
example: 200
message:
type: string
example: success
data:
type: object
properties:
id:
type: integer
exam_name:
type: string
course_id:
type: integer
total_questions:
type: integer
total_score:
type: number
pass_score:
type: number
time_limit:
type: integer
status:
$ref: '#/components/schemas/ExamStatus'
started_at:
type: string
format: date-time
submitted_at:
type: string
format: date-time
questions:
type: array
items:
$ref: '#/components/schemas/QuestionResponse'
QuestionResponse:
type: object
properties:
id:
type: integer
question_order:
type: integer
question_type:
$ref: '#/components/schemas/QuestionType'
question_text:
type: string
options:
type: object
additionalProperties:
type: string
score:
type: number
knowledge_points:
type: array
items:
type: string
difficulty:
type: integer
ExamResultResponse:
type: object
properties:
code:
type: integer
example: 200
message:
type: string
example: success
data:
type: object
properties:
id:
type: integer
exam_id:
type: integer
exam_name:
type: string
total_score:
type: number
actual_score:
type: number
percentage_score:
type: number
is_passed:
type: boolean
total_questions:
type: integer
answered_questions:
type: integer
correct_questions:
type: integer
question_type_stats:
type: object
knowledge_stats:
type: object
total_time_spent:
type: integer
average_time_per_question:
type: number
ai_analysis:
type: string
improvement_suggestions:
type: array
items:
type: string
answer_details:
type: array
items:
$ref: '#/components/schemas/AnswerDetailResponse'
AnswerDetailResponse:
type: object
properties:
question_id:
type: integer
question_order:
type: integer
question_type:
$ref: '#/components/schemas/QuestionType'
question_text:
type: string
user_answer:
type: string
correct_answer:
type: string
is_correct:
type: boolean
actual_score:
type: number
total_score:
type: number
answer_explanation:
type: string
ai_feedback:
type: string
ExamRecordListResponse:
type: object
properties:
code:
type: integer
example: 200
message:
type: string
example: success
data:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/ExamRecordResponse'
total:
type: integer
page:
type: integer
page_size:
type: integer
total_pages:
type: integer
ExamRecordResponse:
type: object
properties:
id:
type: integer
exam_name:
type: string
course_id:
type: integer
course_name:
type: string
status:
$ref: '#/components/schemas/ExamStatus'
total_questions:
type: integer
actual_score:
type: number
percentage_score:
type: number
is_passed:
type: boolean
started_at:
type: string
format: date-time
submitted_at:
type: string
format: date-time
MistakeListResponse:
type: object
properties:
code:
type: integer
example: 200
message:
type: string
example: success
data:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/MistakeResponse'
total:
type: integer
page:
type: integer
page_size:
type: integer
total_pages:
type: integer
MistakeResponse:
type: object
properties:
id:
type: integer
question_type:
$ref: '#/components/schemas/QuestionType'
question_text:
type: string
user_answer:
type: string
correct_answer:
type: string
knowledge_points:
type: array
items:
type: string
difficulty:
type: integer
review_count:
type: integer
is_mastered:
type: boolean
last_review_at:
type: string
format: date-time
created_at:
type: string
format: date-time
ErrorResponse:
type: object
properties:
code:
type: integer
message:
type: string
error:
type: object
properties:
code:
type: string
message:
type: string
details:
type: object
responses:
UnauthorizedError:
description: 未授权,需要登录
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
InternalServerError:
description: 服务器内部错误
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

View File

@@ -0,0 +1,220 @@
# 考试模块Exam Module
## 模块概述
考试模块是考培练系统的核心功能之一提供动态组卷、在线考试、自动判题、成绩分析和错题管理等功能。该模块与Dify AI平台集成支持智能出题和主观题自动评分。
## 主要功能
### 1. 动态组卷
- 根据课程、知识点、难度等参数动态生成试卷
- 支持多种题型:单选题、多选题、判断题、填空题、简答题、论述题
- 通过Dify工作流实现智能出题
### 2. 考试管理
- 考试计时和状态管理
- 防作弊机制(防重复提交、超时控制)
- 题目与答案分离存储,确保安全性
### 3. 自动判题
- 客观题自动判分
- 主观题通过Dify AI评分
- 实时计算成绩和统计信息
### 4. 成绩分析
- 详细的成绩报告
- 题型正确率统计
- 知识点掌握情况分析
- AI生成的学习建议
### 5. 错题管理
- 自动记录错题
- 支持错题复习和标记掌握状态
- 按知识点分类管理
## 技术架构
### 数据模型
- `ExamSession`: 考试会话表
- `ExamQuestion`: 考试题目表
- `ExamAnswer`: 考试答案表
- `ExamResult`: 考试结果表
- `Mistake`: 错题记录表
### API接口
#### 1. 开始考试
```
POST /api/v1/exams/start
```
- 功能:动态生成试卷并开始考试
- 权限:需要登录
- 参数课程ID、题目数量、时长、难度等
#### 2. 提交考试
```
POST /api/v1/exams/{examId}/submit
```
- 功能:提交答案并生成成绩
- 权限:需要登录,只能提交自己的考试
- 参数:答案列表
#### 3. 获取考试详情
```
GET /api/v1/exams/{examId}
```
- 功能:获取考试信息和题目(不含答案)
- 权限:需要登录,只能查看自己的考试
#### 4. 获取考试记录
```
GET /api/v1/exams/records
```
- 功能:分页获取考试历史记录
- 权限:需要登录
- 支持按状态筛选
#### 5. 获取考试结果
```
GET /api/v1/exams/{examId}/result
```
- 功能:获取详细的考试成绩和分析
- 权限:需要登录,只能查看自己的成绩
#### 6. 获取错题列表
```
GET /api/v1/exams/mistakes
```
- 功能:分页获取错题记录
- 权限:需要登录
- 支持按掌握状态筛选
## 配置说明
### 环境变量
```env
# Dify配置
DIFY_API_BASE=https://api.dify.ai/v1
DIFY_API_KEY=your_api_key
DIFY_EXAM_WORKFLOW_ID=exam_workflow_id
DIFY_EVAL_WORKFLOW_ID=eval_workflow_id
DIFY_TIMEOUT=30
```
### 考试参数限制
- 题目数量1-100题
- 考试时长1-300分钟
- 难度等级1-5级
- 默认及格分60分
## 使用示例
### 1. 开始考试
```python
# 请求
POST /api/v1/exams/start
{
"exam_name": "Python基础测试",
"question_count": 20,
"time_limit": 60,
"difficulty": 3,
"knowledge_points": ["Python基础", "数据结构"],
"question_types": ["single_choice", "true_false"]
}
# 响应
{
"code": 200,
"message": "考试开始成功",
"data": {
"id": 1,
"exam_name": "Python基础测试",
"total_questions": 20,
"total_score": 100.0,
"time_limit": 60,
"status": "in_progress",
"started_at": "2024-01-01T10:00:00",
"questions": [...]
}
}
```
### 2. 提交考试
```python
# 请求
POST /api/v1/exams/1/submit
{
"answers": [
{
"question_id": 1,
"user_answer": "B",
"time_spent": 30
},
{
"question_id": 2,
"user_answer": "True",
"time_spent": 20
}
]
}
# 响应
{
"code": 200,
"message": "考试提交成功",
"data": {
"exam_id": 1,
"total_score": 100.0,
"actual_score": 85.0,
"percentage_score": 85.0,
"is_passed": true,
"correct_questions": 17,
"ai_analysis": "您在Python基础部分表现优秀...",
"improvement_suggestions": ["建议加强数据结构的学习"]
}
}
```
## 安全考虑
1. **认证控制**所有接口需要JWT认证
2. **权限隔离**:用户只能访问自己的考试数据
3. **防作弊机制**
- 题目与答案分离存储
- 考试状态严格控制
- 超时自动结束
- 防止重复提交
## 性能优化
1. **数据库索引**
- 用户ID和考试状态联合索引
- 考试ID和题目顺序联合索引
2. **分页查询**
- 考试记录和错题列表支持分页
- 默认每页20条最大100条
3. **异步处理**
- 使用异步数据库操作
- Dify API调用设置超时控制
## 扩展性
该模块设计考虑了未来的扩展需求:
1. **题库管理**:预留了题库查询接口
2. **批量导入**支持从Excel导入试题
3. **考试模板**:可保存常用考试配置
4. **团体考试**:支持班级或部门统一考试
5. **证书生成**:考试通过后生成电子证书
## 测试覆盖
- 单元测试:覆盖所有服务层方法
- 集成测试覆盖所有API接口
- 测试场景包括:
- 正常流程测试
- 异常情况处理
- 权限控制验证
- 边界条件测试