feat: implement QuestionBank CRUD with pagination and template query

- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
This commit is contained in:
Developer
2026-04-23 17:19:11 +08:00
commit 0a9588abb7
492 changed files with 112453 additions and 0 deletions
@@ -0,0 +1,161 @@
import {
Controller,
Post,
Body,
Get,
Param,
UseGuards,
Request,
Sse,
MessageEvent,
Query,
Delete,
} from '@nestjs/common';
import { map } from 'rxjs/operators';
import { AssessmentService } from './assessment.service';
import { CombinedAuthGuard } from '../auth/combined-auth.guard';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@ApiTags('Assessment')
@Controller('assessment')
@UseGuards(CombinedAuthGuard)
export class AssessmentController {
constructor(private readonly assessmentService: AssessmentService) {}
@Post('start')
@ApiOperation({ summary: 'Start a new assessment session' })
async startSession(
@Request() req: any,
@Body()
body: { knowledgeBaseId?: string; language?: string; templateId?: string },
) {
const { id: userId, tenantId } = req.user;
console.log(
`[AssessmentController] startSession: user=${userId}, tenant=${tenantId}, templateId=${body.templateId}, kbId=${body.knowledgeBaseId}`,
);
return this.assessmentService.startSession(
userId,
body.knowledgeBaseId,
tenantId,
body.language,
body.templateId,
);
}
@Post(':id/answer')
@ApiOperation({ summary: 'Submit an answer to the current question' })
async submitAnswer(
@Request() req: any,
@Param('id') sessionId: string,
@Body() body: { answer: string; language?: string },
) {
const { id: userId } = req.user;
console.log(
`[AssessmentController] >>> submitAnswer CALLED: user=${userId}, session=${sessionId}, answerLen=${body.answer?.length}`,
);
return this.assessmentService.submitAnswer(
sessionId,
userId,
body.answer,
body.language,
);
}
@Sse(':id/start-stream')
@ApiOperation({ summary: 'Stream initial session generation' })
startSessionStream(@Request() req: any, @Param('id') sessionId: string) {
const { id: userId } = req.user;
console.log(
`[AssessmentController] startSessionStream: user=${userId}, session=${sessionId}`,
);
return this.assessmentService
.startSessionStream(sessionId, userId)
.pipe(map((data) => ({ data }) as MessageEvent));
}
@Sse(':id/answer-stream')
@ApiOperation({
summary: 'Stream answer evaluation and next question generation',
})
submitAnswerStream(
@Request() req: any,
@Param('id') sessionId: string,
@Query('answer') answer: string,
@Query('language') language?: string,
) {
const { id: userId } = req.user;
console.log(
`[AssessmentController] >>> submitAnswerStream CALLED: user=${userId}, session=${sessionId}, answerLen=${answer?.length}, lang=${language}`,
);
return this.assessmentService
.submitAnswerStream(sessionId, userId, answer, language)
.pipe(map((data) => ({ data }) as MessageEvent));
}
@Get(':id/state')
@ApiOperation({ summary: 'Get the current state of an assessment session' })
async getSessionState(@Request() req: any, @Param('id') sessionId: string) {
const { id: userId } = req.user;
console.log(
`[AssessmentController] getSessionState: user=${userId}, session=${sessionId}`,
);
return this.assessmentService.getSessionState(sessionId, userId);
}
@Get()
@ApiOperation({ summary: 'Get assessment session history' })
async getHistory(@Request() req: any) {
const { id: userId, tenantId } = req.user;
console.log(
`[AssessmentController] getHistory: user=${userId}, tenant=${tenantId}`,
);
return this.assessmentService.getHistory(userId, tenantId);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete an assessment session' })
async deleteSession(@Request() req: any, @Param('id') sessionId: string) {
const user = req.user;
console.log(
`[AssessmentController] deleteSession: user=${user.id}, role=${user.role}, session=${sessionId}`,
);
return this.assessmentService.deleteSession(sessionId, user);
}
@Get(':id/certificate')
@ApiOperation({ summary: 'Get certificate for completed assessment' })
async getCertificate(
@Request() req: any,
@Param('id') sessionId: string,
) {
const { id: userId, tenantId } = req.user;
console.log(
`[AssessmentController] getCertificate: user=${userId}, session=${sessionId}`,
);
return this.assessmentService.generateCertificate(sessionId, userId, tenantId);
}
@Get('stats')
@ApiOperation({ summary: 'Get assessment statistics for admin' })
async getStats(
@Request() req: any,
@Query('startDate') startDate?: string,
@Query('endDate') endDate?: string,
@Query('templateId') templateId?: string,
@Query('knowledgeGroupId') knowledgeGroupId?: string,
) {
const { id: userId, tenantId, role } = req.user;
console.log(
`[AssessmentController] getStats: user=${userId}, role=${role}, tenant=${tenantId}`,
);
return this.assessmentService.getStats(
userId,
tenantId,
role,
startDate,
endDate,
templateId,
knowledgeGroupId,
);
}
}