import { Controller, Post, Get, Delete, Param, Query, UseInterceptors, UploadedFile, Body, HttpException, HttpStatus, } from '@nestjs/common' import { FileInterceptor } from '@nestjs/platform-express' import { diskStorage } from 'multer' import { extname, join } from 'path' import * as fs from 'fs' import { randomUUID } from 'crypto' import { InterviewReviewService } from './interview-review.service' import { CurrentUser } from '../../common/decorators/current-user.decorator' const UPLOAD_DIR = join(process.cwd(), 'uploads', 'reviews') if (!fs.existsSync(UPLOAD_DIR)) { fs.mkdirSync(UPLOAD_DIR, { recursive: true }) } @Controller('interview-review') export class InterviewReviewController { constructor(private service: InterviewReviewService) {} /** Upload audio file + metadata */ @Post() @UseInterceptors(FileInterceptor('file', { storage: diskStorage({ destination: (_req, _file, cb) => cb(null, UPLOAD_DIR), filename: (_req, file, cb) => { const name = randomUUID() + extname(file.originalname || '.mp3') cb(null, name) }, }), limits: { fileSize: 50 * 1024 * 1024 }, fileFilter: (_req, file, cb) => { const allowed = /\.(mp3|m4a|wav|aac|ogg|mp4|webm)$/i if (allowed.test(extname(file.originalname))) { cb(null, true) } else { cb(new HttpException('仅支持 mp3/m4a/wav/aac/ogg 格式', HttpStatus.BAD_REQUEST), false) } }, })) async uploadFile( @UploadedFile() file: any, @Body('position') position: string, @Body('company') company: string, @CurrentUser('userId') userId: string, ) { if (!file) { throw new HttpException('请上传录音文件', HttpStatus.BAD_REQUEST) } if (!position || !position.trim()) { throw new HttpException('请填写面试岗位', HttpStatus.BAD_REQUEST) } return this.service.create(userId, position.trim(), company?.trim(), file) } /** Submit text transcript directly (no audio) */ @Post('text') async submitText( @Body('position') position: string, @Body('company') company: string, @Body('text') text: string, @CurrentUser('userId') userId: string, ) { if (!position || !position.trim()) { throw new HttpException('请填写面试岗位', HttpStatus.BAD_REQUEST) } if (!text || !text.trim()) { throw new HttpException('请填写面试转录文本', HttpStatus.BAD_REQUEST) } return this.service.createFromText(userId, position.trim(), text.trim(), company?.trim()) } @Get('list') async list( @Query('page') page: string, @Query('limit') limit: string, @CurrentUser('userId') userId: string, ) { return this.service.listByUser(userId, parseInt(page) || 1, parseInt(limit) || 20) } @Get(':id') async getDetail( @Param('id') id: string, @CurrentUser('userId') userId: string, ) { return this.service.getDetail(id, userId) } @Delete(':id') async delete( @Param('id') id: string, @CurrentUser('userId') userId: string, ) { return this.service.delete(id, userId) } }