assessment-submit
约 1416 字大约 5 分钟
2025-10-09
概述
assessment-submit 是一个基于 Deno 和 Supabase 的测试提交与自动评分 API。它处理学生提交的测试答案,进行自动评分,并更新测试状态。支持单选题、多选题、判断题的自动评分和主观题的人工评分流程。
接口定义
SubmitRequest 接口
测试提交请求的主体结构。
interface SubmitRequest {
assessment_id: string; // 测试记录ID
answers: AnswerItem[]; // 答案数组
force_submit?: boolean; // 是否强制提交(可选)
}AnswerItem 接口
单个答案项的定义。
interface AnswerItem {
question_id: string; // 题目ID
student_answer: string | string[] | boolean | boolean[] | number; // 学生答案
time_taken?: number; // 答题耗时(秒,可选)
}QuestionData 接口
题目数据结构定义。
interface QuestionData {
id: string; // 题目ID
type: string; // 题目类型
points: number; // 题目分值
correct_answers: string | string[] | boolean | boolean[]; // 正确答案
}核心函数详解
validateSubmitRequest
功能: 验证测试提交请求的合法性
参数:
body: unknown- 待验证的请求体
返回值: SubmitRequest - 验证通过的请求对象
验证规则:
- 必填字段检查:
assessment_id,answers assessment_idUUID 格式验证answers数组非空检查- 每个答案项的
question_idUUID 格式验证 - 每个答案项的
student_answer非空检查 - 每个答案项的
time_taken非负数检查(如果提供)
抛出错误: 验证失败时抛出详细的错误信息
使用示例:
const requestBody = {
assessment_id: "550e8400-e29b-41d4-a716-446655440000",
answers: [
{
question_id: "550e8400-e29b-41d4-a716-446655440001",
student_answer: "A",
time_taken: 30
}
]
};
const validatedRequest = validateSubmitRequest(requestBody);autoGradeAnswer
功能: 自动评分函数,根据题目类型和正确答案对学生答案进行评分
参数:
studentAnswer: string | string[] | boolean | boolean[] | number- 学生答案correctAnswers: string | string[] | boolean | boolean[]- 正确答案questionType: string- 题目类型points: number- 题目分值
返回值: { isCorrect: boolean | null; score: number } - 评分结果
评分规则:
- 单选题/判断题: 完全匹配正确答案
- 多选题: 答案集合完全匹配(排序后比较)
- 主观题: 返回
isCorrect: null和score: 0,需要人工评分
使用示例:
// 单选题评分
const result1 = autoGradeAnswer("A", ["A"], "single_choice", 2);
// { isCorrect: true, score: 2 }
// 多选题评分
const result2 = autoGradeAnswer(["A", "B"], ["B", "A"], "multiple_choice", 3);
// { isCorrect: true, score: 3 }
// 主观题评分
const result3 = autoGradeAnswer("很长的主观题答案", "参考答案", "subjective", 5);
// { isCorrect: null, score: 0 }processAssessmentSubmit
功能: 处理测试提交和评分的核心函数
参数:
submitRequest: SubmitRequest- 验证通过的提交请求
返回值: Promise<any> - 处理结果包含测试信息和评分摘要
处理流程:
- 验证测试状态(必须为 "in_progress")
- 获取题库中的所有题目信息
- 验证答案完整性(除非强制提交)
- 验证所有题目ID的有效性
- 自动评分所有客观题
- 批量插入答案记录
- 更新测试记录状态和统计信息
- 返回处理结果
状态更新规则:
- 不含主观题: 状态变为 "completed",记录完成时间
- 含主观题: 状态变为 "pending_review",等待人工评分
使用示例:
const submitRequest = {
assessment_id: "test-id-123",
answers: [
{
question_id: "question-1",
student_answer: "A",
time_taken: 45
}
],
force_submit: false
};
const result = await processAssessmentSubmit(submitRequest);主处理函数
Deno.serve
功能: 处理HTTP请求的主入口点
支持的HTTP方法: POST
处理流程:
- 处理CORS预检请求
- 验证请求方法
- 解析和验证请求体
- 处理测试提交和评分
- 返回成功响应
使用示例
请求示例
curl -X POST https://your-domain.com/functions/v1/assessment-submit \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SUPABASE_TOKEN" \
-d '{
"assessment_id": "550e8400-e29b-41d4-a716-446655440000",
"answers": [
{
"question_id": "550e8400-e29b-41d4-a716-446655440001",
"student_answer": "A",
"time_taken": 30
},
{
"question_id": "550e8400-e29b-41d4-a716-446655440002",
"student_answer": ["A", "C"],
"time_taken": 45
},
{
"question_id": "550e8400-e29b-41d4-a716-446655440003",
"student_answer": true,
"time_taken": 15
}
],
"force_submit": false
}'成功响应示例(不含主观题)
{
"success": true,
"message": "测试提交成功,所有题目已自动评分完成",
"data": {
"assessment": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"completed_at": "2024-01-01T00:00:00.000Z",
"answered_questions": 3,
"correct_answers": 2,
"total_score": 8,
"time_spent": 90
},
"answers_summary": {
"total_submitted": 3,
"correct_count": 2,
"total_score": 8,
"has_subjective_questions": false
}
}
}成功响应示例(含主观题)
{
"success": true,
"message": "测试提交成功,包含主观题的答案正在等待人工评分",
"data": {
"assessment": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending_review",
"completed_at": null,
"answered_questions": 4,
"correct_answers": 2,
"total_score": 6,
"time_spent": 120
},
"answers_summary": {
"total_submitted": 4,
"correct_count": 2,
"total_score": 6,
"has_subjective_questions": true
}
}
}错误响应示例
{
"success": false,
"error": "还有 2 道题未完成,如需强制提交请设置 force_submit: true",
"code": "INCOMPLETE_ANSWERS"
}错误处理
验证错误 (400)
assessment_id格式错误answers数组为空question_id格式错误student_answer为空time_taken为负数
业务错误 (400)
- 测试不存在或已完成
- 获取题目信息失败
- 答案不完整(未强制提交时)
- 题目ID不存在于题库中
系统错误 (500)
- 数据库操作失败
- 批量插入答案失败
- 更新测试状态失败
数据库表结构
student_assessments 表
CREATE TABLE student_assessments (
id UUID PRIMARY KEY,
question_bank_id UUID NOT NULL,
student_id UUID NOT NULL,
status TEXT NOT NULL DEFAULT 'in_progress',
completed_at TIMESTAMPTZ,
answered_questions INTEGER DEFAULT 0,
correct_answers INTEGER DEFAULT 0,
total_score INTEGER DEFAULT 0,
time_spent INTEGER DEFAULT 0
);student_answers 表
CREATE TABLE student_answers (
assessment_id UUID NOT NULL,
question_id UUID NOT NULL,
student_answer JSONB NOT NULL,
is_correct BOOLEAN,
score INTEGER DEFAULT 0,
time_taken INTEGER,
answered_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (assessment_id, question_id)
);questions 表
CREATE TABLE questions (
id UUID PRIMARY KEY,
question_bank_id UUID NOT NULL,
type TEXT NOT NULL,
points INTEGER NOT NULL,
correct_answers JSONB NOT NULL,
is_active BOOLEAN DEFAULT true
);前端集成示例
// 提交测试答案
async function submitAssessment(assessmentId, answers, forceSubmit = false) {
try {
const response = await fetch('/assessment-submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getAccessToken()}`
},
body: JSON.stringify({
assessment_id: assessmentId,
answers: answers,
force_submit: forceSubmit
})
});
const result = await response.json();
if (result.success) {
return result.data;
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('提交测试失败:', error);
throw error;
}
}
// 使用示例
const answers = [
{
question_id: 'question-1',
student_answer: 'A',
time_taken: 30
},
{
question_id: 'question-2',
student_answer: ['A', 'C'],
time_taken: 45
}
];
const result = await submitAssessment('assessment-123', answers, false);
if (result.answers_summary.has_subjective_questions) {
console.log('包含主观题,等待老师评分');
} else {
console.log(`得分: ${result.assessment.total_score}`);
}版权所有
版权归属:Evoliant
许可证:MIT