kindergarten_java/reading-platform-frontend/src/api/school.ts
Claude Opus 4.6 56508eb066 feat: 三端全链路测试完成 - 学校端、教师端、课程包业务
## 后端修复
- 修复教师端课程查询 - 包含系统课程和租户课程
- 修复系统课程创建 - isSystem 标志正确保存到数据库
- 新增套餐授权接口 POST /api/v1/admin/packages/{id}/grant

## 新增 Controller
- SchoolStatsController - 学校端统计数据
- SchoolCourseController - 学校端课程管理
- TeacherStatsController - 教师端统计数据

## 前端修复
- 修复 API 客户端导入 - getApi → getReadingPlatformAPI
- 修复三端 API 调用方法名
- 更新 Orval 生成配置和 API 类型
- 修复学校端视图 - result.items → result.list

## 测试结果
-  超管端:课程创建/发布、套餐完整流程、授权
-  学校端:登录、统计、课程、套餐查看
-  教师端:登录、Dashboard、班级、课程查看

## 文档更新
- 新增测试记录:/docs/test-logs/
- 更新 CHANGELOG.md
- 更新今日开发日志

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:48:28 +08:00

1004 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { http } from './index';
// ==================== 类型定义 ====================
export interface TeacherQueryParams {
page?: 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 {
page?: 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 }>('/school/teachers', { params });
export const getTeacher = (id: number) =>
http.get<Teacher>(`/school/teachers/${id}`);
export const createTeacher = (data: CreateTeacherDto) =>
http.post<Teacher>('/school/teachers', data);
export const updateTeacher = (id: number, data: Partial<CreateTeacherDto>) =>
http.put<Teacher>(`/school/teachers/${id}`, data);
export const deleteTeacher = (id: number) =>
http.delete(`/school/teachers/${id}`);
export const resetTeacherPassword = (id: number) =>
http.post<{ tempPassword: string }>(`/school/teachers/${id}/reset-password`);
// ==================== 学生管理 ====================
export const getStudents = (params: StudentQueryParams) =>
http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number }>('/school/students', { params });
export const getStudent = (id: number) =>
http.get<Student>(`/school/students/${id}`);
export const createStudent = (data: CreateStudentDto) =>
http.post<Student>('/school/students', data);
export const updateStudent = (id: number, data: Partial<CreateStudentDto>) =>
http.put<Student>(`/school/students/${id}`, data);
export const deleteStudent = (id: number) =>
http.delete(`/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<ImportTemplate>('/school/students/import/template');
export const importStudents = (file: File, defaultClassId?: number): Promise<ImportResult> => {
const formData = new FormData();
formData.append('file', file);
const params = defaultClassId ? { defaultClassId } : {};
return http.post('/school/students/import', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
params,
});
};
// ==================== 班级管理 ====================
export const getClasses = () =>
http.get<{ list: ClassInfo[]; total: number }>('/school/classes').then(res => res.list);
export const getClass = (id: number) =>
http.get<ClassInfo>(`/school/classes/${id}`);
export const createClass = (data: CreateClassDto) =>
http.post<ClassInfo>('/school/classes', data);
export const updateClass = (id: number, data: Partial<CreateClassDto>) =>
http.put<ClassInfo>(`/school/classes/${id}`, data);
export const deleteClass = (id: number) =>
http.delete(`/school/classes/${id}`);
export const getClassStudents = (classId: number, params?: { page?: number; pageSize?: number; keyword?: string }) =>
http.get<{ list: Student[]; total: number; pageNum: number; pageSize: number; pages: number; class?: ClassInfo }>(`/school/classes/${classId}/students`, { params });
// ==================== 统计数据 ====================
export const getSchoolStats = () =>
http.get<SchoolStats>('/school/stats');
export const getActiveTeachers = (limit?: number) =>
http.get<Array<{ id: number; name: string; lessonCount: number }>>('/school/stats/teachers', { params: { limit } });
export const getCourseUsageStats = () =>
http.get<Array<{ courseId: number; courseName: string; usageCount: number }>>('/school/stats/courses');
export const getRecentActivities = (limit?: number) =>
http.get<Array<{ id: number; type: string; title: string; time: string }>>('/school/stats/activities', { params: { limit } });
// ==================== 套餐信息旧API保留兼容 ====================
export const getPackageInfo = () =>
http.get<PackageInfo>('/school/package');
export const getPackageUsage = () =>
http.get<PackageUsage>('/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<TenantPackage[]>('/school/packages');
export const renewPackage = (packageId: number, data: RenewPackageDto) =>
http.post<TenantPackage>(`/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<SystemSettings>('/school/settings');
export const updateSettings = (data: UpdateSettingsDto) =>
http.put<SystemSettings>('/school/settings', data);
// ==================== 课程管理 ====================
export const getSchoolCourses = () =>
http.get<any[]>('/school/courses');
export const getSchoolCourse = (id: number) =>
http.get<any>(`/school/courses/${id}`);
// ==================== 班级教师管理 ====================
export const getClassTeachers = (classId: number) =>
http.get<ClassTeacher[]>(`/school/classes/${classId}/teachers`);
export const addClassTeacher = (classId: number, data: AddClassTeacherDto) =>
http.post<ClassTeacher>(`/school/classes/${classId}/teachers`, data);
export const updateClassTeacher = (classId: number, teacherId: number, data: UpdateClassTeacherDto) =>
http.put<ClassTeacher>(`/school/classes/${classId}/teachers/${teacherId}`, data);
export const removeClassTeacher = (classId: number, teacherId: number) =>
http.delete<{ message: string }>(`/school/classes/${classId}/teachers/${teacherId}`);
// ==================== 学生调班 ====================
export const transferStudent = (studentId: number, data: TransferStudentDto) =>
http.post<{ message: string }>(`/school/students/${studentId}/transfer`, data);
export const getStudentClassHistory = (studentId: number) =>
http.get<StudentClassHistory[]>(`/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;
page?: 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 }>('/school/schedules', { params });
export const getSchedule = (id: number) =>
http.get<SchedulePlan>(`/school/schedules/${id}`);
export const createSchedule = (data: CreateScheduleDto) =>
http.post<SchedulePlan>('/school/schedules', data);
export const updateSchedule = (id: number, data: UpdateScheduleDto) =>
http.put<SchedulePlan>(`/school/schedules/${id}`, data);
export const cancelSchedule = (id: number) =>
http.delete<{ message: string }>(`/school/schedules/${id}`);
export const getTimetable = (params: TimetableQueryParams) =>
http.get<TimetableItem[]>('/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<BatchCreateResult>('/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<LessonTrendItem[]>('/school/stats/lesson-trend', { params: { months } });
export const getCourseDistribution = () =>
http.get<CourseDistributionItem[]>('/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<ScheduleTemplate[]>('/school/schedule-templates', { params });
export const getScheduleTemplate = (id: number) =>
http.get<ScheduleTemplate>(`/school/schedule-templates/${id}`);
export const createScheduleTemplate = (data: CreateScheduleTemplateDto) =>
http.post<ScheduleTemplate>('/school/schedule-templates', data);
export const updateScheduleTemplate = (id: number, data: UpdateScheduleTemplateDto) =>
http.put<ScheduleTemplate>(`/school/schedule-templates/${id}`, data);
export const deleteScheduleTemplate = (id: number) =>
http.delete<{ message: string }>(`/school/schedule-templates/${id}`);
export const applyScheduleTemplate = (id: number, data: ApplyTemplateDto) =>
http.post<SchedulePlan>(`/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?: {
page?: number;
pageSize?: number;
module?: string;
action?: string;
startDate?: string;
endDate?: string;
}) => http.get<{ list: OperationLog[]; total: number; pageNum: number; pageSize: number; pages: number }>(
'/school/operation-logs',
{ params }
);
export const getOperationLogStats = (startDate?: string, endDate?: string) =>
http.get<OperationLogStats>('/school/operation-logs/stats', {
params: { startDate, endDate },
});
export const getOperationLogById = (id: number) =>
http.get<OperationLog>(`/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?: {
page?: number;
pageSize?: number;
taskType?: string;
keyword?: string;
}) => http.get<{ list: TaskTemplate[]; total: number; pageNum: number; pageSize: number; pages: number }>('/school/task-templates', { params });
export const getTaskTemplate = (id: number) =>
http.get<TaskTemplate>(`/school/task-templates/${id}`);
export const getDefaultTaskTemplate = (taskType: string) =>
http.get<TaskTemplate | null>(`/school/task-templates/default/${taskType}`);
export const createTaskTemplate = (data: CreateTaskTemplateDto) =>
http.post<TaskTemplate>('/school/task-templates', data);
export const updateTaskTemplate = (id: number, data: UpdateTaskTemplateDto) =>
http.put<TaskTemplate>(`/school/task-templates/${id}`, data);
export const deleteTaskTemplate = (id: number) =>
http.delete<{ message: string }>(`/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 = () =>
http.get<TaskStats>('/school/tasks/stats');
export const getTaskStatsByType = () =>
http.get<TaskStatsByType>('/school/tasks/stats/by-type');
export const getTaskStatsByClass = () =>
http.get<TaskStatsByClass[]>('/school/tasks/stats/by-class');
export const getMonthlyTaskStats = (months?: number) =>
http.get<MonthlyTaskStats[]>('/school/tasks/stats/monthly', { params: { months } });
// ==================== 任务管理 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?: {
page?: number;
pageSize?: number;
status?: string;
taskType?: string;
keyword?: string;
}) => http.get<{ list: SchoolTask[]; total: number; pageNum: number; pageSize: number; pages: number }>('/school/tasks', { params });
export const getSchoolTask = (id: number) =>
http.get<SchoolTask>(`/school/tasks/${id}`);
export const createSchoolTask = (data: CreateSchoolTaskDto) =>
http.post<SchoolTask>('/school/tasks', data);
export const updateSchoolTask = (id: number, data: UpdateSchoolTaskDto) =>
http.put<SchoolTask>(`/school/tasks/${id}`, data);
export const deleteSchoolTask = (id: number) =>
http.delete<{ message: string }>(`/school/tasks/${id}`);
export const getSchoolTaskCompletions = (taskId: number) =>
http.get<TaskCompletion[]>(`/school/tasks/${taskId}/completions`);
export const getSchoolClasses = () =>
http.get<ClassInfo[]>('/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<ReportOverview>('/school/reports/overview');
export const getTeacherReports = () =>
http.get<TeacherReport[]>('/school/reports/teachers');
export const getCourseReports = () =>
http.get<CourseReport[]>('/school/reports/courses');
export const getStudentReports = () =>
http.get<StudentReport[]>('/school/reports/students');
// ==================== 家长管理 ====================
export interface ParentQueryParams {
page?: 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 }>('/school/parents', { params });
export const getParent = (id: number) =>
http.get<Parent>(`/school/parents/${id}`);
export const createParent = (data: CreateParentDto) =>
http.post<Parent>('/school/parents', data);
export const updateParent = (id: number, data: UpdateParentDto) =>
http.put<Parent>(`/school/parents/${id}`, data);
export const deleteParent = (id: number) =>
http.delete<{ message: string }>(`/school/parents/${id}`);
export const resetParentPassword = (id: number) =>
http.post<{ tempPassword: string }>(`/school/parents/${id}/reset-password`);
export const getParentChildren = async (parentId: number): Promise<ParentChild[]> => {
const parent = await http.get<Parent & { children: ParentChild[] }>(`/school/parents/${parentId}`);
return parent.children || [];
};
export const addChildToParent = (parentId: number, data: AddChildDto) =>
http.post<ParentChild>(`/school/parents/${parentId}/children/${data.studentId}`, { relationship: data.relationship });
export const removeChildFromParent = (parentId: number, studentId: number) =>
http.delete<{ message: string }>(`/school/parents/${parentId}/children/${studentId}`);