import { http } from './index'; // ==================== 类型定义 ==================== export interface TeacherQueryParams { pageNum?: number; pageSize?: number; keyword?: string; status?: string; } export interface Teacher { id: number; name: string; phone: string; email?: string; loginAccount: string; status: string; classIds: number[]; classNames?: string | string[]; lessonCount?: number; createdAt: string; } export interface CreateTeacherDto { name: string; phone: string; email?: string; loginAccount: string; password?: string; classIds?: number[]; } export interface StudentQueryParams { pageNum?: number; pageSize?: number; classId?: number; keyword?: string; } export interface Student { id: number; name: string; gender?: string; birthDate?: string; classId: number; className?: string; parentName?: string; parentPhone?: string; lessonCount?: number; readingCount?: number; avgScore?: number; createdAt: string; } export interface CreateStudentDto { name: string; gender?: string; birthDate?: string; classId: number; parentName?: string; parentPhone?: string; } export interface ClassInfo { id: number; name: string; grade: string; teacherId?: number; teacherName?: string; studentCount: number; lessonCount: number; createdAt?: string; teachers?: ClassTeacher[]; // 新增:教师团队 } export interface ClassTeacher { id: number; teacherId: number; teacherName: string; teacherPhone?: string; teacherEmail?: string; role: 'MAIN' | 'ASSIST' | 'CARE'; isPrimary: boolean; createdAt?: string; } export interface AddClassTeacherDto { teacherId: number; role: 'MAIN' | 'ASSIST' | 'CARE'; isPrimary?: boolean; } export interface UpdateClassTeacherDto { role?: 'MAIN' | 'ASSIST' | 'CARE'; isPrimary?: boolean; } export interface TransferStudentDto { toClassId: number; reason?: string; } export interface StudentClassHistory { id: number; fromClass: { id: number; name: string; grade: string } | null; toClass: { id: number; name: string; grade: string }; reason?: string; operatedBy?: number; createdAt: string; } export interface CreateClassDto { name: string; grade: string; teacherId?: number; } export interface SchoolStats { teacherCount: number; studentCount: number; classCount: number; lessonCount: number; } export interface PackageInfo { packageType: string; teacherQuota: number; studentQuota: number; storageQuota: number; teacherCount: number; studentCount: number; storageUsed: number; startDate: string; expireDate: string; status: string; } export interface PackageUsage { teacher: { used: number; quota: number; percentage: number; }; student: { used: number; quota: number; percentage: number; }; storage: { used: number; quota: number; percentage: number; }; } // ==================== 教师管理 ==================== export const getTeachers = (params: TeacherQueryParams) => http.get<{ list: Teacher[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/teachers', { params }); export const getTeacher = (id: number) => http.get(`/v1/school/teachers/${id}`); export const createTeacher = (data: CreateTeacherDto) => http.post('/v1/school/teachers', data); export const updateTeacher = (id: number, data: Partial) => http.put(`/v1/school/teachers/${id}`, data); export const deleteTeacher = (id: number) => http.delete(`/v1/school/teachers/${id}`); export const resetTeacherPassword = (id: number) => http.post<{ tempPassword: string }>(`/v1/school/teachers/${id}/reset-password`); // ==================== 学生管理 ==================== export const getStudents = (params: StudentQueryParams) => http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/students', { params }); export const getStudent = (id: number) => http.get(`/v1/school/students/${id}`); export const createStudent = (data: CreateStudentDto) => http.post('/v1/school/students', data); export const updateStudent = (id: number, data: Partial) => http.put(`/v1/school/students/${id}`, data); export const deleteStudent = (id: number) => http.delete(`/v1/school/students/${id}`); // ==================== 学生批量导入 ==================== export interface ImportResult { success: number; failed: number; errors: Array<{ row: number; message: string }>; } export interface ImportTemplate { headers: string[]; example: string[]; notes: string[]; } export const getStudentImportTemplate = () => http.get('/v1/school/students/import/template'); export const importStudents = (file: File, defaultClassId?: number): Promise => { const formData = new FormData(); formData.append('file', file); const params = defaultClassId ? { defaultClassId } : {}; return http.post('/v1/school/students/import', formData, { headers: { 'Content-Type': 'multipart/form-data', }, params, }); }; // ==================== 班级管理 ==================== export const getClasses = () => http.get<{ list: ClassInfo[]; total: number }>('/v1/school/classes').then(res => res.list); export const getClass = (id: number) => http.get(`/v1/school/classes/${id}`); export const createClass = (data: CreateClassDto) => http.post('/v1/school/classes', data); export const updateClass = (id: number, data: Partial) => http.put(`/v1/school/classes/${id}`, data); export const deleteClass = (id: number) => http.delete(`/v1/school/classes/${id}`); export const getClassStudents = (classId: number, params?: { pageNum?: number; pageSize?: number; keyword?: string }) => http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number; class?: ClassInfo }>(`/v1/school/classes/${classId}/students`, { params }); // ==================== 统计数据 ==================== export const getSchoolStats = () => http.get('/v1/school/stats'); export const getActiveTeachers = (limit?: number) => http.get>('/v1/school/stats/teachers', { params: { limit } }); export const getCourseUsageStats = () => http.get>('/v1/school/stats/courses'); export const getRecentActivities = (limit?: number) => http.get>('/v1/school/stats/activities', { params: { limit } }); // ==================== 套餐信息(旧API,保留兼容) ==================== export const getPackageInfo = () => http.get('/v1/school/package'); export const getPackageUsage = () => http.get('/v1/school/package/usage'); // ==================== 套餐管理(新API) ==================== export interface TenantPackage { id: number; tenantId: number; packageId: number; startDate: string; endDate: string; status: 'ACTIVE' | 'EXPIRED' | 'CANCELLED'; pricePaid?: number; createdAt: string; package: { id: number; name: string; description?: string; price: number; discountPrice?: number; courseCount: number; gradeLevels: string; status: string; courses: Array<{ id: number; packageId: number; courseId: number; gradeLevel: string; course: { id: number; name: string; coverImagePath?: string; }; }>; }; } export interface RenewPackageDto { endDate: string; pricePaid?: number; } export const getTenantPackages = () => http.get('/v1/school/packages'); export const renewPackage = (packageId: number, data: RenewPackageDto) => http.post(`/v1/school/packages/${packageId}/renew`, data); // ==================== 系统设置 ==================== export interface SystemSettings { id: number; tenantId: number; schoolName?: string; schoolLogo?: string; address?: string; notifyOnLesson: boolean; notifyOnTask: boolean; notifyOnGrowth: boolean; createdAt: string; updatedAt: string; } export interface UpdateSettingsDto { schoolName?: string; schoolLogo?: string; address?: string; notifyOnLesson?: boolean; notifyOnTask?: boolean; notifyOnGrowth?: boolean; } export const getSettings = () => http.get('/v1/school/settings'); export const updateSettings = (data: UpdateSettingsDto) => http.put('/v1/school/settings', data); // ==================== 课程管理 ==================== export const getSchoolCourses = () => http.get('/v1/school/courses'); export const getSchoolCourse = (id: number) => http.get(`/v1/school/courses/${id}`); // ==================== 班级教师管理 ==================== export const getClassTeachers = (classId: number) => http.get(`/v1/school/classes/${classId}/teachers`); export const addClassTeacher = (classId: number, data: AddClassTeacherDto) => http.post(`/v1/school/classes/${classId}/teachers`, data); export const updateClassTeacher = (classId: number, teacherId: number, data: UpdateClassTeacherDto) => http.put(`/v1/school/classes/${classId}/teachers/${teacherId}`, data); export const removeClassTeacher = (classId: number, teacherId: number) => http.delete<{ message: string }>(`/v1/school/classes/${classId}/teachers/${teacherId}`); // ==================== 学生调班 ==================== export const transferStudent = (studentId: number, data: TransferStudentDto) => http.post<{ message: string }>(`/v1/school/students/${studentId}/transfer`, data); export const getStudentClassHistory = (studentId: number) => http.get(`/v1/school/students/${studentId}/history`); // ==================== 排课管理 ==================== export interface SchedulePlan { id: number; tenantId: number; classId: number; className: string; courseId: number; courseName: string; teacherId?: number; teacherName?: string; scheduledDate?: string; scheduledTime?: string; weekDay?: number; repeatType: 'NONE' | 'DAILY' | 'WEEKLY'; repeatEndDate?: string; source: 'SCHOOL' | 'TEACHER'; status: 'ACTIVE' | 'CANCELLED'; note?: string; createdBy: number; createdAt: string; updatedAt: string; } export interface CreateScheduleDto { classId: number; courseId: number; teacherId?: number; scheduledDate?: string; scheduledTime?: string; weekDay?: number; repeatType: 'NONE' | 'DAILY' | 'WEEKLY'; repeatEndDate?: string; note?: string; } export interface UpdateScheduleDto { teacherId?: number; scheduledDate?: string; scheduledTime?: string; weekDay?: number; repeatType?: 'NONE' | 'DAILY' | 'WEEKLY'; repeatEndDate?: string; note?: string; status?: string; } export interface ScheduleQueryParams { classId?: number; teacherId?: number; courseId?: number; startDate?: string; endDate?: string; status?: string; source?: string; pageNum?: number; pageSize?: number; } export interface TimetableItem { date: string; weekDay: number; schedules: SchedulePlan[]; } export interface TimetableQueryParams { startDate: string; endDate: string; classId?: number; teacherId?: number; } export const getSchedules = (params?: ScheduleQueryParams) => http.get<{ list: SchedulePlan[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/schedules', { params }); export const getSchedule = (id: number) => http.get(`/v1/school/schedules/${id}`); export const createSchedule = (data: CreateScheduleDto) => http.post('/v1/school/schedules', data); export const updateSchedule = (id: number, data: UpdateScheduleDto) => http.put(`/v1/school/schedules/${id}`, data); export const cancelSchedule = (id: number) => http.delete<{ message: string }>(`/v1/school/schedules/${id}`); export const getTimetable = (params: TimetableQueryParams) => http.get('/v1/school/schedules/timetable', { params }); export interface BatchScheduleItem { classId: number; courseId: number; teacherId?: number; scheduledDate: string; scheduledTime?: string; note?: string; } export interface BatchCreateResult { success: number; failed: number; results: SchedulePlan[]; errors: Array<{ index: number; message: string }>; } export const batchCreateSchedules = (schedules: BatchScheduleItem[]) => http.post('/v1/school/schedules/batch', { schedules }); // ==================== 趋势与分布统计 ==================== export interface LessonTrendItem { month: string; lessonCount: number; studentCount: number; } export interface CourseDistributionItem { name: string; value: number; } export const getLessonTrend = (months?: number) => http.get('/v1/school/stats/lesson-trend', { params: { months } }); export const getCourseDistribution = () => http.get('/v1/school/stats/course-distribution'); // ==================== 数据导出 ==================== export const exportLessons = (startDate?: string, endDate?: string) => { const params = new URLSearchParams(); if (startDate) params.append('startDate', startDate); if (endDate) params.append('endDate', endDate); const token = localStorage.getItem('token'); const url = `/api/v1/school/export/lessons?${params.toString()}`; return fetch(url, { headers: { Authorization: `Bearer ${token}`, }, }).then((res) => { if (!res.ok) throw new Error('导出失败'); return res.blob(); }).then((blob) => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `授课记录_${startDate || ''}_${endDate || ''}.xlsx`; a.click(); window.URL.revokeObjectURL(url); }); }; export const exportTeacherStats = (startDate?: string, endDate?: string) => { const params = new URLSearchParams(); if (startDate) params.append('startDate', startDate); if (endDate) params.append('endDate', endDate); const token = localStorage.getItem('token'); const url = `/api/v1/school/export/teacher-stats?${params.toString()}`; return fetch(url, { headers: { Authorization: `Bearer ${token}`, }, }).then((res) => { if (!res.ok) throw new Error('导出失败'); return res.blob(); }).then((blob) => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `教师绩效统计_${startDate || ''}_${endDate || ''}.xlsx`; a.click(); window.URL.revokeObjectURL(url); }); }; export const exportStudentStats = (classId?: number) => { const params = new URLSearchParams(); if (classId) params.append('classId', String(classId)); const token = localStorage.getItem('token'); const url = `/api/v1/school/export/student-stats?${params.toString()}`; return fetch(url, { headers: { Authorization: `Bearer ${token}`, }, }).then((res) => { if (!res.ok) throw new Error('导出失败'); return res.blob(); }).then((blob) => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `学生统计.xlsx`; a.click(); window.URL.revokeObjectURL(url); }); }; // ==================== 排课模板 ==================== export interface ScheduleTemplate { id: number; tenantId: number; name: string; courseId: number; courseName?: string; classId?: number; className?: string; teacherId?: number; teacherName?: string; scheduledTime?: string; weekDay?: number; duration: number; isDefault: boolean; createdAt: string; updatedAt: string; } export interface CreateScheduleTemplateDto { name: string; courseId: number; classId?: number; teacherId?: number; scheduledTime?: string; weekDay?: number; duration?: number; isDefault?: boolean; } export interface UpdateScheduleTemplateDto { name?: string; classId?: number; teacherId?: number; scheduledTime?: string; weekDay?: number; duration?: number; isDefault?: boolean; } export interface ApplyTemplateDto { scheduledDate: string; classId?: number; teacherId?: number; } export const getScheduleTemplates = (params?: { classId?: number; courseId?: number }) => http.get('/v1/school/schedule-templates', { params }); export const getScheduleTemplate = (id: number) => http.get(`/v1/school/schedule-templates/${id}`); export const createScheduleTemplate = (data: CreateScheduleTemplateDto) => http.post('/v1/school/schedule-templates', data); export const updateScheduleTemplate = (id: number, data: UpdateScheduleTemplateDto) => http.put(`/v1/school/schedule-templates/${id}`, data); export const deleteScheduleTemplate = (id: number) => http.delete<{ message: string }>(`/v1/school/schedule-templates/${id}`); export const applyScheduleTemplate = (id: number, data: ApplyTemplateDto) => http.post(`/v1/school/schedule-templates/${id}/apply`, data); // ==================== 操作日志 ==================== export interface OperationLog { id: number; tenantId: number; userId: number; userType: string; action: string; module: string; description: string; targetId: number | null; oldValue: string | null; newValue: string | null; ipAddress: string | null; createdAt: string; } export interface OperationLogStats { total: number; modules: { name: string; count: number }[]; actions: { name: string; count: number }[]; } export const getOperationLogs = (params?: { pageNum?: number; pageSize?: number; module?: string; action?: string; startDate?: string; endDate?: string; }) => http.get<{ records: OperationLog[]; total: number; pageNum: number; pageSize: number; pages: number }>( '/v1/school/operation-logs', { params } ).then(res => ({ list: res.records || [], total: res.total || 0, pageNum: res.pageNum || 1, pageSize: res.pageSize || 10, pages: res.pages || 0, })); export const getOperationLogStats = (startDate?: string, endDate?: string) => http.get<{ totalLogs: number; byModule: Record; byOperator: Record }>('/v1/school/operation-logs/stats', { params: { startDate, endDate }, }).then(res => ({ totalLogs: res.totalLogs || 0, byModule: res.byModule || {}, byOperator: res.byOperator || {}, })); export const getOperationLogById = (id: number) => http.get(`/v1/school/operation-logs/${id}`); // ==================== 任务模板 API ==================== export interface TaskTemplate { id: number; tenantId: number; name: string; description?: string; taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK'; relatedCourseId?: number; defaultDuration: number; isDefault: boolean; status: string; createdBy: number; createdAt: string; updatedAt: string; course?: { id: number; name: string; pictureBookName?: string; }; } export interface CreateTaskTemplateDto { name: string; description?: string; taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK'; relatedCourseId?: number; defaultDuration?: number; isDefault?: boolean; } export interface UpdateTaskTemplateDto { name?: string; description?: string; relatedCourseId?: number; defaultDuration?: number; isDefault?: boolean; status?: string; } export const getTaskTemplates = (params?: { pageNum?: number; pageSize?: number; taskType?: string; keyword?: string; }) => http.get<{ list: TaskTemplate[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/task-templates', { params }) .then(res => ({ list: res.list || res.records || [], total: res.total || 0, pageNum: res.pageNum || 1, pageSize: res.pageSize || 10, pages: res.pages || 0, })); export const getTaskTemplate = (id: number) => http.get(`/v1/school/task-templates/${id}`); export const getDefaultTaskTemplate = (taskType: string) => http.get(`/v1/school/task-templates/default/${taskType}`); export const createTaskTemplate = (data: CreateTaskTemplateDto) => http.post('/v1/school/task-templates', data); export const updateTaskTemplate = (id: number, data: UpdateTaskTemplateDto) => http.put(`/v1/school/task-templates/${id}`, data); export const deleteTaskTemplate = (id: number) => http.delete<{ message: string }>(`/v1/school/task-templates/${id}`); // ==================== 任务统计 API ==================== export interface TaskStats { totalTasks: number; publishedTasks: number; completedTasks: number; inProgressTasks: number; pendingCount: number; totalCompletions: number; completionRate: number; } export interface TaskStatsByType { [key: string]: { total: number; completed: number; rate: number; }; } export interface TaskStatsByClass { classId: number; className: string; grade: string; total: number; completed: number; rate: number; } export interface MonthlyTaskStats { month: string; tasks: number; completions: number; completed: number; rate: number; } // 后端没有任务统计接口,返回空数据 export const getTaskStats = () => Promise.resolve({ totalTasks: 0, publishedTasks: 0, completedTasks: 0, inProgressTasks: 0, pendingCount: 0, totalCompletions: 0, completionRate: 0, }); export const getTaskStatsByType = () => Promise.resolve({}); export const getTaskStatsByClass = () => Promise.resolve([]); export const getMonthlyTaskStats = (_months?: number) => Promise.resolve([]); // ==================== 任务管理 API ==================== export interface SchoolTask { id: number; tenantId: number; title: string; description?: string; taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK'; targetType: 'CLASS' | 'STUDENT'; relatedCourseId?: number; course?: { id: number; name: string; }; startDate: string; endDate: string; status: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED'; createdBy: number; targetCount?: number; completionCount?: number; createdAt: string; updatedAt: string; } export interface TaskCompletion { id: number; taskId: number; studentId: number; studentName: string; className: string; status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED'; completedAt?: string; parentFeedback?: string; rating?: number; } export interface CreateSchoolTaskDto { title: string; description?: string; taskType: 'READING' | 'ACTIVITY' | 'HOMEWORK'; targetType: 'CLASS' | 'STUDENT'; targetIds: number[]; relatedCourseId?: number; startDate: string; endDate: string; } export interface UpdateSchoolTaskDto { title?: string; description?: string; taskType?: 'READING' | 'ACTIVITY' | 'HOMEWORK'; relatedCourseId?: number; startDate?: string; endDate?: string; status?: 'DRAFT' | 'PUBLISHED' | 'ARCHIVED'; } export const getSchoolTasks = (params?: { pageNum?: number; pageSize?: number; status?: string; taskType?: string; keyword?: string; }) => http.get<{ list: SchoolTask[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/tasks', { params }); export const getSchoolTask = (id: number) => http.get(`/v1/school/tasks/${id}`); export const createSchoolTask = (data: CreateSchoolTaskDto) => http.post('/v1/school/tasks', data); export const updateSchoolTask = (id: number, data: UpdateSchoolTaskDto) => http.put(`/v1/school/tasks/${id}`, data); export const deleteSchoolTask = (id: number) => http.delete<{ message: string }>(`/v1/school/tasks/${id}`); export const getSchoolTaskCompletions = (taskId: number) => http.get(`/v1/school/tasks/${taskId}/completions`); export const getSchoolClasses = () => http.get('/v1/school/classes'); // ==================== 数据报告 API ==================== export interface ReportOverview { totalLessons: number; activeTeacherCount: number; usedCourseCount: number; avgRating: number; } export interface TeacherReport { id: number; name: string; lessonCount: number; courseCount: number; feedbackCount: number; avgRating: number; } export interface CourseReport { id: number; name: string; lessonCount: number; teacherCount: number; studentCount: number; avgRating: number; } export interface StudentReport { id: number; name: string; className: string; lessonCount: number; avgFocus: number; avgParticipation: number; } export const getReportOverview = () => http.get('/v1/school/reports/overview'); export const getTeacherReports = () => http.get('/v1/school/reports/teachers'); export const getCourseReports = () => http.get('/v1/school/reports/courses'); export const getStudentReports = () => http.get('/v1/school/reports/students'); // ==================== 家长管理 ==================== export interface ParentQueryParams { pageNum?: number; pageSize?: number; keyword?: string; status?: string; } export interface ParentChild { id: number; name: string; relationship: string; class?: { id: number; name: string; }; } export interface Parent { id: number; name: string; phone: string; email?: string; loginAccount: string; status: string; tenantId: number; childrenCount: number; children?: ParentChild[]; createdAt: string; } export interface CreateParentDto { name: string; phone: string; email?: string; loginAccount: string; password?: string; } export interface UpdateParentDto { name?: string; phone?: string; email?: string; } export interface AddChildDto { studentId: number; relationship: string; } export const getParents = (params?: ParentQueryParams) => http.get<{ list: Parent[]; total: number; pageNum: number; pageSize: number; pages: number }>('/v1/school/parents', { params }); export const getParent = (id: number) => http.get(`/v1/school/parents/${id}`); export const createParent = (data: CreateParentDto) => http.post('/v1/school/parents', data); export const updateParent = (id: number, data: UpdateParentDto) => http.put(`/v1/school/parents/${id}`, data); export const deleteParent = (id: number) => http.delete<{ message: string }>(`/v1/school/parents/${id}`); export const resetParentPassword = (id: number) => http.post<{ tempPassword: string }>(`/v1/school/parents/${id}/reset-password`); export const getParentChildren = async (parentId: number): Promise => { const parent = await http.get(`/v1/school/parents/${parentId}`); return parent.children || []; }; export const addChildToParent = (parentId: number, data: AddChildDto) => http.post(`/v1/school/parents/${parentId}/children/${data.studentId}`, { relationship: data.relationship }); export const removeChildFromParent = (parentId: number, studentId: number) => http.delete<{ message: string }>(`/v1/school/parents/${parentId}/children/${studentId}`);