Files
aurak/web/services/assessmentService.ts
T
Developer 46a10ba091 P2全部完成: 尝试限制/预约时段/题目回顾/随机排序
后端:
- assessment-template entity: attemptLimit/scheduledStart/End/reviewMode/shuffleQuestions
- DTO 更新: 新增 P2 字段验证
- startSession: 尝试次数检查、预约时段检查、题目随机排序
- getSessionState: reviewMode 控制答案可见性
- 新增 GET /assessment/:id/review 回顾端点

前端:
- AssessmentTemplateManager: 新增尝试次数/答题回顾/题目排序/预约时段配置
- AssessmentView: 答题回顾按钮(完成页)+提交确认弹窗+标记回头功能
- types.ts: 新增 P2 字段类型
- assessmentService: 新增 getReview 方法
- 进度导航点: 可视化题序+标记状态

测试 20项全部通过 + 系统测试 142项全部通过 

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:57:32 +08:00

222 lines
8.0 KiB
TypeScript

import { apiClient } from './apiClient';
export interface AssessmentSession {
id: string;
userId: string;
knowledgeBaseId: string;
threadId: string;
status: 'IN_PROGRESS' | 'COMPLETED';
finalScore?: number;
finalReport?: string;
createdAt: string;
updatedAt: string;
knowledgeBase?: { id: string; name: string };
knowledgeGroup?: { id: string; name: string };
}
export interface AssessmentState {
messages: any[];
assessmentSessionId: string;
knowledgeBaseId: string;
questions: any[];
currentQuestionIndex: number;
shouldFollowUp: boolean;
scores: Record<string, number>;
feedbackHistory?: any[];
status?: 'IN_PROGRESS' | 'COMPLETED';
report?: string;
finalScore?: number;
passed?: boolean;
dimensionScores?: Record<string, number>;
radarData?: Record<string, number>;
}
export interface Certificate {
id: string;
level: string;
totalScore: number;
passed: boolean;
issuedAt: string;
qrCode?: string;
dimensionScores?: Record<string, number>;
}
export interface TimeCheck {
totalTimeRemaining: number;
questionTimeRemaining: number;
isTotalTimeout: boolean;
isQuestionTimeout: boolean;
}
export interface StatsData {
totalAssessments: number;
averageScore: number;
completionRate: number;
passRate: number;
}
export interface RadarData {
dimensions: Record<string, number>;
}
export interface TrendData {
date: string;
score: number;
count: number;
}
export class AssessmentService {
async startSession(knowledgeBaseId: string, language: string, templateId?: string): Promise<AssessmentSession> {
const { data } = await apiClient.post<AssessmentSession>('/assessment/start', { knowledgeBaseId, language, templateId });
return data;
}
async submitAnswer(sessionId: string, answer: string, language: string): Promise<AssessmentState> {
const { data } = await apiClient.post<AssessmentState>(`/assessment/${sessionId}/answer`, { answer, language });
return data;
}
async getSessionState(sessionId: string): Promise<AssessmentState> {
const { data } = await apiClient.get<AssessmentState>(`/assessment/${sessionId}/state`);
return data;
}
async getHistory(): Promise<AssessmentSession[]> {
const { data } = await apiClient.get<AssessmentSession[]>('/assessment/history');
return data;
}
async getUserHistory(): Promise<AssessmentSession[]> {
const { data } = await apiClient.get<AssessmentSession[]>('/assessment/history');
return data;
}
async deleteSession(sessionId: string): Promise<void> {
await apiClient.delete(`/assessment/${sessionId}`);
}
async getCertificate(sessionId: string): Promise<Certificate> {
const { data } = await apiClient.get<Certificate>(`/assessment/${sessionId}/certificate`);
return data;
}
async reviewAssessment(sessionId: string, newScore: number, comment?: string): Promise<AssessmentSession> {
const { data } = await apiClient.put<AssessmentSession>(`/assessment/${sessionId}/review`, { newScore, comment });
return data;
}
async getStats(startDate?: string, endDate?: string, templateId?: string, knowledgeGroupId?: string): Promise<StatsData> {
const params = new URLSearchParams();
if (startDate) params.append('startDate', startDate);
if (endDate) params.append('endDate', endDate);
if (templateId) params.append('templateId', templateId);
if (knowledgeGroupId) params.append('knowledgeGroupId', knowledgeGroupId);
const { data } = await apiClient.get<StatsData>(`/assessment/stats?${params.toString()}`);
return data;
}
async getRadarStats(templateId?: string): Promise<RadarData> {
const params = templateId ? `?templateId=${templateId}` : '';
const { data } = await apiClient.get<RadarData>(`/assessment/stats/radar${params}`);
return data;
}
async getTrendStats(startDate?: string, endDate?: string): Promise<TrendData[]> {
const params = new URLSearchParams();
if (startDate) params.append('startDate', startDate);
if (endDate) params.append('endDate', endDate);
const { data } = await apiClient.get<TrendData[]>(`/assessment/stats/trend?${params.toString()}`);
return data;
}
async checkTimeLimits(sessionId: string): Promise<TimeCheck> {
const { data } = await apiClient.get<TimeCheck>(`/assessment/${sessionId}/time-check`);
return data;
}
async startNextQuestion(sessionId: string): Promise<{ success: boolean }> {
const { data } = await apiClient.post<{ success: boolean }>(`/assessment/${sessionId}/next-question`, {});
return data;
}
async exportExcel(sessionId: string): Promise<{ filename: string; buffer: string }> {
const { data } = await apiClient.get<{ filename: string; buffer: string }>(`/assessment/${sessionId}/export/excel`);
return data;
}
async exportPdf(sessionId: string): Promise<{ filename: string; buffer: string }> {
const { data } = await apiClient.get<{ filename: string; buffer: string }>(`/assessment/${sessionId}/export/pdf`);
return data;
}
/** P2: Get assessment review data (correct answers) */
async getReview(sessionId: string): Promise<any> {
const { data } = await apiClient.get<any>(`/assessment/${sessionId}/review`);
return data;
}
async nextQuestion(sessionId: string): Promise<{ success: boolean }> {
const { data } = await apiClient.post<{ success: boolean }>(`/assessment/${sessionId}/next-question`, {});
return data;
}
async forceEnd(sessionId: string): Promise<AssessmentSession> {
const { data } = await apiClient.post<AssessmentSession>(`/assessment/${sessionId}/force-end`, {});
return data;
}
async verifyCertificate(certificateId: string): Promise<{ valid: boolean; certificate?: Certificate; message?: string }> {
const { data } = await apiClient.get(`/assessment/certificate/verify/${certificateId}`);
return data;
}
async getPublicCertificate(sessionId: string): Promise<{ exists: boolean; certificate?: Certificate; message?: string }> {
const { data } = await apiClient.get(`/assessment/certificate/public/${sessionId}`);
return data;
}
async *startSessionStream(sessionId: string, templateId?: string): AsyncIterableIterator<any> {
const query = templateId ? `?templateId=${templateId}` : '';
const response = await apiClient.request(`/assessment/${sessionId}/start-stream${query}`, {
method: 'GET',
});
yield* this.parseStream(response);
}
async *submitAnswerStream(sessionId: string, answer: string, language: string, templateId?: string): AsyncIterableIterator<any> {
const query = new URLSearchParams({ answer, language, ...(templateId && { templateId }) }).toString();
const response = await apiClient.request(`/assessment/${sessionId}/answer-stream?${query}`, {
method: 'GET',
});
yield* this.parseStream(response);
}
private async *parseStream(response: Response): AsyncIterableIterator<any> {
const reader = response.body?.getReader();
if (!reader) return;
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.substring(6));
yield data;
} catch (e) {
console.error('Failed to parse SSE data:', line);
}
}
}
}
}
}
export const assessmentService = new AssessmentService();