fixed cheating michanism

This commit is contained in:
2026-05-24 20:11:00 +08:00
parent 0de72a629c
commit 6cac2f0c9a
4 changed files with 694 additions and 142 deletions

View File

@@ -65,6 +65,31 @@ export interface IVocabularyTestAttempt extends Document {
testType: 'chinese-to-english' | 'audio-to-english' | 'multiple-choice';
questionWordIds: mongoose.Types.ObjectId[];
questionTokens: string[];
optionTokens?: string[][];
optionTexts?: string[][];
answeredQuestionTokens?: string[];
answerDurations?: number[];
answers?: Array<{
questionToken: string;
word: mongoose.Types.ObjectId;
userAnswer: string;
correctAnswer: string;
isCorrect: boolean;
submittedAt: Date;
duration: number;
riskFlags: string[];
interactionSummary?: {
keyCount: number;
inputCount: number;
pasteCount: number;
focusCount: number;
blurCount: number;
pointerCount: number;
firstEventOffset: number;
lastEventOffset: number;
};
}>;
riskFlags?: string[];
issuedAt: Date;
expiresAt: Date;
submittedAt?: Date;
@@ -163,6 +188,31 @@ const VocabularyTestAttemptSchema = new Schema<IVocabularyTestAttempt>({
},
questionWordIds: [{ type: Schema.Types.ObjectId, ref: 'Word', required: true }],
questionTokens: [{ type: String, required: true }],
optionTokens: [[{ type: String }]],
optionTexts: [[{ type: String }]],
answeredQuestionTokens: [{ type: String }],
answerDurations: [{ type: Number }],
answers: [{
questionToken: { type: String, required: true },
word: { type: Schema.Types.ObjectId, ref: 'Word', required: true },
userAnswer: { type: String, default: '' },
correctAnswer: { type: String, required: true },
isCorrect: { type: Boolean, required: true },
submittedAt: { type: Date, required: true },
duration: { type: Number, required: true },
riskFlags: [{ type: String }],
interactionSummary: {
keyCount: { type: Number, default: 0 },
inputCount: { type: Number, default: 0 },
pasteCount: { type: Number, default: 0 },
focusCount: { type: Number, default: 0 },
blurCount: { type: Number, default: 0 },
pointerCount: { type: Number, default: 0 },
firstEventOffset: { type: Number, default: 0 },
lastEventOffset: { type: Number, default: 0 }
}
}],
riskFlags: [{ type: String }],
issuedAt: { type: Date, required: true },
expiresAt: { type: Date, required: true },
submittedAt: Date,