fixed fake post attack
This commit is contained in:
@@ -41,6 +41,7 @@ export interface IVocabularyTestRecord extends Document {
|
||||
user: mongoose.Types.ObjectId;
|
||||
wordSet: mongoose.Types.ObjectId;
|
||||
testType: 'chinese-to-english' | 'audio-to-english' | 'multiple-choice';
|
||||
attempt?: mongoose.Types.ObjectId;
|
||||
stats: {
|
||||
totalWords: number;
|
||||
correctWords: number;
|
||||
@@ -49,9 +50,29 @@ export interface IVocabularyTestRecord extends Document {
|
||||
endTime: Date;
|
||||
duration: number;
|
||||
};
|
||||
results?: Array<{
|
||||
word: mongoose.Types.ObjectId;
|
||||
userAnswer: string;
|
||||
correctAnswer: string;
|
||||
isCorrect: boolean;
|
||||
}>;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface IVocabularyTestAttempt extends Document {
|
||||
user: mongoose.Types.ObjectId;
|
||||
wordSet: mongoose.Types.ObjectId;
|
||||
testType: 'chinese-to-english' | 'audio-to-english' | 'multiple-choice';
|
||||
questionWordIds: mongoose.Types.ObjectId[];
|
||||
questionTokens: string[];
|
||||
issuedAt: Date;
|
||||
expiresAt: Date;
|
||||
submittedAt?: Date;
|
||||
status: 'active' | 'submitted' | 'expired';
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// 单词模式
|
||||
const WordSchema = new Schema<IWord>({
|
||||
word: { type: String, required: true, trim: true },
|
||||
@@ -83,7 +104,8 @@ const ModeStatsSchema = new Schema({
|
||||
totalWrong: { type: Number, default: 0 },
|
||||
mastered: { type: Boolean, default: false },
|
||||
inWrongBook: { type: Boolean, default: false },
|
||||
lastTestedAt: Date
|
||||
lastTestedAt: Date,
|
||||
lastMasteredAt: Date
|
||||
}, { _id: false });
|
||||
|
||||
// 简化后的主记录模式
|
||||
@@ -107,6 +129,7 @@ const WordRecordSchema = new Schema({
|
||||
const VocabularyTestRecordSchema = new Schema<IVocabularyTestRecord>({
|
||||
user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
||||
wordSet: { type: Schema.Types.ObjectId, ref: 'WordSet', required: true },
|
||||
attempt: { type: Schema.Types.ObjectId, ref: 'VocabularyTestAttempt' },
|
||||
testType: {
|
||||
type: String,
|
||||
enum: ['chinese-to-english', 'audio-to-english', 'multiple-choice'],
|
||||
@@ -120,18 +143,51 @@ const VocabularyTestRecordSchema = new Schema<IVocabularyTestRecord>({
|
||||
endTime: { type: Date, required: true },
|
||||
duration: { type: Number, required: true }
|
||||
},
|
||||
results: [{
|
||||
word: { type: Schema.Types.ObjectId, ref: 'Word', required: true },
|
||||
userAnswer: { type: String, default: '' },
|
||||
correctAnswer: { type: String, required: true },
|
||||
isCorrect: { type: Boolean, required: true }
|
||||
}],
|
||||
createdAt: { type: Date, default: Date.now }
|
||||
});
|
||||
|
||||
// 单词测试会话模式,用于防止直接伪造测试提交
|
||||
const VocabularyTestAttemptSchema = new Schema<IVocabularyTestAttempt>({
|
||||
user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
||||
wordSet: { type: Schema.Types.ObjectId, ref: 'WordSet', required: true },
|
||||
testType: {
|
||||
type: String,
|
||||
enum: ['chinese-to-english', 'audio-to-english', 'multiple-choice'],
|
||||
required: true
|
||||
},
|
||||
questionWordIds: [{ type: Schema.Types.ObjectId, ref: 'Word', required: true }],
|
||||
questionTokens: [{ type: String, required: true }],
|
||||
issuedAt: { type: Date, required: true },
|
||||
expiresAt: { type: Date, required: true },
|
||||
submittedAt: Date,
|
||||
status: {
|
||||
type: String,
|
||||
enum: ['active', 'submitted', 'expired'],
|
||||
default: 'active',
|
||||
required: true
|
||||
}
|
||||
}, { timestamps: true });
|
||||
|
||||
VocabularyTestAttemptSchema.index({ user: 1, status: 1, expiresAt: 1 });
|
||||
VocabularyTestAttemptSchema.index({ expiresAt: 1 });
|
||||
|
||||
// 创建和导出模型
|
||||
export const Word = mongoose.model<IWord>('Word', WordSchema);
|
||||
export const WordSet = mongoose.model<IWordSet>('WordSet', WordSetSchema);
|
||||
export const WordRecord = mongoose.model('WordRecord', WordRecordSchema);
|
||||
export const VocabularyTestRecord = mongoose.model<IVocabularyTestRecord>('VocabularyTestRecord', VocabularyTestRecordSchema);
|
||||
export const VocabularyTestAttempt = mongoose.model<IVocabularyTestAttempt>('VocabularyTestAttempt', VocabularyTestAttemptSchema);
|
||||
|
||||
export default {
|
||||
Word,
|
||||
WordSet,
|
||||
WordRecord,
|
||||
VocabularyTestRecord
|
||||
};
|
||||
VocabularyTestRecord,
|
||||
VocabularyTestAttempt
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user