feat: interview review module with whisper.cpp ASR + AI analysis + frontend page
New backend module 'interview-review' provides: - Audio upload (50MB limit, MP3/M4A/WAV/AAC/OGG/MP4/WebM) - Text transcript submission - whisper.cpp local ASR integration (tiny + base models) - AI analysis (4-dimension scoring: logic/expression/professionalism/stability) - Speech analysis (filler words detection, pace, duration) - Async processing pipeline with status polling - Graceful fallback to mock ASR when whisper unavailable New frontend page 'pages/review/review.vue' with 3 modes: - List mode: review history with status indicators - Upload mode: audio file upload or text paste - Report mode: score radar, dimension bars, analysis details Docs updated: PROJECT-STATUS.md v4.4, FEATURE-LIST.md v4.2, ROADMAP.md v4.2
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
|
||||
import { Document, Types } from 'mongoose'
|
||||
|
||||
export type InterviewReviewDocument = InterviewReview & Document
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class InterviewReview {
|
||||
@Prop({ type: Types.ObjectId, ref: 'User', required: true })
|
||||
userId: Types.ObjectId
|
||||
|
||||
@Prop({ required: true })
|
||||
position: string
|
||||
|
||||
@Prop({ default: '' })
|
||||
company: string
|
||||
|
||||
@Prop({ default: 'processing' })
|
||||
status: 'processing' | 'completed' | 'failed'
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
audioFile?: {
|
||||
hash: string
|
||||
filePath: string
|
||||
duration: number
|
||||
size: number
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
transcript?: {
|
||||
fullText: string
|
||||
segments: {
|
||||
startTime: number
|
||||
endTime: number
|
||||
speaker: 'interviewer' | 'candidate'
|
||||
text: string
|
||||
}[]
|
||||
}
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
analysis?: {
|
||||
overallScore: number
|
||||
dimensions: {
|
||||
logic: number
|
||||
expression: number
|
||||
professionalism: number
|
||||
stability: number
|
||||
}
|
||||
strengths: string[]
|
||||
weaknesses: string[]
|
||||
suggestions: string[]
|
||||
questionBreakdown: {
|
||||
question: string
|
||||
answer: string
|
||||
score: number
|
||||
comment: string
|
||||
suggestedAnswer: string
|
||||
}[]
|
||||
}
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
speechAnalysis?: {
|
||||
fillerWords: { word: string; count: number }[]
|
||||
fillerScore: number
|
||||
fillerDensity: number
|
||||
pace: string
|
||||
totalDuration: number
|
||||
totalChars: number
|
||||
}
|
||||
|
||||
@Prop({ default: 0 })
|
||||
retryCount: number
|
||||
|
||||
readonly createdAt?: Date
|
||||
readonly updatedAt?: Date
|
||||
}
|
||||
|
||||
export const InterviewReviewSchema = SchemaFactory.createForClass(InterviewReview)
|
||||
InterviewReviewSchema.index({ userId: 1, createdAt: -1 })
|
||||
Reference in New Issue
Block a user