P3-02-03-04: audit log, batch ops, transactions
P3-02: audit-log.entity + service, manual logging in controller (startSession, submitAnswer, deleteSession, review, forceEnd) P3-03: POST batch-delete, POST batch-export endpoints + service methods P3-04: DataSource.transaction for deleteSession + reviewAssessment, graph state cleanup on session delete
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AssessmentService } from './assessment.service';
|
||||
import { ExportService } from './services/export.service';
|
||||
import { AuditLogService } from './services/audit-log.service';
|
||||
import { CombinedAuthGuard } from '../auth/combined-auth.guard';
|
||||
import { Public } from '../auth/public.decorator';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
@@ -28,6 +29,7 @@ export class AssessmentController {
|
||||
constructor(
|
||||
private readonly assessmentService: AssessmentService,
|
||||
private readonly exportService: ExportService,
|
||||
private readonly auditLog: AuditLogService,
|
||||
) {}
|
||||
|
||||
@Post('start')
|
||||
@@ -41,13 +43,15 @@ export class AssessmentController {
|
||||
console.log(
|
||||
`[AssessmentController] startSession: user=${userId}, tenant=${tenantId}, templateId=${body.templateId}, kbId=${body.knowledgeBaseId}`,
|
||||
);
|
||||
return this.assessmentService.startSession(
|
||||
const session = await this.assessmentService.startSession(
|
||||
userId,
|
||||
body.knowledgeBaseId,
|
||||
tenantId,
|
||||
body.language,
|
||||
body.templateId,
|
||||
);
|
||||
this.auditLog.log({ userId, tenantId, action: 'session.start', resourceType: 'assessment_session', resourceId: session.id });
|
||||
return session;
|
||||
}
|
||||
|
||||
@Post(':id/answer')
|
||||
@@ -61,12 +65,14 @@ export class AssessmentController {
|
||||
console.log(
|
||||
`[AssessmentController] >>> submitAnswer CALLED: user=${userId}, session=${sessionId}, answerLen=${body.answer?.length}`,
|
||||
);
|
||||
return this.assessmentService.submitAnswer(
|
||||
const result = await this.assessmentService.submitAnswer(
|
||||
sessionId,
|
||||
userId,
|
||||
body.answer,
|
||||
body.language,
|
||||
);
|
||||
this.auditLog.log({ userId, action: 'session.answer', resourceType: 'assessment_session', resourceId: sessionId, details: { answerLength: body.answer?.length } });
|
||||
return result;
|
||||
}
|
||||
|
||||
@Sse(':id/start-stream')
|
||||
@@ -117,7 +123,9 @@ export class AssessmentController {
|
||||
console.log(
|
||||
`[AssessmentController] deleteSession: user=${user.id}, role=${user.role}, session=${sessionId}`,
|
||||
);
|
||||
return this.assessmentService.deleteSession(sessionId, user);
|
||||
await this.assessmentService.deleteSession(sessionId, user);
|
||||
this.auditLog.log({ userId: user.id, tenantId: user.tenantId, action: 'session.delete', resourceType: 'assessment_session', resourceId: sessionId });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@Get(':id/certificate')
|
||||
@@ -216,6 +224,26 @@ export class AssessmentController {
|
||||
);
|
||||
}
|
||||
|
||||
@Post('batch-delete')
|
||||
@ApiOperation({ summary: 'Batch delete assessment sessions (admin only)' })
|
||||
async batchDelete(@Request() req: any, @Body() body: { ids: string[] }) {
|
||||
const user = req.user;
|
||||
const isAdmin = user.role === 'super_admin' || user.role === 'admin';
|
||||
if (!isAdmin) {
|
||||
throw new ForbiddenException('Only admin can batch delete');
|
||||
}
|
||||
const count = await this.assessmentService.batchDeleteSessions(body.ids, user);
|
||||
this.auditLog.log({ userId: user.id, tenantId: user.tenantId, action: 'session.batch_delete', resourceType: 'assessment_session', details: { count, ids: body.ids } });
|
||||
return { deleted: count };
|
||||
}
|
||||
|
||||
@Post('batch-export')
|
||||
@ApiOperation({ summary: 'Batch export assessments as JSON array' })
|
||||
async batchExport(@Request() req: any, @Body() body: { ids: string[] }) {
|
||||
const { id: userId } = req.user;
|
||||
return this.assessmentService.batchExportSessions(body.ids, userId);
|
||||
}
|
||||
|
||||
@Put(':id/review')
|
||||
@ApiOperation({ summary: 'Review assessment - adjust final score' })
|
||||
async review(
|
||||
@@ -224,13 +252,15 @@ export class AssessmentController {
|
||||
@Req() req: any,
|
||||
) {
|
||||
const { id: userId, tenantId } = req.user;
|
||||
return this.assessmentService.reviewAssessment(
|
||||
const result = await this.assessmentService.reviewAssessment(
|
||||
sessionId,
|
||||
body.newScore,
|
||||
body.comment,
|
||||
userId,
|
||||
tenantId,
|
||||
);
|
||||
this.auditLog.log({ userId, tenantId, action: 'session.review', resourceType: 'assessment_session', resourceId: sessionId, details: { newScore: body.newScore, comment: body.comment } });
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get(':id/time-check')
|
||||
@@ -252,12 +282,14 @@ export class AssessmentController {
|
||||
@Param('id') sessionId: string,
|
||||
@Request() req: any,
|
||||
) {
|
||||
const { role } = req.user;
|
||||
const { id: userId, tenantId, role } = req.user;
|
||||
const isAdmin = role === 'super_admin' || role === 'admin';
|
||||
if (!isAdmin) {
|
||||
throw new ForbiddenException('Only admin can force end assessment');
|
||||
}
|
||||
return this.assessmentService.forceEndAssessment(sessionId);
|
||||
const result = await this.assessmentService.forceEndAssessment(sessionId);
|
||||
this.auditLog.log({ userId, tenantId, action: 'session.force_end', resourceType: 'assessment_session', resourceId: sessionId });
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get(':id/export/excel')
|
||||
|
||||
Reference in New Issue
Block a user