363 lines
9.5 KiB
TypeScript
363 lines
9.5 KiB
TypeScript
// src/api/admin.ts
|
|
import { api } from './apiClient';
|
|
|
|
export type PracticeLevel = 'keyword' | 'basic' | 'intermediate' | 'advanced';
|
|
export interface User {
|
|
_id: string;
|
|
username: string;
|
|
email: string;
|
|
fullname: string;
|
|
isAdmin: boolean;
|
|
createdAt: string;
|
|
status?: string;
|
|
}
|
|
export interface CodeExample {
|
|
_id: string;
|
|
title: string;
|
|
content: string;
|
|
level: PracticeLevel;
|
|
description?: string;
|
|
difficulty?: number;
|
|
createdAt?: Date;
|
|
updatedAt?: Date;
|
|
}
|
|
export interface UserUpdateData extends Partial<User> {
|
|
password?: string; // 添加可选的密码字段
|
|
}
|
|
export interface CreateUserData {
|
|
username: string;
|
|
email: string;
|
|
fullname: string;
|
|
password: string;
|
|
isAdmin?: boolean;
|
|
}
|
|
|
|
// 添加练习记录相关的接口
|
|
export interface PracticeRecord {
|
|
_id: string;
|
|
userId: string;
|
|
username: string;
|
|
fullname: string;
|
|
type: string;
|
|
stats: {
|
|
totalWords: number;
|
|
correctWords: number;
|
|
accuracy: number;
|
|
wordsPerMinute: number;
|
|
startTime: string;
|
|
endTime: string;
|
|
duration: number;
|
|
};
|
|
createdAt: string;
|
|
}
|
|
|
|
// 添加 OAuth2Client 接口
|
|
export interface OAuth2Client {
|
|
_id: string;
|
|
clientId: string;
|
|
clientSecret: string;
|
|
name: string;
|
|
redirectUris: string[];
|
|
grants: string[];
|
|
scope: string[];
|
|
createdAt: string;
|
|
updatedAt?: Date;
|
|
}
|
|
|
|
export interface CreateOAuth2ClientData {
|
|
name: string;
|
|
redirectUris: string[];
|
|
scope: string;
|
|
}
|
|
|
|
// 添加词汇相关接口
|
|
export interface WordSet {
|
|
_id: string;
|
|
name: string;
|
|
description?: string;
|
|
totalWords: number;
|
|
createdAt: string;
|
|
owner: {
|
|
_id: string;
|
|
username: string;
|
|
fullname: string;
|
|
};
|
|
}
|
|
|
|
export interface Word {
|
|
_id: string;
|
|
word: string;
|
|
translation: string;
|
|
pronunciation?: string;
|
|
example?: string;
|
|
wordSet: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface VocabularyPassSummaryItem {
|
|
userId: string;
|
|
username: string;
|
|
fullname: string;
|
|
email?: string;
|
|
passedWords: number;
|
|
uniqueWords: number;
|
|
testRecords: number;
|
|
totalRecords?: number;
|
|
validRecords?: number;
|
|
invalidatedRecords?: number;
|
|
wordSets: number;
|
|
testTypes: string[];
|
|
riskFlags?: string[];
|
|
firstPassedAt: string;
|
|
lastPassedAt: string;
|
|
firstRecordAt?: string;
|
|
lastRecordAt?: string;
|
|
}
|
|
|
|
export interface VocabularyPassSummaryResponse {
|
|
start: string;
|
|
end: string;
|
|
totalStudents: number;
|
|
totalPassedWords: number;
|
|
totalRecords?: number;
|
|
invalidatedRecords?: number;
|
|
items: VocabularyPassSummaryItem[];
|
|
}
|
|
|
|
export interface VocabularyAuditQueryParams {
|
|
start: string;
|
|
end: string;
|
|
studentNo?: string;
|
|
minUniqueWords?: string;
|
|
}
|
|
|
|
export interface ClearVocabularyPassSummaryResponse {
|
|
message: string;
|
|
userId?: string;
|
|
username?: string;
|
|
fullname?: string;
|
|
deletedRecords: number;
|
|
removedPassedWords: number;
|
|
affectedWords: number;
|
|
rebuiltWords: number;
|
|
}
|
|
|
|
export interface VocabularyAuditWordBrief {
|
|
_id: string;
|
|
word?: string;
|
|
translation?: string;
|
|
pronunciation?: string;
|
|
example?: string;
|
|
}
|
|
|
|
export interface VocabularyAuditUserBrief {
|
|
_id?: string;
|
|
userId?: string;
|
|
username?: string;
|
|
fullname?: string;
|
|
email?: string;
|
|
}
|
|
|
|
export interface VocabularyAuditInteractionSummary {
|
|
keyCount: number;
|
|
inputCount: number;
|
|
pasteCount: number;
|
|
focusCount: number;
|
|
blurCount: number;
|
|
pointerCount: number;
|
|
firstEventOffset: number;
|
|
lastEventOffset: number;
|
|
}
|
|
|
|
export interface VocabularyAuditResultDetail {
|
|
index: number;
|
|
wordId: string;
|
|
word: VocabularyAuditWordBrief | null;
|
|
userAnswer: string;
|
|
correctAnswer: string;
|
|
isCorrect: boolean;
|
|
}
|
|
|
|
export interface VocabularyAuditAnswerDetail extends VocabularyAuditResultDetail {
|
|
questionToken: string;
|
|
submittedAt?: string;
|
|
duration?: number;
|
|
riskFlags: string[];
|
|
interactionSummary?: VocabularyAuditInteractionSummary | null;
|
|
}
|
|
|
|
export interface VocabularyAuditAttemptDetail {
|
|
attemptId: string;
|
|
status: string;
|
|
testType: string;
|
|
wordSetId: string;
|
|
issuedAt?: string;
|
|
expiresAt?: string;
|
|
submittedAt?: string;
|
|
riskFlags: string[];
|
|
reviewDecision?: string;
|
|
reviewedAt?: string;
|
|
reviewedBy?: VocabularyAuditUserBrief | null;
|
|
reviewNote?: string;
|
|
questionWordIds: string[];
|
|
questionTokens: string[];
|
|
optionTokens: string[][];
|
|
optionTexts: string[][];
|
|
answeredQuestionTokens: string[];
|
|
answerDurations: number[];
|
|
answers: VocabularyAuditAnswerDetail[];
|
|
}
|
|
|
|
export interface VocabularyAuditRecordDetail {
|
|
recordId: string;
|
|
attemptId: string;
|
|
wordSet: {
|
|
_id: string;
|
|
name?: string;
|
|
description?: string;
|
|
} | null;
|
|
testType: string;
|
|
stats: {
|
|
totalWords: number;
|
|
correctWords: number;
|
|
accuracy: number;
|
|
startTime: string;
|
|
endTime: string;
|
|
duration: number;
|
|
};
|
|
invalidated: boolean;
|
|
riskFlags: string[];
|
|
reviewDecision?: string;
|
|
reviewedAt?: string;
|
|
reviewedBy?: VocabularyAuditUserBrief | null;
|
|
reviewNote?: string;
|
|
createdAt: string;
|
|
results: VocabularyAuditResultDetail[];
|
|
attempt: VocabularyAuditAttemptDetail | null;
|
|
}
|
|
|
|
export interface VocabularyPassSummaryDetailsResponse {
|
|
start: string;
|
|
end: string;
|
|
user: VocabularyAuditUserBrief;
|
|
totalRecords: number;
|
|
totalPassedWords: number;
|
|
invalidatedRecords: number;
|
|
approvedRecords: number;
|
|
records: VocabularyAuditRecordDetail[];
|
|
}
|
|
|
|
export interface ApproveVocabularyTestRecordResponse {
|
|
message: string;
|
|
recordId: string;
|
|
attemptId?: string;
|
|
totalWords?: number;
|
|
correctWords?: number;
|
|
accuracy?: number;
|
|
riskFlags?: string[];
|
|
affectedWords?: number;
|
|
rebuiltWords?: number;
|
|
alreadyEffective?: boolean;
|
|
}
|
|
|
|
export const adminApi = {
|
|
getUsers: async (): Promise<User[]> => {
|
|
return api.get<User[]>('/admin/users');
|
|
},
|
|
createUser: async (userData: CreateUserData): Promise<User> => {
|
|
return api.post<User>('/admin/users', userData);
|
|
},
|
|
updateUser: async (userId: string, userData: Partial<User>): Promise<User> => {
|
|
return api.put<User>(`/admin/users/${userId}`, userData);
|
|
},
|
|
deleteUser: async (userId: string): Promise<void> => {
|
|
await api.delete(`/admin/users/${userId}`);
|
|
},
|
|
// Code example methods
|
|
getCodeExamples: async (): Promise<CodeExample[]> => {
|
|
return api.get<CodeExample[]>('/code-examples');
|
|
},
|
|
|
|
createCodeExample: async (codeData: Omit<CodeExample, '_id'>): Promise<CodeExample> => {
|
|
return api.post<CodeExample>('/code-examples', codeData);
|
|
},
|
|
|
|
updateCodeExample: async (id: string, codeData: Partial<CodeExample>): Promise<CodeExample> => {
|
|
return api.put<CodeExample>(`/code-examples/${id}`, codeData);
|
|
},
|
|
|
|
deleteCodeExample: async (id: string): Promise<void> => {
|
|
await api.delete(`/code-examples/${id}`);
|
|
},
|
|
|
|
// 获取所有练习记录
|
|
getPracticeRecords: async (params?: { date?: string; userId?: string }): Promise<PracticeRecord[]> => {
|
|
return api.get<PracticeRecord[]>('/practice-records/all', { params });
|
|
},
|
|
|
|
// OAuth2 相关方法
|
|
getOAuth2Clients: async (): Promise<OAuth2Client[]> => {
|
|
return api.get<OAuth2Client[]>('/admin/oauth2/clients');
|
|
},
|
|
|
|
createOAuth2Client: async (data: CreateOAuth2ClientData): Promise<OAuth2Client> => {
|
|
return api.post<OAuth2Client>('/admin/oauth2/clients', data);
|
|
},
|
|
|
|
updateOAuth2Client: async (id: string, clientData: Partial<OAuth2Client>): Promise<OAuth2Client> => {
|
|
return api.put<OAuth2Client>(`/admin/oauth2/clients/${id}`, clientData);
|
|
},
|
|
|
|
deleteOAuth2Client: async (id: string): Promise<void> => {
|
|
await api.delete(`/admin/oauth2/clients/${id}`);
|
|
},
|
|
|
|
// 词汇管理相关方法
|
|
getVocabularyWordSets: async (): Promise<WordSet[]> => {
|
|
return api.get<WordSet[]>('/admin/vocabulary/word-sets');
|
|
},
|
|
|
|
getVocabularyWordSetDetails: async (id: string): Promise<{ wordSet: WordSet, words: Word[] }> => {
|
|
return api.get<{ wordSet: WordSet, words: Word[] }>(`/admin/vocabulary/word-sets/${id}/words`);
|
|
},
|
|
|
|
uploadVocabularyFile: async (formData: FormData): Promise<{ message: string, wordSet: WordSet }> => {
|
|
return api.post<{ message: string, wordSet: WordSet }>('/admin/vocabulary/upload', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
});
|
|
},
|
|
|
|
deleteVocabularyWordSet: async (id: string): Promise<{ message: string }> => {
|
|
return api.delete<{ message: string }>(`/admin/vocabulary/word-sets/${id}`);
|
|
},
|
|
|
|
// 获取单词集单词的方法
|
|
getWordsByWordSetId: async (wordSetId: string) => {
|
|
return api.get(`/vocabulary/word-set/${wordSetId}/words`);
|
|
},
|
|
|
|
// 更新单词的方法
|
|
updateWords: async (words: any[]) => {
|
|
return api.put('/api/vocabulary/words', { words });
|
|
},
|
|
|
|
getVocabularyPassSummary: async (params: VocabularyAuditQueryParams): Promise<VocabularyPassSummaryResponse> => {
|
|
return api.get<VocabularyPassSummaryResponse>('/admin/vocabulary/test-pass-summary', { params });
|
|
},
|
|
|
|
clearVocabularyPassSummary: async (data: { userId: string; start: string; end: string; studentNo?: string; minUniqueWords?: string }): Promise<ClearVocabularyPassSummaryResponse> => {
|
|
return api.post<ClearVocabularyPassSummaryResponse>('/admin/vocabulary/test-pass-summary/clear', data);
|
|
},
|
|
|
|
getVocabularyPassSummaryDetails: async (params: { userId: string } & VocabularyAuditQueryParams): Promise<VocabularyPassSummaryDetailsResponse> => {
|
|
const { userId, ...query } = params;
|
|
return api.get<VocabularyPassSummaryDetailsResponse>(`/admin/vocabulary/test-pass-summary/${userId}/details`, { params: query });
|
|
},
|
|
|
|
approveVocabularyTestRecord: async (recordId: string, data: { note?: string } = {}): Promise<ApproveVocabularyTestRecordResponse> => {
|
|
return api.post<ApproveVocabularyTestRecordResponse>(`/admin/vocabulary/test-records/${recordId}/approve`, data);
|
|
},
|
|
};
|