Merge remote-tracking branch 'origin/master'
# Conflicts: # reading-platform-frontend/openapi.json # reading-platform-frontend/src/api/generated/index.ts # reading-platform-frontend/src/api/generated/model/index.ts # reading-platform-frontend/src/api/generated/model/taskCreateRequest.ts
This commit is contained in:
commit
6e1758a44d
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import type {
|
import type {
|
||||||
BasicSettingsUpdateRequest,
|
BasicSettingsUpdateRequest,
|
||||||
|
BatchStudentRecordsRequest,
|
||||||
BindStudentParams,
|
BindStudentParams,
|
||||||
ChangePasswordParams,
|
ChangePasswordParams,
|
||||||
CheckConflictParams,
|
CheckConflictParams,
|
||||||
@ -32,6 +33,7 @@ import type {
|
|||||||
GetAllCoursesParams,
|
GetAllCoursesParams,
|
||||||
GetAllStudentsParams,
|
GetAllStudentsParams,
|
||||||
GetCalendarViewDataParams,
|
GetCalendarViewDataParams,
|
||||||
|
GetChildLessonsParams,
|
||||||
GetClassPageParams,
|
GetClassPageParams,
|
||||||
GetClassStudents1Params,
|
GetClassStudents1Params,
|
||||||
GetClassStudentsParams,
|
GetClassStudentsParams,
|
||||||
@ -98,6 +100,7 @@ import type {
|
|||||||
StudentCreateRequest,
|
StudentCreateRequest,
|
||||||
StudentRecordRequest,
|
StudentRecordRequest,
|
||||||
StudentUpdateRequest,
|
StudentUpdateRequest,
|
||||||
|
TaskCompleteRequest,
|
||||||
TaskCreateRequest,
|
TaskCreateRequest,
|
||||||
TaskFeedbackRequest,
|
TaskFeedbackRequest,
|
||||||
TaskSubmitRequest,
|
TaskSubmitRequest,
|
||||||
@ -111,7 +114,6 @@ import type {
|
|||||||
UpdateBasicSettings1Body,
|
UpdateBasicSettings1Body,
|
||||||
UpdateClassTeacherBody,
|
UpdateClassTeacherBody,
|
||||||
UpdateNotificationSettings1Body,
|
UpdateNotificationSettings1Body,
|
||||||
UpdateProfileRequest,
|
|
||||||
UpdateSecuritySettings1Body,
|
UpdateSecuritySettings1Body,
|
||||||
UpdateSettings1Body,
|
UpdateSettings1Body,
|
||||||
UpdateStorageSettingsBody,
|
UpdateStorageSettingsBody,
|
||||||
@ -853,76 +855,6 @@ const submitTask = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Get growth record by ID
|
|
||||||
*/
|
|
||||||
const getGrowthRecord2 = (
|
|
||||||
id: number,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/parent/growth-records/${id}`, method: 'GET',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Update growth record
|
|
||||||
*/
|
|
||||||
const updateGrowthRecord2 = (
|
|
||||||
id: number,
|
|
||||||
growthRecordUpdateRequest: GrowthRecordUpdateRequest,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/parent/growth-records/${id}`, method: 'PUT',
|
|
||||||
headers: {'Content-Type': 'application/json', },
|
|
||||||
data: growthRecordUpdateRequest,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Delete growth record
|
|
||||||
*/
|
|
||||||
const deleteGrowthRecord2 = (
|
|
||||||
id: number,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/parent/growth-records/${id}`, method: 'DELETE',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 获取当前用户信息
|
|
||||||
*/
|
|
||||||
const getCurrentUser = (
|
|
||||||
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/auth/profile`, method: 'GET',
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 修改个人信息
|
|
||||||
*/
|
|
||||||
const updateProfile = (
|
|
||||||
updateProfileRequest: UpdateProfileRequest,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/auth/profile`, method: 'PUT',
|
|
||||||
headers: {'Content-Type': 'application/json', },
|
|
||||||
data: updateProfileRequest,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 查询主题详情
|
* @summary 查询主题详情
|
||||||
*/
|
*/
|
||||||
@ -1666,12 +1598,12 @@ const saveStudentRecord = (
|
|||||||
*/
|
*/
|
||||||
const batchSaveStudentRecords = (
|
const batchSaveStudentRecords = (
|
||||||
id: number,
|
id: number,
|
||||||
studentRecordRequest: StudentRecordRequest[],
|
batchStudentRecordsRequest: BatchStudentRecordsRequest,
|
||||||
) => {
|
) => {
|
||||||
return customMutator<Blob>(
|
return customMutator<Blob>(
|
||||||
{url: `/v1/teacher/lessons/${id}/students/batch-records`, method: 'POST',
|
{url: `/v1/teacher/lessons/${id}/students/batch-records`, method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json', },
|
headers: {'Content-Type': 'application/json', },
|
||||||
data: studentRecordRequest,
|
data: batchStudentRecordsRequest,
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -2182,14 +2114,17 @@ const assignStudents = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 完成任务(旧接口,兼容使用)
|
* @summary 完成任务(旧接口,兼容使用,支持 JSON body)
|
||||||
*/
|
*/
|
||||||
const completeTask = (
|
const completeTask = (
|
||||||
id: number,
|
id: number,
|
||||||
params: CompleteTaskParams,
|
taskCompleteRequest: TaskCompleteRequest,
|
||||||
|
params?: CompleteTaskParams,
|
||||||
) => {
|
) => {
|
||||||
return customMutator<Blob>(
|
return customMutator<Blob>(
|
||||||
{url: `/v1/parent/tasks/${id}/complete`, method: 'POST',
|
{url: `/v1/parent/tasks/${id}/complete`, method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json', },
|
||||||
|
data: taskCompleteRequest,
|
||||||
params,
|
params,
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
},
|
},
|
||||||
@ -2222,21 +2157,6 @@ const markAllAsRead1 = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Create growth record
|
|
||||||
*/
|
|
||||||
const createGrowthRecord2 = (
|
|
||||||
growthRecordCreateRequest: GrowthRecordCreateRequest,
|
|
||||||
) => {
|
|
||||||
return customMutator<Blob>(
|
|
||||||
{url: `/v1/parent/growth-records`, method: 'POST',
|
|
||||||
headers: {'Content-Type': 'application/json', },
|
|
||||||
data: growthRecordCreateRequest,
|
|
||||||
responseType: 'blob'
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当 Token 即将过期时刷新
|
* 当 Token 即将过期时刷新
|
||||||
* @summary 刷新 WebOffice Token
|
* @summary 刷新 WebOffice Token
|
||||||
@ -3541,7 +3461,7 @@ const getSchoolCourse = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取我的任务列表
|
* @summary 获取我的任务列表(聚合多孩子任务)
|
||||||
*/
|
*/
|
||||||
const getMyTasks = (
|
const getMyTasks = (
|
||||||
params?: GetMyTasksParams,
|
params?: GetMyTasksParams,
|
||||||
@ -3568,7 +3488,7 @@ const getTask1 = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取孩子的任务列表
|
* @summary 获取孩子的任务列表(含完成信息与教师评价)
|
||||||
*/
|
*/
|
||||||
const getTasksByStudent = (
|
const getTasksByStudent = (
|
||||||
studentId: number,
|
studentId: number,
|
||||||
@ -3648,6 +3568,19 @@ const getUnreadCount1 = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get growth record by ID
|
||||||
|
*/
|
||||||
|
const getGrowthRecord2 = (
|
||||||
|
id: number,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/growth-records/${id}`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get growth records by student ID
|
* @summary Get growth records by student ID
|
||||||
*/
|
*/
|
||||||
@ -3704,6 +3637,21 @@ const getChild = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Get child lesson records (reading history)
|
||||||
|
*/
|
||||||
|
const getChildLessons = (
|
||||||
|
id: number,
|
||||||
|
params?: GetChildLessonsParams,
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/parent/children/${id}/lessons`, method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get child growth records
|
* @summary Get child growth records
|
||||||
*/
|
*/
|
||||||
@ -3761,6 +3709,19 @@ const getOssToken = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary 获取当前用户信息
|
||||||
|
*/
|
||||||
|
const getCurrentUser = (
|
||||||
|
|
||||||
|
) => {
|
||||||
|
return customMutator<Blob>(
|
||||||
|
{url: `/v1/auth/profile`, method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary 获取租户统计信息
|
* @summary 获取租户统计信息
|
||||||
*/
|
*/
|
||||||
@ -3936,7 +3897,7 @@ const deleteFile = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {getTask,updateTask,deleteTask,updateFeedback,submitFeedback,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,updateSubmission,submitTask,getGrowthRecord2,updateGrowthRecord2,deleteGrowthRecord2,getCurrentUser,updateProfile,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback1,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,createGrowthRecord2,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getTaskCompletions,getCompletionDetail,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getTaskList,getTaskDetail,getTaskCompletions1,getStatistics,getCompletionDetail1,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask1,getTasksByStudent,getCompletionDetail2,getFeedback,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,getAllPublishedCourses,findByType,getAllPublishedCollections,deleteFile}};
|
return {getTask,updateTask,deleteTask,updateFeedback,submitFeedback,getTemplate,updateTemplate,deleteTemplate,getSchedule,updateSchedule,cancelSchedule,getLesson,updateLesson,getLessonProgress,saveLessonProgress,getGrowthRecord,updateGrowthRecord,deleteGrowthRecord,getTeacher,updateTeacher,deleteTeacher,getTemplate1,updateTemplate1,deleteTemplate1,getStudent,updateStudent,deleteStudent,getSettings,updateSettings,getSecuritySettings,updateSecuritySettings,getNotificationSettings,updateNotificationSettings,getBasicSettings,updateBasicSettings,getSchedule1,updateSchedule1,cancelSchedule1,getParent,updateParent,deleteParent,getGrowthRecord1,updateGrowthRecord1,deleteGrowthRecord1,getClass,updateClass,deleteClass,updateClassTeacher,removeClassTeacher,updateSubmission,submitTask,findOne,update,_delete,reorder,getTenant,updateTenant,deleteTenant,updateTenantStatus,updateTenantQuota,getAllSettings,updateSettings1,getStorageSettings,updateStorageSettings,getSecuritySettings1,updateSecuritySettings1,getNotificationSettings1,updateNotificationSettings1,getBasicSettings1,updateBasicSettings1,findLibrary,updateLibrary,deleteLibrary,findItem,updateItem,deleteItem,getCourse1,updateCourse,deleteCourse,reorderSteps,findOne1,update1,delete1,updateStep,removeStep,reorder1,findOne2,update2,delete2,setPackages,getTaskPage,createTask,getTemplates,createTemplate,createFromTemplate,getSchedules,createSchedule,markAsRead,markAllAsRead,getMyLessons,createLesson,saveStudentRecord,batchSaveStudentRecords,startLesson,getLessonFeedback,submitFeedback1,completeLesson,cancelLesson,createLessonFromSchedule,startLessonFromSchedule,getGrowthRecordPage,createGrowthRecord,getTeacherPage,createTeacher,resetPassword,getTemplates1,createTemplate1,getStudentPage,createStudent,getSchedules1,createSchedule1,checkConflict,batchCreateSchedules,createSchedulesByClasses,getParentPage,createParent,bindStudent,unbindStudent,resetPassword1,renewCollection,getGrowthRecordPage1,createGrowthRecord1,getClassPage,createClass,getClassTeachers1,assignTeachers,getClassStudents1,assignStudents,completeTask,markAsRead1,markAllAsRead1,refreshToken,uploadFile,refreshToken1,logout,login,changePassword,findAll,create,getTenantPage,createTenant,resetTenantPassword,findAllLibraries,createLibrary,findAllItems,createItem,batchDeleteItems,getCoursePage1,createCourse,submitCourse,rejectCourse,publishCourse,archiveCourse,findAll1,create1,findSteps,createStep,page,create2,withdraw,submit,republish,reject,publish,archive,getWeeklyStats,getTodayLessons,getTaskCompletions,getCompletionDetail,getDefaultTemplate,getAllStudents,getTodaySchedules,getTimetable,getRecommendedCourses,getMyNotifications,getNotification,getUnreadCount,getStudentRecords,getTodayLessons1,getLessonTrend,getFeedbacks,getFeedbackStats,getDashboard,getCoursePage,getCourse,getAllCourses,getCourseUsage,getClasses,getClassTeachers,getClassStudents,getDefaultTemplate1,getSchoolStats,getActiveTeachers,getLessonTrend1,getCourseUsageStats,getCourseDistribution,getRecentActivities,getTimetable1,getCoursePackageLessonTypes,getCalendarViewData,getTeacherReports,getStudentReports,getOverview,getCourseReports,getTaskList,getTaskDetail,getTaskCompletions1,getStatistics,getCompletionDetail1,getParentChildren,findTenantCollections,getPackagesByCollection,getPackageCourses,getPackageInfo,getPackageUsage,getLogList,getLogDetail,getLogStats,getFeedbacks1,getFeedbackStats1,exportTeacherStats,exportStudentStats,exportLessons,exportGrowthRecords,getSchoolCourses,getSchoolCourse,getMyTasks,getTask1,getTasksByStudent,getCompletionDetail2,getFeedback,getMyNotifications1,getNotification1,getUnreadCount1,getGrowthRecord2,getGrowthRecordsByStudent,getRecentGrowthRecords,getMyChildren,getChild,getChildLessons,getChildGrowth,generateEditToken,generateReadOnlyToken,getOssToken,getCurrentUser,getTenantStats,getAllActiveTenants,getStats,getTrendData,getActiveTenants,getPopularCourses,getRecentActivities1,getTenantDefaults,getStats1,getAllPublishedCourses,findByType,getAllPublishedCollections,deleteFile}};
|
||||||
export type GetTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask']>>>
|
export type GetTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTask']>>>
|
||||||
export type UpdateTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask']>>>
|
export type UpdateTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateTask']>>>
|
||||||
export type DeleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask']>>>
|
export type DeleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteTask']>>>
|
||||||
@ -3988,11 +3949,6 @@ export type UpdateClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType
|
|||||||
export type RemoveClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['removeClassTeacher']>>>
|
export type RemoveClassTeacherResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['removeClassTeacher']>>>
|
||||||
export type UpdateSubmissionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateSubmission']>>>
|
export type UpdateSubmissionResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateSubmission']>>>
|
||||||
export type SubmitTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitTask']>>>
|
export type SubmitTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['submitTask']>>>
|
||||||
export type GetGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecord2']>>>
|
|
||||||
export type UpdateGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateGrowthRecord2']>>>
|
|
||||||
export type DeleteGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['deleteGrowthRecord2']>>>
|
|
||||||
export type GetCurrentUserResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCurrentUser']>>>
|
|
||||||
export type UpdateProfileResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['updateProfile']>>>
|
|
||||||
export type FindOneResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findOne']>>>
|
export type FindOneResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['findOne']>>>
|
||||||
export type UpdateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['update']>>>
|
export type UpdateResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['update']>>>
|
||||||
export type _DeleteResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['_delete']>>>
|
export type _DeleteResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['_delete']>>>
|
||||||
@ -4083,7 +4039,6 @@ export type AssignStudentsResult = NonNullable<Awaited<ReturnType<ReturnType<typ
|
|||||||
export type CompleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['completeTask']>>>
|
export type CompleteTaskResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['completeTask']>>>
|
||||||
export type MarkAsRead1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['markAsRead1']>>>
|
export type MarkAsRead1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['markAsRead1']>>>
|
||||||
export type MarkAllAsRead1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['markAllAsRead1']>>>
|
export type MarkAllAsRead1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['markAllAsRead1']>>>
|
||||||
export type CreateGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['createGrowthRecord2']>>>
|
|
||||||
export type RefreshTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['refreshToken']>>>
|
export type RefreshTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['refreshToken']>>>
|
||||||
export type UploadFileResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['uploadFile']>>>
|
export type UploadFileResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['uploadFile']>>>
|
||||||
export type RefreshToken1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['refreshToken1']>>>
|
export type RefreshToken1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['refreshToken1']>>>
|
||||||
@ -4187,14 +4142,17 @@ export type GetFeedbackResult = NonNullable<Awaited<ReturnType<ReturnType<typeof
|
|||||||
export type GetMyNotifications1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyNotifications1']>>>
|
export type GetMyNotifications1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyNotifications1']>>>
|
||||||
export type GetNotification1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getNotification1']>>>
|
export type GetNotification1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getNotification1']>>>
|
||||||
export type GetUnreadCount1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getUnreadCount1']>>>
|
export type GetUnreadCount1Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getUnreadCount1']>>>
|
||||||
|
export type GetGrowthRecord2Result = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecord2']>>>
|
||||||
export type GetGrowthRecordsByStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecordsByStudent']>>>
|
export type GetGrowthRecordsByStudentResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getGrowthRecordsByStudent']>>>
|
||||||
export type GetRecentGrowthRecordsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getRecentGrowthRecords']>>>
|
export type GetRecentGrowthRecordsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getRecentGrowthRecords']>>>
|
||||||
export type GetMyChildrenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyChildren']>>>
|
export type GetMyChildrenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getMyChildren']>>>
|
||||||
export type GetChildResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getChild']>>>
|
export type GetChildResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getChild']>>>
|
||||||
|
export type GetChildLessonsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getChildLessons']>>>
|
||||||
export type GetChildGrowthResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getChildGrowth']>>>
|
export type GetChildGrowthResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getChildGrowth']>>>
|
||||||
export type GenerateEditTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateEditToken']>>>
|
export type GenerateEditTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateEditToken']>>>
|
||||||
export type GenerateReadOnlyTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateReadOnlyToken']>>>
|
export type GenerateReadOnlyTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['generateReadOnlyToken']>>>
|
||||||
export type GetOssTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOssToken']>>>
|
export type GetOssTokenResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getOssToken']>>>
|
||||||
|
export type GetCurrentUserResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getCurrentUser']>>>
|
||||||
export type GetTenantStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTenantStats']>>>
|
export type GetTenantStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getTenantStats']>>>
|
||||||
export type GetAllActiveTenantsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllActiveTenants']>>>
|
export type GetAllActiveTenantsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getAllActiveTenants']>>>
|
||||||
export type GetStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStats']>>>
|
export type GetStatsResult = NonNullable<Awaited<ReturnType<ReturnType<typeof getReadingPlatformAPI>['getStats']>>>
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { StudentRecordRequest } from './studentRecordRequest';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存学生记录请求
|
||||||
|
*/
|
||||||
|
export interface BatchStudentRecordsRequest {
|
||||||
|
/** 记录列表 */
|
||||||
|
records: StudentRecordRequest[];
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ClassInfo } from './classInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子列表响应
|
||||||
|
*/
|
||||||
|
export interface ChildInfoResponse {
|
||||||
|
/** 学生ID */
|
||||||
|
id?: number;
|
||||||
|
/** 姓名 */
|
||||||
|
name?: string;
|
||||||
|
/** 性别 */
|
||||||
|
gender?: string;
|
||||||
|
/** 出生日期 */
|
||||||
|
birthDate?: string;
|
||||||
|
/** 与家长关系:FATHER/MOTHER/GRANDFATHER/GRANDMOTHER/OTHER */
|
||||||
|
relationship?: string;
|
||||||
|
/** 阅读次数(student_record 数量) */
|
||||||
|
readingCount?: number;
|
||||||
|
/** 上课次数(lesson 记录数) */
|
||||||
|
lessonCount?: number;
|
||||||
|
class?: ClassInfo;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ClassInfo } from './classInfo';
|
||||||
|
import type { StatsInfo } from './statsInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子详情响应
|
||||||
|
*/
|
||||||
|
export interface ChildProfileResponse {
|
||||||
|
/** 学生ID */
|
||||||
|
id?: number;
|
||||||
|
/** 姓名 */
|
||||||
|
name?: string;
|
||||||
|
/** 性别 */
|
||||||
|
gender?: string;
|
||||||
|
/** 出生日期 */
|
||||||
|
birthDate?: string;
|
||||||
|
/** 与家长关系 */
|
||||||
|
relationship?: string;
|
||||||
|
/** 阅读次数 */
|
||||||
|
readingCount?: number;
|
||||||
|
/** 上课次数 */
|
||||||
|
lessonCount?: number;
|
||||||
|
stats?: StatsInfo;
|
||||||
|
class?: ClassInfo;
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type CompleteTaskParams = {
|
export type CompleteTaskParams = {
|
||||||
studentId: number;
|
studentId?: number;
|
||||||
content?: string;
|
content?: string;
|
||||||
attachments?: string;
|
attachments?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联课程信息
|
||||||
|
*/
|
||||||
|
export interface CourseInfo {
|
||||||
|
/** 课程ID */
|
||||||
|
id?: number;
|
||||||
|
/** 课程名称 */
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetChildLessonsParams = {
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
};
|
||||||
@ -10,4 +10,5 @@ export type GetMyNotifications1Params = {
|
|||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
isRead?: number;
|
isRead?: number;
|
||||||
|
notificationType?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,5 +11,6 @@ pageNum?: number;
|
|||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
taskType?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ClassInfo } from './classInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端成长档案响应
|
||||||
|
*/
|
||||||
|
export interface GrowthRecordForParentResponse {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 内容 */
|
||||||
|
content?: string;
|
||||||
|
/** 图片URL列表 */
|
||||||
|
images?: string[];
|
||||||
|
/** 记录日期 */
|
||||||
|
recordDate?: string;
|
||||||
|
/** 记录类型 */
|
||||||
|
recordType?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
class?: ClassInfo;
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ export * from './addClassTeacherDto';
|
|||||||
export * from './addPackageToCollectionParams';
|
export * from './addPackageToCollectionParams';
|
||||||
export * from './adminStatsControllerGetActiveTenantsParams';
|
export * from './adminStatsControllerGetActiveTenantsParams';
|
||||||
export * from './adminStatsControllerGetPopularCoursesParams';
|
export * from './adminStatsControllerGetPopularCoursesParams';
|
||||||
|
export * from './adminStatsControllerGetRecentActivitiesParams';
|
||||||
export * from './approveCourseDto';
|
export * from './approveCourseDto';
|
||||||
export * from './approveCourseDtoChecklist';
|
export * from './approveCourseDtoChecklist';
|
||||||
export * from './basicSettingsResponse';
|
export * from './basicSettingsResponse';
|
||||||
@ -19,11 +20,14 @@ export * from './basicSettingsUpdateRequest';
|
|||||||
export * from './batchCreateSchedulesBody';
|
export * from './batchCreateSchedulesBody';
|
||||||
export * from './batchStudentRecordsDto';
|
export * from './batchStudentRecordsDto';
|
||||||
export * from './batchStudentRecordsDtoRecordsItem';
|
export * from './batchStudentRecordsDtoRecordsItem';
|
||||||
|
export * from './batchStudentRecordsRequest';
|
||||||
export * from './bindStudentParams';
|
export * from './bindStudentParams';
|
||||||
export * from './calendarViewResponse';
|
export * from './calendarViewResponse';
|
||||||
export * from './calendarViewResponseSchedules';
|
export * from './calendarViewResponseSchedules';
|
||||||
export * from './changePasswordParams';
|
export * from './changePasswordParams';
|
||||||
export * from './checkConflictParams';
|
export * from './checkConflictParams';
|
||||||
|
export * from './childInfoResponse';
|
||||||
|
export * from './childProfileResponse';
|
||||||
export * from './classCreateRequest';
|
export * from './classCreateRequest';
|
||||||
export * from './classInfo';
|
export * from './classInfo';
|
||||||
export * from './classResponse';
|
export * from './classResponse';
|
||||||
@ -40,6 +44,7 @@ export * from './courseCollectionResponse';
|
|||||||
export * from './courseControllerFindAllParams';
|
export * from './courseControllerFindAllParams';
|
||||||
export * from './courseControllerGetReviewListParams';
|
export * from './courseControllerGetReviewListParams';
|
||||||
export * from './courseCreateRequest';
|
export * from './courseCreateRequest';
|
||||||
|
export * from './courseInfo';
|
||||||
export * from './courseLesson';
|
export * from './courseLesson';
|
||||||
export * from './courseLessonCreateRequest';
|
export * from './courseLessonCreateRequest';
|
||||||
export * from './courseLessonResponse';
|
export * from './courseLessonResponse';
|
||||||
@ -48,13 +53,11 @@ export * from './coursePackageControllerFindAllParams';
|
|||||||
export * from './coursePackageCourseItem';
|
export * from './coursePackageCourseItem';
|
||||||
export * from './coursePackageItem';
|
export * from './coursePackageItem';
|
||||||
export * from './coursePackageResponse';
|
export * from './coursePackageResponse';
|
||||||
export * from './coursePackageVO';
|
|
||||||
export * from './coursePageQueryRequest';
|
export * from './coursePageQueryRequest';
|
||||||
export * from './courseRejectRequest';
|
export * from './courseRejectRequest';
|
||||||
export * from './courseReportResponse';
|
export * from './courseReportResponse';
|
||||||
export * from './courseResponse';
|
export * from './courseResponse';
|
||||||
export * from './courseUpdateRequest';
|
export * from './courseUpdateRequest';
|
||||||
export * from './courseUsageVO';
|
|
||||||
export * from './createClassDto';
|
export * from './createClassDto';
|
||||||
export * from './createCollectionRequest';
|
export * from './createCollectionRequest';
|
||||||
export * from './createFromSourceDto';
|
export * from './createFromSourceDto';
|
||||||
@ -100,6 +103,7 @@ export * from './getActiveTenantsParams';
|
|||||||
export * from './getAllCoursesParams';
|
export * from './getAllCoursesParams';
|
||||||
export * from './getAllStudentsParams';
|
export * from './getAllStudentsParams';
|
||||||
export * from './getCalendarViewDataParams';
|
export * from './getCalendarViewDataParams';
|
||||||
|
export * from './getChildLessonsParams';
|
||||||
export * from './getClassPageParams';
|
export * from './getClassPageParams';
|
||||||
export * from './getClassStudents1Params';
|
export * from './getClassStudents1Params';
|
||||||
export * from './getClassStudentsParams';
|
export * from './getClassStudentsParams';
|
||||||
@ -122,6 +126,10 @@ export * from './getParentPageParams';
|
|||||||
export * from './getPopularCourses200';
|
export * from './getPopularCourses200';
|
||||||
export * from './getPopularCourses200DataItem';
|
export * from './getPopularCourses200DataItem';
|
||||||
export * from './getPopularCoursesParams';
|
export * from './getPopularCoursesParams';
|
||||||
|
export * from './getRecentActivities1200';
|
||||||
|
export * from './getRecentActivities1200DataItem';
|
||||||
|
export * from './getRecentActivities1Params';
|
||||||
|
export * from './getRecentActivitiesParams';
|
||||||
export * from './getRecentGrowthRecordsParams';
|
export * from './getRecentGrowthRecordsParams';
|
||||||
export * from './getSchedules1Params';
|
export * from './getSchedules1Params';
|
||||||
export * from './getSchedulesParams';
|
export * from './getSchedulesParams';
|
||||||
@ -144,6 +152,7 @@ export * from './grantCollectionRequest';
|
|||||||
export * from './grantRequest';
|
export * from './grantRequest';
|
||||||
export * from './growthRecord';
|
export * from './growthRecord';
|
||||||
export * from './growthRecordCreateRequest';
|
export * from './growthRecordCreateRequest';
|
||||||
|
export * from './growthRecordForParentResponse';
|
||||||
export * from './growthRecordResponse';
|
export * from './growthRecordResponse';
|
||||||
export * from './growthRecordUpdateRequest';
|
export * from './growthRecordUpdateRequest';
|
||||||
export * from './immTokenVo';
|
export * from './immTokenVo';
|
||||||
@ -159,10 +168,12 @@ export * from './lessonFeedbackDtoActivitiesDone';
|
|||||||
export * from './lessonFeedbackDtoStepFeedbacks';
|
export * from './lessonFeedbackDtoStepFeedbacks';
|
||||||
export * from './lessonFeedbackRequest';
|
export * from './lessonFeedbackRequest';
|
||||||
export * from './lessonFeedbackResponse';
|
export * from './lessonFeedbackResponse';
|
||||||
|
export * from './lessonInfo';
|
||||||
export * from './lessonProgressDto';
|
export * from './lessonProgressDto';
|
||||||
export * from './lessonProgressDtoProgressData';
|
export * from './lessonProgressDtoProgressData';
|
||||||
export * from './lessonProgressRequest';
|
export * from './lessonProgressRequest';
|
||||||
export * from './lessonProgressRequestProgressData';
|
export * from './lessonProgressRequestProgressData';
|
||||||
|
export * from './lessonRecordResponse';
|
||||||
export * from './lessonResponse';
|
export * from './lessonResponse';
|
||||||
export * from './lessonStep';
|
export * from './lessonStep';
|
||||||
export * from './lessonStepCreateRequest';
|
export * from './lessonStepCreateRequest';
|
||||||
@ -178,6 +189,7 @@ export * from './loginDto';
|
|||||||
export * from './loginRequest';
|
export * from './loginRequest';
|
||||||
export * from './loginResponse';
|
export * from './loginResponse';
|
||||||
export * from './notification';
|
export * from './notification';
|
||||||
|
export * from './notificationForParentResponse';
|
||||||
export * from './notificationResponse';
|
export * from './notificationResponse';
|
||||||
export * from './notificationSettingsResponse';
|
export * from './notificationSettingsResponse';
|
||||||
export * from './notificationSettingsUpdateRequest';
|
export * from './notificationSettingsUpdateRequest';
|
||||||
@ -201,11 +213,14 @@ export * from './pageResultCourseCollectionResponse';
|
|||||||
export * from './pageResultCoursePackageResponse';
|
export * from './pageResultCoursePackageResponse';
|
||||||
export * from './pageResultCourseResponse';
|
export * from './pageResultCourseResponse';
|
||||||
export * from './pageResultGrowthRecord';
|
export * from './pageResultGrowthRecord';
|
||||||
|
export * from './pageResultGrowthRecordForParentResponse';
|
||||||
export * from './pageResultGrowthRecordResponse';
|
export * from './pageResultGrowthRecordResponse';
|
||||||
export * from './pageResultLesson';
|
export * from './pageResultLesson';
|
||||||
export * from './pageResultLessonFeedbackResponse';
|
export * from './pageResultLessonFeedbackResponse';
|
||||||
|
export * from './pageResultLessonRecordResponse';
|
||||||
export * from './pageResultLessonResponse';
|
export * from './pageResultLessonResponse';
|
||||||
export * from './pageResultNotification';
|
export * from './pageResultNotification';
|
||||||
|
export * from './pageResultNotificationForParentResponse';
|
||||||
export * from './pageResultNotificationResponse';
|
export * from './pageResultNotificationResponse';
|
||||||
export * from './pageResultOperationLogResponse';
|
export * from './pageResultOperationLogResponse';
|
||||||
export * from './pageResultParent';
|
export * from './pageResultParent';
|
||||||
@ -222,6 +237,7 @@ export * from './pageResultTask';
|
|||||||
export * from './pageResultTaskCompletionDetailResponse';
|
export * from './pageResultTaskCompletionDetailResponse';
|
||||||
export * from './pageResultTaskResponse';
|
export * from './pageResultTaskResponse';
|
||||||
export * from './pageResultTaskTemplateResponse';
|
export * from './pageResultTaskTemplateResponse';
|
||||||
|
export * from './pageResultTaskWithCompletionResponse';
|
||||||
export * from './pageResultTeacher';
|
export * from './pageResultTeacher';
|
||||||
export * from './pageResultTeacherResponse';
|
export * from './pageResultTeacherResponse';
|
||||||
export * from './pageResultTenant';
|
export * from './pageResultTenant';
|
||||||
@ -233,6 +249,8 @@ export * from './parentStudentResponse';
|
|||||||
export * from './parentUpdateRequest';
|
export * from './parentUpdateRequest';
|
||||||
export * from './popularCourseItemResponse';
|
export * from './popularCourseItemResponse';
|
||||||
export * from './popularCoursesQueryRequest';
|
export * from './popularCoursesQueryRequest';
|
||||||
|
export * from './recentActivitiesQueryRequest';
|
||||||
|
export * from './recentActivityItemResponse';
|
||||||
export * from './refreshTokenRequest';
|
export * from './refreshTokenRequest';
|
||||||
export * from './rejectCourseDto';
|
export * from './rejectCourseDto';
|
||||||
export * from './rejectCourseDtoChecklist';
|
export * from './rejectCourseDtoChecklist';
|
||||||
@ -251,6 +269,7 @@ export * from './resourceLibraryResponse';
|
|||||||
export * from './resourceLibraryUpdateRequest';
|
export * from './resourceLibraryUpdateRequest';
|
||||||
export * from './resultBasicSettingsResponse';
|
export * from './resultBasicSettingsResponse';
|
||||||
export * from './resultCalendarViewResponse';
|
export * from './resultCalendarViewResponse';
|
||||||
|
export * from './resultChildProfileResponse';
|
||||||
export * from './resultClassResponse';
|
export * from './resultClassResponse';
|
||||||
export * from './resultClazz';
|
export * from './resultClazz';
|
||||||
export * from './resultConflictCheckResult';
|
export * from './resultConflictCheckResult';
|
||||||
@ -264,6 +283,7 @@ export * from './resultCourseResponse';
|
|||||||
export * from './resultDto';
|
export * from './resultDto';
|
||||||
export * from './resultDtoData';
|
export * from './resultDtoData';
|
||||||
export * from './resultGrowthRecord';
|
export * from './resultGrowthRecord';
|
||||||
|
export * from './resultGrowthRecordForParentResponse';
|
||||||
export * from './resultGrowthRecordResponse';
|
export * from './resultGrowthRecordResponse';
|
||||||
export * from './resultImmTokenVo';
|
export * from './resultImmTokenVo';
|
||||||
export * from './resultLesson';
|
export * from './resultLesson';
|
||||||
@ -273,6 +293,7 @@ export * from './resultLessonResponse';
|
|||||||
export * from './resultLessonStep';
|
export * from './resultLessonStep';
|
||||||
export * from './resultLessonStepResponse';
|
export * from './resultLessonStepResponse';
|
||||||
export * from './resultListActiveTenantItemResponse';
|
export * from './resultListActiveTenantItemResponse';
|
||||||
|
export * from './resultListChildInfoResponse';
|
||||||
export * from './resultListClassResponse';
|
export * from './resultListClassResponse';
|
||||||
export * from './resultListClassTeacherResponse';
|
export * from './resultListClassTeacherResponse';
|
||||||
export * from './resultListClazz';
|
export * from './resultListClazz';
|
||||||
@ -282,11 +303,10 @@ export * from './resultListCourseLesson';
|
|||||||
export * from './resultListCourseLessonResponse';
|
export * from './resultListCourseLessonResponse';
|
||||||
export * from './resultListCoursePackage';
|
export * from './resultListCoursePackage';
|
||||||
export * from './resultListCoursePackageResponse';
|
export * from './resultListCoursePackageResponse';
|
||||||
export * from './resultListCoursePackageVO';
|
|
||||||
export * from './resultListCourseReportResponse';
|
export * from './resultListCourseReportResponse';
|
||||||
export * from './resultListCourseResponse';
|
export * from './resultListCourseResponse';
|
||||||
export * from './resultListCourseUsageVO';
|
|
||||||
export * from './resultListGrowthRecord';
|
export * from './resultListGrowthRecord';
|
||||||
|
export * from './resultListGrowthRecordForParentResponse';
|
||||||
export * from './resultListGrowthRecordResponse';
|
export * from './resultListGrowthRecordResponse';
|
||||||
export * from './resultListLesson';
|
export * from './resultListLesson';
|
||||||
export * from './resultListLessonResponse';
|
export * from './resultListLessonResponse';
|
||||||
@ -297,13 +317,12 @@ export * from './resultListMapStringObject';
|
|||||||
export * from './resultListMapStringObjectDataItem';
|
export * from './resultListMapStringObjectDataItem';
|
||||||
export * from './resultListParentStudentResponse';
|
export * from './resultListParentStudentResponse';
|
||||||
export * from './resultListPopularCourseItemResponse';
|
export * from './resultListPopularCourseItemResponse';
|
||||||
|
export * from './resultListRecentActivityItemResponse';
|
||||||
export * from './resultListSchedulePlanResponse';
|
export * from './resultListSchedulePlanResponse';
|
||||||
export * from './resultListStudent';
|
export * from './resultListStudent';
|
||||||
export * from './resultListStudentRecordResponse';
|
export * from './resultListStudentRecordResponse';
|
||||||
export * from './resultListStudentReportResponse';
|
export * from './resultListStudentReportResponse';
|
||||||
export * from './resultListStudentResponse';
|
export * from './resultListStudentResponse';
|
||||||
export * from './resultListTeacherLessonTrendVO';
|
|
||||||
export * from './resultListTeacherLessonVO';
|
|
||||||
export * from './resultListTeacherReportResponse';
|
export * from './resultListTeacherReportResponse';
|
||||||
export * from './resultListTeacherResponse';
|
export * from './resultListTeacherResponse';
|
||||||
export * from './resultListTenantPackage';
|
export * from './resultListTenantPackage';
|
||||||
@ -318,6 +337,7 @@ export * from './resultMapStringObjectData';
|
|||||||
export * from './resultMapStringString';
|
export * from './resultMapStringString';
|
||||||
export * from './resultMapStringStringData';
|
export * from './resultMapStringStringData';
|
||||||
export * from './resultNotification';
|
export * from './resultNotification';
|
||||||
|
export * from './resultNotificationForParentResponse';
|
||||||
export * from './resultNotificationResponse';
|
export * from './resultNotificationResponse';
|
||||||
export * from './resultNotificationSettingsResponse';
|
export * from './resultNotificationSettingsResponse';
|
||||||
export * from './resultObject';
|
export * from './resultObject';
|
||||||
@ -336,11 +356,14 @@ export * from './resultPageResultCourseCollectionResponse';
|
|||||||
export * from './resultPageResultCoursePackageResponse';
|
export * from './resultPageResultCoursePackageResponse';
|
||||||
export * from './resultPageResultCourseResponse';
|
export * from './resultPageResultCourseResponse';
|
||||||
export * from './resultPageResultGrowthRecord';
|
export * from './resultPageResultGrowthRecord';
|
||||||
|
export * from './resultPageResultGrowthRecordForParentResponse';
|
||||||
export * from './resultPageResultGrowthRecordResponse';
|
export * from './resultPageResultGrowthRecordResponse';
|
||||||
export * from './resultPageResultLesson';
|
export * from './resultPageResultLesson';
|
||||||
export * from './resultPageResultLessonFeedbackResponse';
|
export * from './resultPageResultLessonFeedbackResponse';
|
||||||
|
export * from './resultPageResultLessonRecordResponse';
|
||||||
export * from './resultPageResultLessonResponse';
|
export * from './resultPageResultLessonResponse';
|
||||||
export * from './resultPageResultNotification';
|
export * from './resultPageResultNotification';
|
||||||
|
export * from './resultPageResultNotificationForParentResponse';
|
||||||
export * from './resultPageResultNotificationResponse';
|
export * from './resultPageResultNotificationResponse';
|
||||||
export * from './resultPageResultOperationLogResponse';
|
export * from './resultPageResultOperationLogResponse';
|
||||||
export * from './resultPageResultParent';
|
export * from './resultPageResultParent';
|
||||||
@ -357,6 +380,7 @@ export * from './resultPageResultTask';
|
|||||||
export * from './resultPageResultTaskCompletionDetailResponse';
|
export * from './resultPageResultTaskCompletionDetailResponse';
|
||||||
export * from './resultPageResultTaskResponse';
|
export * from './resultPageResultTaskResponse';
|
||||||
export * from './resultPageResultTaskTemplateResponse';
|
export * from './resultPageResultTaskTemplateResponse';
|
||||||
|
export * from './resultPageResultTaskWithCompletionResponse';
|
||||||
export * from './resultPageResultTeacher';
|
export * from './resultPageResultTeacher';
|
||||||
export * from './resultPageResultTeacherResponse';
|
export * from './resultPageResultTeacherResponse';
|
||||||
export * from './resultPageResultTenant';
|
export * from './resultPageResultTenant';
|
||||||
@ -377,6 +401,7 @@ export * from './resultStatsTrendResponse';
|
|||||||
export * from './resultString';
|
export * from './resultString';
|
||||||
export * from './resultStudent';
|
export * from './resultStudent';
|
||||||
export * from './resultStudentRecordResponse';
|
export * from './resultStudentRecordResponse';
|
||||||
|
export * from './resultStudentRecordsResponse';
|
||||||
export * from './resultStudentResponse';
|
export * from './resultStudentResponse';
|
||||||
export * from './resultTask';
|
export * from './resultTask';
|
||||||
export * from './resultTaskCompletionDetailResponse';
|
export * from './resultTaskCompletionDetailResponse';
|
||||||
@ -384,16 +409,13 @@ export * from './resultTaskFeedbackResponse';
|
|||||||
export * from './resultTaskResponse';
|
export * from './resultTaskResponse';
|
||||||
export * from './resultTaskTemplateResponse';
|
export * from './resultTaskTemplateResponse';
|
||||||
export * from './resultTeacher';
|
export * from './resultTeacher';
|
||||||
export * from './resultTeacherDashboardResponse';
|
|
||||||
export * from './resultTeacherResponse';
|
export * from './resultTeacherResponse';
|
||||||
export * from './resultTeacherWeeklyStatsResponse';
|
|
||||||
export * from './resultTenant';
|
export * from './resultTenant';
|
||||||
export * from './resultTenantResponse';
|
export * from './resultTenantResponse';
|
||||||
export * from './resultTheme';
|
export * from './resultTheme';
|
||||||
export * from './resultThemeResponse';
|
export * from './resultThemeResponse';
|
||||||
export * from './resultTimetableResponse';
|
export * from './resultTimetableResponse';
|
||||||
export * from './resultTokenResponse';
|
export * from './resultTokenResponse';
|
||||||
export * from './resultUpdateProfileResponse';
|
|
||||||
export * from './resultUserInfoResponse';
|
export * from './resultUserInfoResponse';
|
||||||
export * from './resultVoid';
|
export * from './resultVoid';
|
||||||
export * from './resultVoidData';
|
export * from './resultVoidData';
|
||||||
@ -413,6 +435,8 @@ export * from './securitySettingsResponse';
|
|||||||
export * from './securitySettingsUpdateRequest';
|
export * from './securitySettingsUpdateRequest';
|
||||||
export * from './statsControllerGetActiveTeachersParams';
|
export * from './statsControllerGetActiveTeachersParams';
|
||||||
export * from './statsControllerGetLessonTrendParams';
|
export * from './statsControllerGetLessonTrendParams';
|
||||||
|
export * from './statsControllerGetRecentActivitiesParams';
|
||||||
|
export * from './statsInfo';
|
||||||
export * from './statsResponse';
|
export * from './statsResponse';
|
||||||
export * from './statsTrendResponse';
|
export * from './statsTrendResponse';
|
||||||
export * from './stepCreateRequest';
|
export * from './stepCreateRequest';
|
||||||
@ -422,20 +446,25 @@ export * from './studentInfo';
|
|||||||
export * from './studentRecordDto';
|
export * from './studentRecordDto';
|
||||||
export * from './studentRecordRequest';
|
export * from './studentRecordRequest';
|
||||||
export * from './studentRecordResponse';
|
export * from './studentRecordResponse';
|
||||||
|
export * from './studentRecordsResponse';
|
||||||
export * from './studentReportResponse';
|
export * from './studentReportResponse';
|
||||||
export * from './studentResponse';
|
export * from './studentResponse';
|
||||||
export * from './studentUpdateRequest';
|
export * from './studentUpdateRequest';
|
||||||
|
export * from './studentWithRecordResponse';
|
||||||
export * from './submitCourseDto';
|
export * from './submitCourseDto';
|
||||||
export * from './task';
|
export * from './task';
|
||||||
|
export * from './taskCompleteRequest';
|
||||||
export * from './taskCompletionDetailResponse';
|
export * from './taskCompletionDetailResponse';
|
||||||
export * from './taskCreateRequest';
|
export * from './taskCreateRequest';
|
||||||
export * from './taskFeedbackRequest';
|
export * from './taskFeedbackRequest';
|
||||||
export * from './taskFeedbackResponse';
|
export * from './taskFeedbackResponse';
|
||||||
|
export * from './taskInfo';
|
||||||
export * from './taskResponse';
|
export * from './taskResponse';
|
||||||
export * from './taskSubmitRequest';
|
export * from './taskSubmitRequest';
|
||||||
export * from './taskTemplateCreateRequest';
|
export * from './taskTemplateCreateRequest';
|
||||||
export * from './taskTemplateResponse';
|
export * from './taskTemplateResponse';
|
||||||
export * from './taskUpdateRequest';
|
export * from './taskUpdateRequest';
|
||||||
|
export * from './taskWithCompletionResponse';
|
||||||
export * from './teacher';
|
export * from './teacher';
|
||||||
export * from './teacherCourseControllerFindAllParams';
|
export * from './teacherCourseControllerFindAllParams';
|
||||||
export * from './teacherCourseControllerGetAllStudentsParams';
|
export * from './teacherCourseControllerGetAllStudentsParams';
|
||||||
@ -444,17 +473,12 @@ export * from './teacherCourseControllerGetLessonTrendParams';
|
|||||||
export * from './teacherCourseControllerGetTeacherSchedulesParams';
|
export * from './teacherCourseControllerGetTeacherSchedulesParams';
|
||||||
export * from './teacherCourseControllerGetTeacherTimetableParams';
|
export * from './teacherCourseControllerGetTeacherTimetableParams';
|
||||||
export * from './teacherCreateRequest';
|
export * from './teacherCreateRequest';
|
||||||
export * from './teacherDashboardResponse';
|
|
||||||
export * from './teacherFeedbackControllerFindAllParams';
|
export * from './teacherFeedbackControllerFindAllParams';
|
||||||
export * from './teacherLessonTrendVO';
|
|
||||||
export * from './teacherLessonVO';
|
|
||||||
export * from './teacherReportResponse';
|
export * from './teacherReportResponse';
|
||||||
export * from './teacherResponse';
|
export * from './teacherResponse';
|
||||||
export * from './teacherResponseClassNames';
|
export * from './teacherResponseClassNames';
|
||||||
export * from './teacherStats';
|
|
||||||
export * from './teacherTaskControllerGetMonthlyStatsParams';
|
export * from './teacherTaskControllerGetMonthlyStatsParams';
|
||||||
export * from './teacherUpdateRequest';
|
export * from './teacherUpdateRequest';
|
||||||
export * from './teacherWeeklyStatsResponse';
|
|
||||||
export * from './tenant';
|
export * from './tenant';
|
||||||
export * from './tenantControllerFindAllPackageType';
|
export * from './tenantControllerFindAllPackageType';
|
||||||
export * from './tenantControllerFindAllParams';
|
export * from './tenantControllerFindAllParams';
|
||||||
@ -480,8 +504,6 @@ export * from './updateLessonDto';
|
|||||||
export * from './updateLibraryDto';
|
export * from './updateLibraryDto';
|
||||||
export * from './updateNotificationSettings1Body';
|
export * from './updateNotificationSettings1Body';
|
||||||
export * from './updateNotificationSettingsBody';
|
export * from './updateNotificationSettingsBody';
|
||||||
export * from './updateProfileRequest';
|
|
||||||
export * from './updateProfileResponse';
|
|
||||||
export * from './updateResourceItemDto';
|
export * from './updateResourceItemDto';
|
||||||
export * from './updateSchedule1Body';
|
export * from './updateSchedule1Body';
|
||||||
export * from './updateScheduleBody';
|
export * from './updateScheduleBody';
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课时简要信息
|
||||||
|
*/
|
||||||
|
export interface LessonInfo {
|
||||||
|
/** 课时 ID */
|
||||||
|
id?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 班级名称 */
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { LessonInfo } from './lessonInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端阅读记录响应
|
||||||
|
*/
|
||||||
|
export interface LessonRecordResponse {
|
||||||
|
/** 记录ID(student_record.id) */
|
||||||
|
id?: number;
|
||||||
|
lesson?: LessonInfo;
|
||||||
|
/** 专注度评分 */
|
||||||
|
focus?: number;
|
||||||
|
/** 参与度评分 */
|
||||||
|
participation?: number;
|
||||||
|
/** 兴趣度评分 */
|
||||||
|
interest?: number;
|
||||||
|
/** 理解度评分 */
|
||||||
|
understanding?: number;
|
||||||
|
/** 备注 */
|
||||||
|
notes?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端通知响应
|
||||||
|
*/
|
||||||
|
export interface NotificationForParentResponse {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 内容 */
|
||||||
|
content?: string;
|
||||||
|
/** 是否已读 */
|
||||||
|
isRead?: boolean;
|
||||||
|
/** 阅读时间 */
|
||||||
|
readAt?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 通知类型 */
|
||||||
|
notificationType?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { GrowthRecordForParentResponse } from './growthRecordForParentResponse';
|
||||||
|
|
||||||
|
export interface PageResultGrowthRecordForParentResponse {
|
||||||
|
list?: GrowthRecordForParentResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { LessonRecordResponse } from './lessonRecordResponse';
|
||||||
|
|
||||||
|
export interface PageResultLessonRecordResponse {
|
||||||
|
list?: LessonRecordResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { NotificationForParentResponse } from './notificationForParentResponse';
|
||||||
|
|
||||||
|
export interface PageResultNotificationForParentResponse {
|
||||||
|
list?: NotificationForParentResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TaskWithCompletionResponse } from './taskWithCompletionResponse';
|
||||||
|
|
||||||
|
export interface PageResultTaskWithCompletionResponse {
|
||||||
|
list?: TaskWithCompletionResponse[];
|
||||||
|
total?: number;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
pages?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ChildProfileResponse } from './childProfileResponse';
|
||||||
|
|
||||||
|
export interface ResultChildProfileResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: ChildProfileResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { GrowthRecordForParentResponse } from './growthRecordForParentResponse';
|
||||||
|
|
||||||
|
export interface ResultGrowthRecordForParentResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: GrowthRecordForParentResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { ChildInfoResponse } from './childInfoResponse';
|
||||||
|
|
||||||
|
export interface ResultListChildInfoResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: ChildInfoResponse[];
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { GrowthRecordForParentResponse } from './growthRecordForParentResponse';
|
||||||
|
|
||||||
|
export interface ResultListGrowthRecordForParentResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: GrowthRecordForParentResponse[];
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { NotificationForParentResponse } from './notificationForParentResponse';
|
||||||
|
|
||||||
|
export interface ResultNotificationForParentResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: NotificationForParentResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultGrowthRecordForParentResponse } from './pageResultGrowthRecordForParentResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultGrowthRecordForParentResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultGrowthRecordForParentResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultLessonRecordResponse } from './pageResultLessonRecordResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultLessonRecordResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultLessonRecordResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultNotificationForParentResponse } from './pageResultNotificationForParentResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultNotificationForParentResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultNotificationForParentResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { PageResultTaskWithCompletionResponse } from './pageResultTaskWithCompletionResponse';
|
||||||
|
|
||||||
|
export interface ResultPageResultTaskWithCompletionResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: PageResultTaskWithCompletionResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { StudentRecordsResponse } from './studentRecordsResponse';
|
||||||
|
|
||||||
|
export interface ResultStudentRecordsResponse {
|
||||||
|
code?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: StudentRecordsResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计信息
|
||||||
|
*/
|
||||||
|
export interface StatsInfo {
|
||||||
|
/** 阅读记录数 */
|
||||||
|
lessonRecords?: number;
|
||||||
|
/** 成长档案数 */
|
||||||
|
growthRecords?: number;
|
||||||
|
/** 任务完成数 */
|
||||||
|
taskCompletions?: number;
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { LessonInfo } from './lessonInfo';
|
||||||
|
import type { StudentWithRecordResponse } from './studentWithRecordResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课后记录列表响应
|
||||||
|
*/
|
||||||
|
export interface StudentRecordsResponse {
|
||||||
|
lesson?: LessonInfo;
|
||||||
|
/** 学生列表(含记录) */
|
||||||
|
students?: StudentWithRecordResponse[];
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { StudentRecordResponse } from './studentRecordResponse';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学生及其课后记录
|
||||||
|
*/
|
||||||
|
export interface StudentWithRecordResponse {
|
||||||
|
/** 学生 ID */
|
||||||
|
id?: number;
|
||||||
|
/** 学生姓名 */
|
||||||
|
name?: string;
|
||||||
|
/** 性别 */
|
||||||
|
gender?: string;
|
||||||
|
record?: StudentRecordResponse;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务完成请求
|
||||||
|
*/
|
||||||
|
export interface TaskCompleteRequest {
|
||||||
|
/** 学生ID */
|
||||||
|
studentId: number;
|
||||||
|
/** 完成内容/反馈 */
|
||||||
|
content?: string;
|
||||||
|
/** 附件 */
|
||||||
|
attachments?: string;
|
||||||
|
}
|
||||||
@ -16,14 +16,20 @@ export interface TaskCreateRequest {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 任务类型:reading-阅读,homework-作业,activity-活动 */
|
/** 任务类型:reading-阅读,homework-作业,activity-活动 */
|
||||||
type?: string;
|
type?: string;
|
||||||
|
/** 任务类型(前端兼容 taskType) */
|
||||||
|
taskType?: string;
|
||||||
/** 关联绘本名称(手动填写) */
|
/** 关联绘本名称(手动填写) */
|
||||||
relatedBookName?: string;
|
relatedBookName?: string;
|
||||||
/** 课程 ID */
|
/** 课程 ID */
|
||||||
courseId?: number;
|
courseId?: number;
|
||||||
|
/** 课程 ID(前端兼容 relatedCourseId) */
|
||||||
|
relatedCourseId?: number;
|
||||||
/** 开始日期 */
|
/** 开始日期 */
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
/** 截止日期 */
|
/** 截止日期 */
|
||||||
dueDate?: string;
|
dueDate?: string;
|
||||||
|
/** 截止日期(前端兼容 endDate) */
|
||||||
|
endDate?: string;
|
||||||
/** 附件(JSON 数组) */
|
/** 附件(JSON 数组) */
|
||||||
attachments?: string;
|
attachments?: string;
|
||||||
/** 目标类型:class-班级,student-学生 */
|
/** 目标类型:class-班级,student-学生 */
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { CourseInfo } from './courseInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务基本信息
|
||||||
|
*/
|
||||||
|
export interface TaskInfo {
|
||||||
|
/** 任务ID */
|
||||||
|
id?: number;
|
||||||
|
/** 任务标题 */
|
||||||
|
title?: string;
|
||||||
|
/** 任务描述 */
|
||||||
|
description?: string;
|
||||||
|
/** 任务类型 */
|
||||||
|
taskType?: string;
|
||||||
|
/** 开始日期 */
|
||||||
|
startDate?: string;
|
||||||
|
/** 截止日期 */
|
||||||
|
endDate?: string;
|
||||||
|
/** 关联绘本名称 */
|
||||||
|
relatedBookName?: string;
|
||||||
|
course?: CourseInfo;
|
||||||
|
}
|
||||||
@ -36,6 +36,10 @@ export interface TaskResponse {
|
|||||||
status?: string;
|
status?: string;
|
||||||
/** 附件 */
|
/** 附件 */
|
||||||
attachments?: string;
|
attachments?: string;
|
||||||
|
/** 目标类型:CLASS-班级,STUDENT-学生 */
|
||||||
|
targetType?: string;
|
||||||
|
/** 目标 IDs(班级或学生) */
|
||||||
|
targetIds?: number[];
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
|
|||||||
@ -16,6 +16,14 @@ export interface TaskUpdateRequest {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 任务类型 */
|
/** 任务类型 */
|
||||||
type?: string;
|
type?: string;
|
||||||
|
/** 任务类型(前端兼容 taskType) */
|
||||||
|
taskType?: string;
|
||||||
|
/** 关联绘本名称 */
|
||||||
|
relatedBookName?: string;
|
||||||
|
/** 课程 ID */
|
||||||
|
courseId?: number;
|
||||||
|
/** 课程 ID(前端兼容 relatedCourseId) */
|
||||||
|
relatedCourseId?: number;
|
||||||
/** 开始日期 */
|
/** 开始日期 */
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
/** 截止日期 */
|
/** 截止日期 */
|
||||||
@ -24,4 +32,8 @@ export interface TaskUpdateRequest {
|
|||||||
status?: string;
|
status?: string;
|
||||||
/** 附件(JSON 数组) */
|
/** 附件(JSON 数组) */
|
||||||
attachments?: string;
|
attachments?: string;
|
||||||
|
/** 目标类型:CLASS-班级,STUDENT-学生 */
|
||||||
|
targetType?: string;
|
||||||
|
/** 目标 IDs */
|
||||||
|
targetIds?: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v8.5.3 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* Reading Platform API
|
||||||
|
* Reading Platform Backend Service API Documentation
|
||||||
|
* OpenAPI spec version: 1.0.0
|
||||||
|
*/
|
||||||
|
import type { TaskFeedbackResponse } from './taskFeedbackResponse';
|
||||||
|
import type { TaskInfo } from './taskInfo';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端任务列表响应(含完成信息)
|
||||||
|
*/
|
||||||
|
export interface TaskWithCompletionResponse {
|
||||||
|
/** 任务ID */
|
||||||
|
id?: number;
|
||||||
|
/** 完成状态:PENDING-待提交/SUBMITTED-已提交/REVIEWED-已评价 */
|
||||||
|
status?: string;
|
||||||
|
/** 提交时间 */
|
||||||
|
submittedAt?: string;
|
||||||
|
/** 评价时间 */
|
||||||
|
reviewedAt?: string;
|
||||||
|
/** 照片URL列表 */
|
||||||
|
photos?: string[];
|
||||||
|
/** 视频URL */
|
||||||
|
videoUrl?: string;
|
||||||
|
/** 语音URL */
|
||||||
|
audioUrl?: string;
|
||||||
|
/** 阅读心得/完成内容 */
|
||||||
|
content?: string;
|
||||||
|
teacherFeedback?: TaskFeedbackResponse;
|
||||||
|
task?: TaskInfo;
|
||||||
|
}
|
||||||
@ -92,6 +92,7 @@ export interface TaskSubmitRequest {
|
|||||||
// 带完成信息的任务(兼容旧接口)
|
// 带完成信息的任务(兼容旧接口)
|
||||||
export interface TaskWithCompletion {
|
export interface TaskWithCompletion {
|
||||||
id: number;
|
id: number;
|
||||||
|
studentId?: number; // 学生ID,多孩子聚合时用于标识任务归属
|
||||||
status: TaskCompletionStatus;
|
status: TaskCompletionStatus;
|
||||||
completedAt?: string;
|
completedAt?: string;
|
||||||
feedback?: string;
|
feedback?: string;
|
||||||
@ -154,23 +155,41 @@ export const getChildProfile = (childId: number): Promise<ChildProfile> =>
|
|||||||
|
|
||||||
export const getChildLessons = (
|
export const getChildLessons = (
|
||||||
childId: number,
|
childId: number,
|
||||||
params?: { pageNum?: number; pageSize?: number }
|
params?: { pageNum?: number; pageSize?: number; page?: number }
|
||||||
): Promise<{ items: LessonRecord[]; total: number; page: number; pageSize: number }> =>
|
): Promise<{ items: LessonRecord[]; total: number; page: number; pageSize: number }> =>
|
||||||
http.get(`/v1/parent/children/${childId}/lessons`, { params });
|
http.get<{ list: LessonRecord[]; total: number; pageNum: number; pageSize: number }>(`/v1/parent/children/${childId}/lessons`, {
|
||||||
|
params: { pageNum: params?.pageNum ?? params?.page ?? 1, pageSize: params?.pageSize ?? 10 },
|
||||||
|
}).then(res => ({
|
||||||
|
items: res.list || [],
|
||||||
|
total: res.total || 0,
|
||||||
|
page: res.pageNum || 1,
|
||||||
|
pageSize: res.pageSize || 10,
|
||||||
|
}));
|
||||||
|
|
||||||
// ==================== 任务 API ====================
|
// ==================== 任务 API ====================
|
||||||
|
|
||||||
|
/** 获取我的任务列表(聚合多孩子任务,无 childId 时使用) */
|
||||||
|
export const getMyTasks = (
|
||||||
|
params?: { pageNum?: number; pageSize?: number; status?: string }
|
||||||
|
): Promise<{ items: TaskWithCompletion[]; total: number; page: number; pageSize: number }> =>
|
||||||
|
http.get<{ list: TaskWithCompletion[]; total: number; pageNum: number; pageSize: number }>('/v1/parent/tasks', { params }).then(res => ({
|
||||||
|
items: res.list || [],
|
||||||
|
total: res.total || 0,
|
||||||
|
page: res.pageNum || 1,
|
||||||
|
pageSize: res.pageSize || 10,
|
||||||
|
}));
|
||||||
|
|
||||||
|
/** 获取指定孩子的任务列表 */
|
||||||
export const getChildTasks = (
|
export const getChildTasks = (
|
||||||
childId: number,
|
childId: number,
|
||||||
params?: { pageNum?: number; pageSize?: number; status?: string }
|
params?: { pageNum?: number; pageSize?: number; status?: string }
|
||||||
): Promise<{ items: TaskWithCompletion[]; total: number; page: number; pageSize: number }> =>
|
): Promise<{ items: TaskWithCompletion[]; total: number; page: number; pageSize: number }> =>
|
||||||
http.get<{ list: TaskWithCompletion[]; total: number; pageNum: number; pageSize: number }>(`/v1/parent/tasks/student/${childId}`, { params })
|
http.get<{ list: TaskWithCompletion[]; total: number; pageNum: number; pageSize: number }>(`/v1/parent/tasks/student/${childId}`, { params }).then(res => ({
|
||||||
.then(res => ({
|
items: res.list || [],
|
||||||
items: res.list || [],
|
total: res.total || 0,
|
||||||
total: res.total || 0,
|
page: res.pageNum || 1,
|
||||||
page: res.pageNum || 1,
|
pageSize: res.pageSize || 10,
|
||||||
pageSize: res.pageSize || 10,
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
// 提交任务完成(新版)
|
// 提交任务完成(新版)
|
||||||
export const submitTaskCompletion = (
|
export const submitTaskCompletion = (
|
||||||
@ -233,8 +252,18 @@ export const getNotifications = (
|
|||||||
unreadCount: number;
|
unreadCount: number;
|
||||||
page: number;
|
page: number;
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
}> =>
|
}> => {
|
||||||
http.get<{ list: Notification[]; total: number; pageNum: number; pageSize: number }>('/v1/parent/notifications', { params })
|
const queryParams: Record<string, any> = {
|
||||||
|
pageNum: params?.pageNum ?? 1,
|
||||||
|
pageSize: params?.pageSize ?? 10,
|
||||||
|
};
|
||||||
|
if (params?.isRead !== undefined) {
|
||||||
|
queryParams.isRead = params.isRead ? 1 : 0;
|
||||||
|
}
|
||||||
|
if (params?.notificationType) {
|
||||||
|
queryParams.notificationType = params.notificationType;
|
||||||
|
}
|
||||||
|
return http.get<{ list: Notification[]; total: number; pageNum: number; pageSize: number }>('/v1/parent/notifications', { params: queryParams })
|
||||||
.then(res => ({
|
.then(res => ({
|
||||||
items: res.list || [],
|
items: res.list || [],
|
||||||
total: res.total || 0,
|
total: res.total || 0,
|
||||||
@ -242,6 +271,7 @@ export const getNotifications = (
|
|||||||
page: res.pageNum || 1,
|
page: res.pageNum || 1,
|
||||||
pageSize: res.pageSize || 10,
|
pageSize: res.pageSize || 10,
|
||||||
}));
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
export const getUnreadCount = (): Promise<number> =>
|
export const getUnreadCount = (): Promise<number> =>
|
||||||
http.get<number>('/v1/parent/notifications/unread-count').then(res => res || 0);
|
http.get<number>('/v1/parent/notifications/unread-count').then(res => res || 0);
|
||||||
|
|||||||
@ -1273,8 +1273,13 @@ export const getParents = (params?: ParentQueryParams) =>
|
|||||||
export const getParent = (id: number) =>
|
export const getParent = (id: number) =>
|
||||||
http.get<Parent>(`/v1/school/parents/${id}`);
|
http.get<Parent>(`/v1/school/parents/${id}`);
|
||||||
|
|
||||||
export const createParent = (data: CreateParentDto) =>
|
export const createParent = (data: CreateParentDto) => {
|
||||||
http.post<Parent>('/v1/school/parents', data);
|
const { loginAccount, ...rest } = data;
|
||||||
|
return http.post<Parent>('/v1/school/parents', {
|
||||||
|
...rest,
|
||||||
|
username: loginAccount,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const updateParent = (id: number, data: UpdateParentDto) =>
|
export const updateParent = (id: number, data: UpdateParentDto) =>
|
||||||
http.put<Parent>(`/v1/school/parents/${id}`, data);
|
http.put<Parent>(`/v1/school/parents/${id}`, data);
|
||||||
@ -1286,8 +1291,8 @@ export const resetParentPassword = (id: number) =>
|
|||||||
http.post<{ tempPassword: string }>(`/v1/school/parents/${id}/reset-password`);
|
http.post<{ tempPassword: string }>(`/v1/school/parents/${id}/reset-password`);
|
||||||
|
|
||||||
export const getParentChildren = async (parentId: number): Promise<ParentChild[]> => {
|
export const getParentChildren = async (parentId: number): Promise<ParentChild[]> => {
|
||||||
const parent = await http.get<Parent & { children: ParentChild[] }>(`/v1/school/parents/${parentId}`);
|
const list = await http.get<ParentChild[]>(`/v1/school/parents/${parentId}/children`);
|
||||||
return parent.children || [];
|
return Array.isArray(list) ? list : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addChildToParent = (parentId: number, data: AddChildDto) =>
|
export const addChildToParent = (parentId: number, data: AddChildDto) =>
|
||||||
|
|||||||
@ -122,6 +122,7 @@ export function getTeacherStudents(params?: { pageNum?: number; pageSize?: numbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取班级学生列表
|
// 获取班级学生列表
|
||||||
|
// 后端返回 { list, total, pageNum, pageSize, classInfo },前端统一为 { items, total, page, pageSize, class }
|
||||||
export function getTeacherClassStudents(classId: number, params?: { pageNum?: number; pageSize?: number; keyword?: string }): Promise<{
|
export function getTeacherClassStudents(classId: number, params?: { pageNum?: number; pageSize?: number; keyword?: string }): Promise<{
|
||||||
items: Array<{
|
items: Array<{
|
||||||
id: number;
|
id: number;
|
||||||
@ -151,7 +152,13 @@ export function getTeacherClassStudents(classId: number, params?: { pageNum?: nu
|
|||||||
pageSize: params?.pageSize,
|
pageSize: params?.pageSize,
|
||||||
keyword: params?.keyword,
|
keyword: params?.keyword,
|
||||||
},
|
},
|
||||||
}) as any;
|
}).then((res: any) => ({
|
||||||
|
items: res?.list ?? res?.items ?? [],
|
||||||
|
total: res?.total ?? 0,
|
||||||
|
page: res?.pageNum ?? res?.page ?? 1,
|
||||||
|
pageSize: res?.pageSize ?? 10,
|
||||||
|
class: res?.classInfo ?? res?.class,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取班级教师列表
|
// 获取班级教师列表
|
||||||
|
|||||||
@ -11,9 +11,15 @@
|
|||||||
<p>关注孩子的阅读成长,陪伴每一步进步!</p>
|
<p>关注孩子的阅读成长,陪伴每一步进步!</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="banner-decorations">
|
<div class="banner-decorations">
|
||||||
<span class="decoration"><BookOutlined /></span>
|
<span class="decoration">
|
||||||
<span class="decoration"><StarOutlined /></span>
|
<BookOutlined />
|
||||||
<span class="decoration"><HeartOutlined /></span>
|
</span>
|
||||||
|
<span class="decoration">
|
||||||
|
<StarOutlined />
|
||||||
|
</span>
|
||||||
|
<span class="decoration">
|
||||||
|
<HeartOutlined />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -22,15 +28,12 @@
|
|||||||
<!-- 我的孩子 -->
|
<!-- 我的孩子 -->
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3><TeamOutlined /> 我的孩子</h3>
|
<h3>
|
||||||
|
<TeamOutlined /> 我的孩子
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="children-grid" v-if="children.length > 0">
|
<div class="children-grid" v-if="children.length > 0">
|
||||||
<div
|
<div v-for="child in children" :key="child.id" class="child-card" @click="goToChildDetail(child.id)">
|
||||||
v-for="child in children"
|
|
||||||
:key="child.id"
|
|
||||||
class="child-card"
|
|
||||||
@click="goToChildDetail(child.id)"
|
|
||||||
>
|
|
||||||
<div class="child-avatar">
|
<div class="child-avatar">
|
||||||
<a-avatar :size="isMobile ? 56 : 64" :style="{ backgroundColor: getAvatarColor(child.id) }">
|
<a-avatar :size="isMobile ? 56 : 64" :style="{ backgroundColor: getAvatarColor(child.id) }">
|
||||||
{{ child.name.charAt(0) }}
|
{{ child.name.charAt(0) }}
|
||||||
@ -43,8 +46,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="child-class">{{ child.class?.name || '未分班' }}</div>
|
<div class="child-class">{{ child.class?.name || '未分班' }}</div>
|
||||||
<div class="child-stats">
|
<div class="child-stats">
|
||||||
<span><BookOutlined /> {{ child.readingCount }} 次阅读</span>
|
<span>
|
||||||
<span><ReadOutlined /> {{ child.lessonCount }} 节课</span>
|
<BookOutlined /> {{ child.readingCount }} 次阅读
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<ReadOutlined /> {{ child.lessonCount }} 节课
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-arrow">
|
<div class="card-arrow">
|
||||||
@ -62,11 +69,13 @@
|
|||||||
<!-- 最近任务 -->
|
<!-- 最近任务 -->
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3><CheckSquareOutlined /> 最近任务</h3>
|
<h3>
|
||||||
|
<CheckSquareOutlined /> 最近任务
|
||||||
|
</h3>
|
||||||
<a-button type="link" @click="goToTasks" class="view-all-btn">查看全部</a-button>
|
<a-button type="link" @click="goToTasks" class="view-all-btn">查看全部</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="task-list" v-if="recentTasks.length > 0">
|
<div class="task-list" v-if="recentTasks.length > 0">
|
||||||
<div v-for="task in recentTasks" :key="task.id" class="task-item">
|
<div v-for="task in recentTasks" :key="`${task.id}-${task.studentId ?? ''}`" class="task-item">
|
||||||
<div class="task-status" :class="getStatusClass(task.status)">
|
<div class="task-status" :class="getStatusClass(task.status)">
|
||||||
{{ getStatusText(task.status) }}
|
{{ getStatusText(task.status) }}
|
||||||
</div>
|
</div>
|
||||||
@ -87,7 +96,9 @@
|
|||||||
<!-- 成长档案 -->
|
<!-- 成长档案 -->
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3><FileImageOutlined /> 成长档案</h3>
|
<h3>
|
||||||
|
<FileImageOutlined /> 成长档案
|
||||||
|
</h3>
|
||||||
<a-button type="link" @click="goToGrowth" class="view-all-btn">查看全部</a-button>
|
<a-button type="link" @click="goToGrowth" class="view-all-btn">查看全部</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="growth-list" v-if="recentGrowth.length > 0">
|
<div class="growth-list" v-if="recentGrowth.length > 0">
|
||||||
@ -126,7 +137,7 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
InboxOutlined,
|
InboxOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { getChildren, type ChildInfo } from '@/api/parent';
|
import { getChildren, getMyTasks, getChildGrowthRecords, type ChildInfo, type TaskWithCompletion } from '@/api/parent';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -153,8 +164,8 @@ const getRelationshipText = (relationship: string) => {
|
|||||||
|
|
||||||
const statusMap: Record<string, { text: string; class: string }> = {
|
const statusMap: Record<string, { text: string; class: string }> = {
|
||||||
PENDING: { text: '待完成', class: 'status-pending' },
|
PENDING: { text: '待完成', class: 'status-pending' },
|
||||||
IN_PROGRESS: { text: '进行中', class: 'status-progress' },
|
SUBMITTED: { text: '已提交', class: 'status-progress' },
|
||||||
COMPLETED: { text: '已完成', class: 'status-completed' },
|
REVIEWED: { text: '已评价', class: 'status-completed' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusText = (status: string) => statusMap[status]?.text || status;
|
const getStatusText = (status: string) => statusMap[status]?.text || status;
|
||||||
@ -171,11 +182,13 @@ const goToChildDetail = (childId: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const goToTasks = () => {
|
const goToTasks = () => {
|
||||||
router.push('/parent/tasks');
|
router.push('/parent/tasks'); // 任务页支持聚合视图,无需 childId
|
||||||
};
|
};
|
||||||
|
|
||||||
const goToGrowth = () => {
|
const goToGrowth = () => {
|
||||||
router.push('/parent/growth');
|
// 成长档案需要指定孩子,有孩子时传第一个
|
||||||
|
const childId = children.value[0]?.id;
|
||||||
|
router.push(childId ? `/parent/growth?childId=${childId}` : '/parent/growth');
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
@ -184,9 +197,16 @@ const loadData = async () => {
|
|||||||
const data = await getChildren();
|
const data = await getChildren();
|
||||||
children.value = data;
|
children.value = data;
|
||||||
|
|
||||||
// 如果有孩子,加载第一个孩子的任务和成长记录
|
// 加载最近任务(聚合多孩子)
|
||||||
|
const tasksRes = await getMyTasks({ pageNum: 1, pageSize: 5 });
|
||||||
|
recentTasks.value = tasksRes.items || [];
|
||||||
|
|
||||||
|
// 加载第一个孩子的最近成长档案
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
// 这里可以加载任务和成长记录
|
const growthRes = await getChildGrowthRecords(data[0].id, { pageNum: 1, pageSize: 4 });
|
||||||
|
recentGrowth.value = growthRes.items || [];
|
||||||
|
} else {
|
||||||
|
recentGrowth.value = [];
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.response?.data?.message || '加载数据失败');
|
message.error(error.response?.data?.message || '加载数据失败');
|
||||||
@ -212,6 +232,7 @@ $primary-light: #f6ffed;
|
|||||||
$primary-dark: #389e0d;
|
$primary-dark: #389e0d;
|
||||||
|
|
||||||
.parent-dashboard {
|
.parent-dashboard {
|
||||||
|
|
||||||
// 欢迎横幅
|
// 欢迎横幅
|
||||||
.welcome-banner {
|
.welcome-banner {
|
||||||
background: linear-gradient(135deg, $primary-color 0%, #73d13d 100%);
|
background: linear-gradient(135deg, $primary-color 0%, #73d13d 100%);
|
||||||
@ -550,6 +571,7 @@ $primary-dark: #389e0d;
|
|||||||
|
|
||||||
.task-content,
|
.task-content,
|
||||||
.growth-content {
|
.growth-content {
|
||||||
|
|
||||||
.task-title,
|
.task-title,
|
||||||
.growth-title {
|
.growth-title {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@ -1,14 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-layout :class="['parent-layout', { 'is-collapsed': collapsed }]">
|
<a-layout :class="['parent-layout', { 'is-collapsed': collapsed }]">
|
||||||
<!-- 桌面端侧边栏 -->
|
<!-- 桌面端侧边栏 -->
|
||||||
<a-layout-sider
|
<a-layout-sider v-if="!isMobile" v-model:collapsed="collapsed" :trigger="null" collapsible class="parent-sider"
|
||||||
v-if="!isMobile"
|
:width="220">
|
||||||
v-model:collapsed="collapsed"
|
|
||||||
:trigger="null"
|
|
||||||
collapsible
|
|
||||||
class="parent-sider"
|
|
||||||
:width="220"
|
|
||||||
>
|
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="/logo.png" alt="Logo" class="logo-img" />
|
<img src="/logo.png" alt="Logo" class="logo-img" />
|
||||||
<div v-if="!collapsed" class="logo-text">
|
<div v-if="!collapsed" class="logo-text">
|
||||||
@ -18,47 +12,45 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sider-menu-wrapper">
|
<div class="sider-menu-wrapper">
|
||||||
<a-menu
|
<a-menu v-model:selectedKeys="selectedKeys" mode="inline" theme="light" :inline-collapsed="collapsed"
|
||||||
v-model:selectedKeys="selectedKeys"
|
@click="handleMenuClick" class="side-menu">
|
||||||
mode="inline"
|
<a-menu-item key="dashboard">
|
||||||
theme="light"
|
<template #icon>
|
||||||
:inline-collapsed="collapsed"
|
<HomeOutlined />
|
||||||
@click="handleMenuClick"
|
</template>
|
||||||
class="side-menu"
|
<span>首页</span>
|
||||||
>
|
</a-menu-item>
|
||||||
<a-menu-item key="dashboard">
|
<a-menu-item key="children">
|
||||||
<template #icon><HomeOutlined /></template>
|
<template #icon>
|
||||||
<span>首页</span>
|
<TeamOutlined />
|
||||||
</a-menu-item>
|
</template>
|
||||||
<a-menu-item key="children">
|
<span>我的孩子</span>
|
||||||
<template #icon><TeamOutlined /></template>
|
</a-menu-item>
|
||||||
<span>我的孩子</span>
|
<a-menu-item key="lessons">
|
||||||
</a-menu-item>
|
<template #icon>
|
||||||
<a-menu-item key="lessons">
|
<BookOutlined />
|
||||||
<template #icon><BookOutlined /></template>
|
</template>
|
||||||
<span>阅读记录</span>
|
<span>阅读记录</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="tasks">
|
<a-menu-item key="tasks">
|
||||||
<template #icon><CheckSquareOutlined /></template>
|
<template #icon>
|
||||||
<span>阅读任务</span>
|
<CheckSquareOutlined />
|
||||||
</a-menu-item>
|
</template>
|
||||||
<a-menu-item key="growth">
|
<span>阅读任务</span>
|
||||||
<template #icon><FileImageOutlined /></template>
|
</a-menu-item>
|
||||||
<span>成长档案</span>
|
<a-menu-item key="growth">
|
||||||
</a-menu-item>
|
<template #icon>
|
||||||
</a-menu>
|
<FileImageOutlined />
|
||||||
|
</template>
|
||||||
|
<span>成长档案</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
</div>
|
</div>
|
||||||
</a-layout-sider>
|
</a-layout-sider>
|
||||||
|
|
||||||
<!-- 移动端抽屉菜单 -->
|
<!-- 移动端抽屉菜单 -->
|
||||||
<a-drawer
|
<a-drawer v-if="isMobile" v-model:open="drawerVisible" placement="left" :closable="false" :width="260"
|
||||||
v-if="isMobile"
|
class="mobile-drawer">
|
||||||
v-model:open="drawerVisible"
|
|
||||||
placement="left"
|
|
||||||
:closable="false"
|
|
||||||
:width="260"
|
|
||||||
class="mobile-drawer"
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="drawer-header">
|
<div class="drawer-header">
|
||||||
<img src="/logo.png" alt="Logo" class="drawer-logo" />
|
<img src="/logo.png" alt="Logo" class="drawer-logo" />
|
||||||
@ -68,35 +60,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-menu
|
<a-menu v-model:selectedKeys="selectedKeys" mode="inline" theme="light" @click="handleMobileMenuClick"
|
||||||
v-model:selectedKeys="selectedKeys"
|
class="drawer-menu">
|
||||||
mode="inline"
|
|
||||||
theme="light"
|
|
||||||
@click="handleMobileMenuClick"
|
|
||||||
class="drawer-menu"
|
|
||||||
>
|
|
||||||
<a-menu-item key="dashboard">
|
<a-menu-item key="dashboard">
|
||||||
<template #icon><HomeOutlined /></template>
|
<template #icon>
|
||||||
|
<HomeOutlined />
|
||||||
|
</template>
|
||||||
<span>首页</span>
|
<span>首页</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="children">
|
<a-menu-item key="children">
|
||||||
<template #icon><TeamOutlined /></template>
|
<template #icon>
|
||||||
|
<TeamOutlined />
|
||||||
|
</template>
|
||||||
<span>我的孩子</span>
|
<span>我的孩子</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="lessons">
|
<a-menu-item key="lessons">
|
||||||
<template #icon><BookOutlined /></template>
|
<template #icon>
|
||||||
|
<BookOutlined />
|
||||||
|
</template>
|
||||||
<span>阅读记录</span>
|
<span>阅读记录</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="tasks">
|
<a-menu-item key="tasks">
|
||||||
<template #icon><CheckSquareOutlined /></template>
|
<template #icon>
|
||||||
|
<CheckSquareOutlined />
|
||||||
|
</template>
|
||||||
<span>阅读任务</span>
|
<span>阅读任务</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="growth">
|
<a-menu-item key="growth">
|
||||||
<template #icon><FileImageOutlined /></template>
|
<template #icon>
|
||||||
|
<FileImageOutlined />
|
||||||
|
</template>
|
||||||
<span>成长档案</span>
|
<span>成长档案</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="profile">
|
<a-menu-item key="profile">
|
||||||
<template #icon><UserOutlined /></template>
|
<template #icon>
|
||||||
|
<UserOutlined />
|
||||||
|
</template>
|
||||||
<span>个人信息</span>
|
<span>个人信息</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@ -112,16 +111,8 @@
|
|||||||
<!-- 桌面端顶部栏 -->
|
<!-- 桌面端顶部栏 -->
|
||||||
<a-layout-header v-if="!isMobile" class="parent-header">
|
<a-layout-header v-if="!isMobile" class="parent-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<MenuUnfoldOutlined
|
<MenuUnfoldOutlined v-if="collapsed" class="trigger" @click="collapsed = !collapsed" />
|
||||||
v-if="collapsed"
|
<MenuFoldOutlined v-else class="trigger" @click="collapsed = !collapsed" />
|
||||||
class="trigger"
|
|
||||||
@click="collapsed = !collapsed"
|
|
||||||
/>
|
|
||||||
<MenuFoldOutlined
|
|
||||||
v-else
|
|
||||||
class="trigger"
|
|
||||||
@click="collapsed = !collapsed"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
@ -129,7 +120,9 @@
|
|||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<a-space class="user-info" style="cursor: pointer;">
|
<a-space class="user-info" style="cursor: pointer;">
|
||||||
<a-avatar :size="32" class="user-avatar">
|
<a-avatar :size="32" class="user-avatar">
|
||||||
<template #icon><UserOutlined /></template>
|
<template #icon>
|
||||||
|
<UserOutlined />
|
||||||
|
</template>
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
<span class="user-name">{{ userName }}</span>
|
<span class="user-name">{{ userName }}</span>
|
||||||
<DownOutlined />
|
<DownOutlined />
|
||||||
@ -166,12 +159,8 @@
|
|||||||
|
|
||||||
<!-- 移动端底部导航 -->
|
<!-- 移动端底部导航 -->
|
||||||
<div v-if="isMobile" class="mobile-bottom-nav">
|
<div v-if="isMobile" class="mobile-bottom-nav">
|
||||||
<div
|
<div v-for="nav in navItems" :key="nav.key" :class="['nav-item', { active: selectedKeys[0] === nav.key }]"
|
||||||
v-for="nav in navItems"
|
@click="handleNavClick(nav.key)">
|
||||||
:key="nav.key"
|
|
||||||
:class="['nav-item', { active: selectedKeys[0] === nav.key }]"
|
|
||||||
@click="handleNavClick(nav.key)"
|
|
||||||
>
|
|
||||||
<component :is="nav.icon" class="nav-icon" />
|
<component :is="nav.icon" class="nav-icon" />
|
||||||
<span class="nav-text">{{ nav.text }}</span>
|
<span class="nav-text">{{ nav.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="growth-record-view">
|
<div class="growth-record-view">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2><FileImageOutlined /> 成长档案</h2>
|
<div class="header-left">
|
||||||
<p class="page-desc">记录孩子成长的每一个精彩瞬间</p>
|
<h2>
|
||||||
|
<FileImageOutlined /> 成长档案
|
||||||
|
</h2>
|
||||||
|
<p class="page-desc">记录孩子成长的每一个精彩瞬间</p>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-if="children.length > 1"
|
||||||
|
v-model:value="selectedChildId"
|
||||||
|
placeholder="选择孩子"
|
||||||
|
style="width: 140px;"
|
||||||
|
@change="onChildChange"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="c in children" :key="c.id" :value="c.id">
|
||||||
|
{{ c.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
@ -22,7 +37,9 @@
|
|||||||
<h3>{{ record.title }}</h3>
|
<h3>{{ record.title }}</h3>
|
||||||
<p v-if="record.content">{{ record.content }}</p>
|
<p v-if="record.content">{{ record.content }}</p>
|
||||||
<div class="card-meta">
|
<div class="card-meta">
|
||||||
<span><CalendarOutlined /> {{ formatDate(record.recordDate) }}</span>
|
<span>
|
||||||
|
<CalendarOutlined /> {{ formatDate(record.recordDate) }}
|
||||||
|
</span>
|
||||||
<span v-if="record.class">
|
<span v-if="record.class">
|
||||||
<TeamOutlined /> {{ record.class.name }}
|
<TeamOutlined /> {{ record.class.name }}
|
||||||
</span>
|
</span>
|
||||||
@ -38,18 +55,13 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
<div class="pagination-section" v-if="total > pageSize">
|
<div class="pagination-section" v-if="total > pageSize">
|
||||||
<a-pagination
|
<a-pagination v-model:current="currentPage" :total="total" :page-size="pageSize" @change="onPageChange" />
|
||||||
v-model:current="currentPage"
|
|
||||||
:total="total"
|
|
||||||
:page-size="pageSize"
|
|
||||||
@change="onPageChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
@ -59,7 +71,7 @@ import {
|
|||||||
TeamOutlined,
|
TeamOutlined,
|
||||||
InboxOutlined,
|
InboxOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { getChildGrowthRecords, getChildren, type GrowthRecord } from '@/api/parent';
|
import { getChildGrowthRecords, getChildren, type GrowthRecord, type ChildInfo } from '@/api/parent';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -69,7 +81,9 @@ const records = ref<GrowthRecord[]>([]);
|
|||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const pageSize = ref(12);
|
const pageSize = ref(12);
|
||||||
|
const children = ref<ChildInfo[]>([]);
|
||||||
const currentChildId = ref<number | null>(null);
|
const currentChildId = ref<number | null>(null);
|
||||||
|
const selectedChildId = ref<number | null>(null);
|
||||||
|
|
||||||
const formatDate = (date: string) => dayjs(date).format('YYYY-MM-DD');
|
const formatDate = (date: string) => dayjs(date).format('YYYY-MM-DD');
|
||||||
|
|
||||||
@ -78,25 +92,32 @@ const onPageChange = (page: number) => {
|
|||||||
loadRecords();
|
loadRecords();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChildChange = (val: number) => {
|
||||||
|
router.replace({ query: { childId: String(val) } });
|
||||||
|
currentPage.value = 1;
|
||||||
|
loadRecords();
|
||||||
|
};
|
||||||
|
|
||||||
const loadRecords = async () => {
|
const loadRecords = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
let childId = route.query.childId ? Number(route.query.childId) : null;
|
let childId = route.query.childId ? Number(route.query.childId) : null;
|
||||||
|
|
||||||
// 如果没有指定childId,自动获取第一个孩子
|
// 加载孩子列表
|
||||||
if (!childId) {
|
children.value = await getChildren();
|
||||||
const children = await getChildren();
|
if (children.value.length === 0) {
|
||||||
if (children && children.length > 0) {
|
records.value = [];
|
||||||
childId = children[0].id;
|
total.value = 0;
|
||||||
router.replace({ query: { childId: String(childId) } });
|
loading.value = false;
|
||||||
} else {
|
return;
|
||||||
records.value = [];
|
|
||||||
total.value = 0;
|
|
||||||
loading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果没有指定 childId,使用第一个孩子
|
||||||
|
if (!childId) {
|
||||||
|
childId = children.value[0].id;
|
||||||
|
router.replace({ query: { childId: String(childId) } });
|
||||||
|
}
|
||||||
|
selectedChildId.value = childId;
|
||||||
currentChildId.value = childId;
|
currentChildId.value = childId;
|
||||||
|
|
||||||
const data = await getChildGrowthRecords(childId, {
|
const data = await getChildGrowthRecords(childId, {
|
||||||
@ -115,6 +136,8 @@ const loadRecords = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => route.query.childId, () => loadRecords());
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadRecords();
|
loadRecords();
|
||||||
});
|
});
|
||||||
@ -127,6 +150,15 @@ $primary-light: #f6ffed;
|
|||||||
.growth-record-view {
|
.growth-record-view {
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
|
|||||||
@ -1,8 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="lesson-history-view">
|
<div class="lesson-history-view">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2><BookOutlined /> 阅读记录</h2>
|
<div class="header-left">
|
||||||
<p class="page-desc">查看孩子的阅读学习记录</p>
|
<h2>
|
||||||
|
<BookOutlined /> 阅读记录
|
||||||
|
</h2>
|
||||||
|
<p class="page-desc">查看孩子的阅读学习记录</p>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-if="children.length > 1"
|
||||||
|
v-model:value="selectedChildId"
|
||||||
|
placeholder="选择孩子"
|
||||||
|
style="width: 140px;"
|
||||||
|
@change="onChildChange"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="c in children" :key="c.id" :value="c.id">
|
||||||
|
{{ c.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
@ -61,18 +76,13 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
<div class="pagination-section" v-if="total > pageSize">
|
<div class="pagination-section" v-if="total > pageSize">
|
||||||
<a-pagination
|
<a-pagination v-model:current="currentPage" :total="total" :page-size="pageSize" @change="onPageChange" />
|
||||||
v-model:current="currentPage"
|
|
||||||
:total="total"
|
|
||||||
:page-size="pageSize"
|
|
||||||
@change="onPageChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
@ -82,7 +92,7 @@ import {
|
|||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
InboxOutlined,
|
InboxOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { getChildLessons, getChildren, type LessonRecord } from '@/api/parent';
|
import { getChildLessons, getChildren, type LessonRecord, type ChildInfo } from '@/api/parent';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -92,7 +102,9 @@ const records = ref<LessonRecord[]>([]);
|
|||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const pageSize = ref(10);
|
const pageSize = ref(10);
|
||||||
|
const children = ref<ChildInfo[]>([]);
|
||||||
const currentChildId = ref<number | null>(null);
|
const currentChildId = ref<number | null>(null);
|
||||||
|
const selectedChildId = ref<number | null>(null);
|
||||||
|
|
||||||
const formatDateTime = (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm');
|
const formatDateTime = (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm');
|
||||||
|
|
||||||
@ -101,23 +113,32 @@ const onPageChange = (page: number) => {
|
|||||||
loadRecords();
|
loadRecords();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onChildChange = (val: number) => {
|
||||||
|
router.replace({ query: { childId: String(val) } });
|
||||||
|
currentPage.value = 1;
|
||||||
|
loadRecords();
|
||||||
|
};
|
||||||
|
|
||||||
const loadRecords = async () => {
|
const loadRecords = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
let childId = route.query.childId ? Number(route.query.childId) : null;
|
let childId = route.query.childId ? Number(route.query.childId) : null;
|
||||||
|
|
||||||
// 如果没有指定childId,自动获取第一个孩子
|
// 加载孩子列表
|
||||||
if (!childId) {
|
children.value = await getChildren();
|
||||||
const children = await getChildren();
|
if (children.value.length === 0) {
|
||||||
if (children && children.length > 0) {
|
records.value = [];
|
||||||
childId = children[0].id;
|
total.value = 0;
|
||||||
router.replace({ query: { childId: String(childId) } });
|
loading.value = false;
|
||||||
} else {
|
return;
|
||||||
loading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果没有指定 childId,使用第一个孩子
|
||||||
|
if (!childId) {
|
||||||
|
childId = children.value[0].id;
|
||||||
|
router.replace({ query: { childId: String(childId) } });
|
||||||
|
}
|
||||||
|
selectedChildId.value = childId;
|
||||||
currentChildId.value = childId;
|
currentChildId.value = childId;
|
||||||
|
|
||||||
const data = await getChildLessons(childId, {
|
const data = await getChildLessons(childId, {
|
||||||
@ -133,6 +154,8 @@ const loadRecords = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => route.query.childId, () => loadRecords());
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadRecords();
|
loadRecords();
|
||||||
});
|
});
|
||||||
@ -142,6 +165,15 @@ onMounted(() => {
|
|||||||
.lesson-history-view {
|
.lesson-history-view {
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
|
|||||||
@ -1,13 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="task-list-view">
|
<div class="task-list-view">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2><CheckSquareOutlined /> 阅读任务</h2>
|
<div class="header-left">
|
||||||
<p class="page-desc">查看孩子的阅读任务并提交完成情况</p>
|
<h2>
|
||||||
|
<CheckSquareOutlined /> 阅读任务
|
||||||
|
</h2>
|
||||||
|
<p class="page-desc">查看孩子的阅读任务并提交完成情况</p>
|
||||||
|
</div>
|
||||||
|
<a-select
|
||||||
|
v-if="children.length > 1"
|
||||||
|
v-model:value="selectedChildId"
|
||||||
|
placeholder="选择孩子"
|
||||||
|
style="width: 140px;"
|
||||||
|
allow-clear
|
||||||
|
@change="onChildChange"
|
||||||
|
>
|
||||||
|
<a-select-option :value="null">全部孩子</a-select-option>
|
||||||
|
<a-select-option v-for="c in children" :key="c.id" :value="c.id">
|
||||||
|
{{ c.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<div class="task-list" v-if="tasks.length > 0">
|
<div class="task-list" v-if="tasks.length > 0">
|
||||||
<div v-for="task in tasks" :key="task.id" class="task-card">
|
<div v-for="task in tasks" :key="`${task.id}-${task.studentId ?? ''}`" class="task-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="task-info">
|
<div class="task-info">
|
||||||
<div class="task-title-row">
|
<div class="task-title-row">
|
||||||
@ -36,7 +53,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 已提交内容预览 -->
|
<!-- 已提交内容预览 -->
|
||||||
<div class="submission-preview" v-if="task.status !== 'PENDING' && (task.photos?.length || task.videoUrl || task.audioUrl || task.content)">
|
<div class="submission-preview"
|
||||||
|
v-if="task.status !== 'PENDING' && (task.photos?.length || task.videoUrl || task.audioUrl || task.content)">
|
||||||
<div class="preview-title">已提交内容:</div>
|
<div class="preview-title">已提交内容:</div>
|
||||||
<div class="preview-items">
|
<div class="preview-items">
|
||||||
<span v-if="task.photos?.length" class="preview-item">
|
<span v-if="task.photos?.length" class="preview-item">
|
||||||
@ -108,15 +126,8 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
<!-- 提交任务弹窗 -->
|
<!-- 提交任务弹窗 -->
|
||||||
<a-modal
|
<a-modal v-model:open="submitModalVisible" :title="isEditSubmit ? '修改提交' : '提交任务完成'" @ok="handleSubmit"
|
||||||
v-model:open="submitModalVisible"
|
:confirm-loading="submitting" width="600px" okText="提交" cancelText="取消">
|
||||||
:title="isEditSubmit ? '修改提交' : '提交任务完成'"
|
|
||||||
@ok="handleSubmit"
|
|
||||||
:confirm-loading="submitting"
|
|
||||||
width="600px"
|
|
||||||
okText="提交"
|
|
||||||
cancelText="取消"
|
|
||||||
>
|
|
||||||
<div class="task-info-header" v-if="selectedTask">
|
<div class="task-info-header" v-if="selectedTask">
|
||||||
<h4>{{ selectedTask.task.title }}</h4>
|
<h4>{{ selectedTask.task.title }}</h4>
|
||||||
<p v-if="selectedTask.task.relatedBookName">
|
<p v-if="selectedTask.task.relatedBookName">
|
||||||
@ -135,12 +146,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-upload
|
<a-upload :customRequest="handlePhotoUpload" :showUploadList="false" accept="image/*" multiple>
|
||||||
:customRequest="handlePhotoUpload"
|
|
||||||
:showUploadList="false"
|
|
||||||
accept="image/*"
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<div class="upload-btn">
|
<div class="upload-btn">
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
<span>添加照片</span>
|
<span>添加照片</span>
|
||||||
@ -151,40 +157,22 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="视频链接">
|
<a-form-item label="视频链接">
|
||||||
<a-input
|
<a-input v-model:value="submitForm.videoUrl" placeholder="请输入视频链接(可选,如腾讯视频、优酷等分享链接)" allow-clear />
|
||||||
v-model:value="submitForm.videoUrl"
|
|
||||||
placeholder="请输入视频链接(可选,如腾讯视频、优酷等分享链接)"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="音频链接">
|
<a-form-item label="音频链接">
|
||||||
<a-input
|
<a-input v-model:value="submitForm.audioUrl" placeholder="请输入音频链接(可选)" allow-clear />
|
||||||
v-model:value="submitForm.audioUrl"
|
|
||||||
placeholder="请输入音频链接(可选)"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="阅读心得">
|
<a-form-item label="阅读心得">
|
||||||
<a-textarea
|
<a-textarea v-model:value="submitForm.content" placeholder="请描述孩子的阅读情况、感受等(可选)" :rows="4" :maxlength="500"
|
||||||
v-model:value="submitForm.content"
|
show-count />
|
||||||
placeholder="请描述孩子的阅读情况、感受等(可选)"
|
|
||||||
:rows="4"
|
|
||||||
:maxlength="500"
|
|
||||||
show-count
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- 评价详情弹窗 -->
|
<!-- 评价详情弹窗 -->
|
||||||
<a-modal
|
<a-modal v-model:open="feedbackModalVisible" title="教师评价详情" :footer="null" width="500px">
|
||||||
v-model:open="feedbackModalVisible"
|
|
||||||
title="教师评价详情"
|
|
||||||
:footer="null"
|
|
||||||
width="500px"
|
|
||||||
>
|
|
||||||
<div class="feedback-detail" v-if="selectedTask?.teacherFeedback">
|
<div class="feedback-detail" v-if="selectedTask?.teacherFeedback">
|
||||||
<div class="feedback-result-section">
|
<div class="feedback-result-section">
|
||||||
<div class="result-label">评价结果</div>
|
<div class="result-label">评价结果</div>
|
||||||
@ -214,7 +202,8 @@
|
|||||||
<a-divider>提交内容</a-divider>
|
<a-divider>提交内容</a-divider>
|
||||||
<div class="review-photos" v-if="selectedTask.photos?.length">
|
<div class="review-photos" v-if="selectedTask.photos?.length">
|
||||||
<div class="photos-grid">
|
<div class="photos-grid">
|
||||||
<img v-for="(photo, idx) in selectedTask.photos" :key="idx" :src="photo" alt="照片" @click="previewImage(photo)" />
|
<img v-for="(photo, idx) in selectedTask.photos" :key="idx" :src="photo" alt="照片"
|
||||||
|
@click="previewImage(photo)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="review-links" v-if="selectedTask.videoUrl || selectedTask.audioUrl">
|
<div class="review-links" v-if="selectedTask.videoUrl || selectedTask.audioUrl">
|
||||||
@ -233,19 +222,15 @@
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- 图片预览 -->
|
<!-- 图片预览 -->
|
||||||
<a-image
|
<a-image :style="{ display: 'none' }" :preview="{
|
||||||
:style="{ display: 'none' }"
|
visible: imagePreviewVisible,
|
||||||
:preview="{
|
onVisibleChange: (visible: boolean) => { imagePreviewVisible = visible; },
|
||||||
visible: imagePreviewVisible,
|
}" :src="previewImageUrl" />
|
||||||
onVisibleChange: (visible: boolean) => { imagePreviewVisible = visible; },
|
|
||||||
}"
|
|
||||||
:src="previewImageUrl"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue';
|
import { ref, reactive, onMounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { message, Upload } from 'ant-design-vue';
|
import { message, Upload } from 'ant-design-vue';
|
||||||
import type { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
|
import type { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
|
||||||
@ -267,10 +252,13 @@ import {
|
|||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import {
|
import {
|
||||||
getChildTasks,
|
getChildTasks,
|
||||||
|
getMyTasks,
|
||||||
|
getChildren,
|
||||||
submitTaskCompletion,
|
submitTaskCompletion,
|
||||||
updateTaskCompletion,
|
updateTaskCompletion,
|
||||||
type TaskWithCompletion,
|
type TaskWithCompletion,
|
||||||
type TaskSubmitRequest,
|
type TaskSubmitRequest,
|
||||||
|
type ChildInfo,
|
||||||
} from '@/api/parent';
|
} from '@/api/parent';
|
||||||
import { uploadFile } from '@/api/file';
|
import { uploadFile } from '@/api/file';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@ -279,7 +267,9 @@ const route = useRoute();
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const tasks = ref<TaskWithCompletion[]>([]);
|
const tasks = ref<TaskWithCompletion[]>([]);
|
||||||
|
const children = ref<ChildInfo[]>([]);
|
||||||
const currentChildId = ref<number | null>(null);
|
const currentChildId = ref<number | null>(null);
|
||||||
|
const selectedChildId = ref<number | null>(null); // null = 全部孩子,使用 getMyTasks
|
||||||
|
|
||||||
// 提交弹窗
|
// 提交弹窗
|
||||||
const submitModalVisible = ref(false);
|
const submitModalVisible = ref(false);
|
||||||
@ -336,20 +326,21 @@ const openSubmitModal = (task: TaskWithCompletion) => {
|
|||||||
submitModalVisible.value = true;
|
submitModalVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理照片上传
|
// 处理照片上传(符合 API:photos 为 URL 字符串数组)
|
||||||
const handlePhotoUpload = async (options: UploadRequestOption) => {
|
const handlePhotoUpload = async (options: UploadRequestOption) => {
|
||||||
const { file } = options;
|
const { file } = options;
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
if (submitForm.photos.length >= 9) {
|
||||||
formData.append('file', file as File);
|
|
||||||
const url = await uploadFile(formData);
|
|
||||||
if (submitForm.photos.length < 9) {
|
|
||||||
submitForm.photos.push(url);
|
|
||||||
} else {
|
|
||||||
message.warning('最多上传9张照片');
|
message.warning('最多上传9张照片');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await uploadFile(file as File, 'poster');
|
||||||
|
const url = result.filePath;
|
||||||
|
if (url) {
|
||||||
|
submitForm.photos.push(url);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error('上传失败');
|
message.error(error?.message || '上传失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -362,7 +353,8 @@ const removePhoto = (index: number) => {
|
|||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!selectedTask.value) return;
|
if (!selectedTask.value) return;
|
||||||
|
|
||||||
const childId = currentChildId.value || (route.query.childId ? Number(route.query.childId) : null);
|
// 优先使用任务自带的 studentId(聚合视图),否则用当前选中的孩子
|
||||||
|
const childId = selectedTask.value?.studentId ?? currentChildId.value ?? (route.query.childId ? Number(route.query.childId) : null);
|
||||||
if (!childId) {
|
if (!childId) {
|
||||||
message.warning('请先选择孩子');
|
message.warning('请先选择孩子');
|
||||||
return;
|
return;
|
||||||
@ -407,28 +399,43 @@ const previewImage = (url: string) => {
|
|||||||
imagePreviewVisible.value = true;
|
imagePreviewVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载任务列表
|
// 孩子切换
|
||||||
|
const onChildChange = (val: number | null) => {
|
||||||
|
if (val) {
|
||||||
|
router.replace({ query: { childId: String(val) } });
|
||||||
|
} else {
|
||||||
|
router.replace({ query: {} });
|
||||||
|
}
|
||||||
|
loadTasks();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载任务列表:有 childId 用 getChildTasks,无 childId 用 getMyTasks(聚合多孩子)
|
||||||
const loadTasks = async () => {
|
const loadTasks = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
let childId = route.query.childId ? Number(route.query.childId) : null;
|
let childId = route.query.childId ? Number(route.query.childId) : null;
|
||||||
|
|
||||||
// 如果没有指定childId,自动获取第一个孩子
|
// 加载孩子列表(用于选择器)
|
||||||
if (!childId) {
|
children.value = await getChildren();
|
||||||
const { getChildren } = await import('@/api/parent');
|
if (children.value.length === 0) {
|
||||||
const children = await getChildren();
|
tasks.value = [];
|
||||||
if (children && children.length > 0) {
|
loading.value = false;
|
||||||
childId = children[0].id;
|
return;
|
||||||
router.replace({ query: { childId: String(childId) } });
|
|
||||||
} else {
|
|
||||||
loading.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChildId.value = childId;
|
selectedChildId.value = childId;
|
||||||
const data = await getChildTasks(childId);
|
|
||||||
tasks.value = data.items;
|
if (childId) {
|
||||||
|
// 指定孩子:调用 getChildTasks
|
||||||
|
currentChildId.value = childId;
|
||||||
|
const data = await getChildTasks(childId);
|
||||||
|
tasks.value = data.items;
|
||||||
|
} else {
|
||||||
|
// 未指定:调用 getMyTasks 聚合多孩子任务
|
||||||
|
currentChildId.value = children.value[0]?.id ?? null;
|
||||||
|
const data = await getMyTasks();
|
||||||
|
tasks.value = data.items;
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.response?.data?.message || '加载数据失败');
|
message.error(error.response?.data?.message || '加载数据失败');
|
||||||
} finally {
|
} finally {
|
||||||
@ -436,6 +443,9 @@ const loadTasks = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 监听路由变化(如从孩子详情页跳转带 childId)
|
||||||
|
watch(() => route.query.childId, () => loadTasks());
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadTasks();
|
loadTasks();
|
||||||
});
|
});
|
||||||
@ -448,6 +458,15 @@ $primary-light: #f6ffed;
|
|||||||
.task-list-view {
|
.task-list-view {
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
@ -698,6 +717,7 @@ $primary-light: #f6ffed;
|
|||||||
|
|
||||||
// 评价详情弹窗样式
|
// 评价详情弹窗样式
|
||||||
.feedback-detail {
|
.feedback-detail {
|
||||||
|
|
||||||
.feedback-result-section,
|
.feedback-result-section,
|
||||||
.feedback-rating-section {
|
.feedback-rating-section {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
@ -854,6 +874,7 @@ $primary-light: #f6ffed;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.photo-upload-area {
|
.photo-upload-area {
|
||||||
|
|
||||||
.photo-item,
|
.photo-item,
|
||||||
.upload-btn {
|
.upload-btn {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<CalendarOutlined class="info-icon" />
|
<CalendarOutlined class="info-icon" />
|
||||||
<span class="info-value">{{ calculateAge(student.birthDate) || '--' }}{{ student.birthDate ? '岁' : ''
|
<span class="info-value">{{ calculateAge(student.birthDate) || '--' }}{{ student.birthDate ? '岁' : ''
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="gender-tag" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'">
|
<span class="gender-tag" :class="normalizeGender(student.gender) === '男' ? 'boy' : 'girl'">
|
||||||
{{ normalizeGender(student.gender) }}
|
{{ normalizeGender(student.gender) }}
|
||||||
</span>
|
</span>
|
||||||
@ -400,7 +400,7 @@ interface FormState {
|
|||||||
name: string;
|
name: string;
|
||||||
gender: string;
|
gender: string;
|
||||||
birthDate?: string | null;
|
birthDate?: string | null;
|
||||||
classId: number;
|
classId?: number;
|
||||||
parentName: string;
|
parentName: string;
|
||||||
parentPhone: string;
|
parentPhone: string;
|
||||||
}
|
}
|
||||||
@ -409,7 +409,7 @@ const formState = reactive<FormState>({
|
|||||||
name: '',
|
name: '',
|
||||||
gender: '男',
|
gender: '男',
|
||||||
birthDate: null,
|
birthDate: null,
|
||||||
classId: 0,
|
classId: undefined,
|
||||||
parentName: '',
|
parentName: '',
|
||||||
parentPhone: '',
|
parentPhone: '',
|
||||||
});
|
});
|
||||||
@ -417,7 +417,9 @@ const formState = reactive<FormState>({
|
|||||||
const rules: Record<string, any[]> = {
|
const rules: Record<string, any[]> = {
|
||||||
name: [{ required: true, message: '请输入学生姓名', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入学生姓名', trigger: 'blur' }],
|
||||||
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
||||||
classId: [{ required: true, message: '请选择班级', trigger: 'change', type: 'number' }],
|
classId: [
|
||||||
|
{ required: true, message: '请选择班级', trigger: 'change' },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateAge = (birthDate?: string | null): number | null => {
|
const calculateAge = (birthDate?: string | null): number | null => {
|
||||||
@ -482,7 +484,7 @@ const resetForm = () => {
|
|||||||
formState.name = '';
|
formState.name = '';
|
||||||
formState.gender = '男';
|
formState.gender = '男';
|
||||||
formState.birthDate = null;
|
formState.birthDate = null;
|
||||||
formState.classId = 0;
|
formState.classId = undefined;
|
||||||
formState.parentName = '';
|
formState.parentName = '';
|
||||||
formState.parentPhone = '';
|
formState.parentPhone = '';
|
||||||
};
|
};
|
||||||
@ -508,6 +510,11 @@ const handleEdit = (record: Student) => {
|
|||||||
const handleModalOk = async () => {
|
const handleModalOk = async () => {
|
||||||
try {
|
try {
|
||||||
await formRef.value?.validate();
|
await formRef.value?.validate();
|
||||||
|
const classId = formState.classId;
|
||||||
|
if (Number(classId || '-1') < 0) {
|
||||||
|
message.warning('请选择班级');
|
||||||
|
return;
|
||||||
|
}
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
|
|
||||||
if (isEdit.value && formState.id) {
|
if (isEdit.value && formState.id) {
|
||||||
@ -515,7 +522,7 @@ const handleModalOk = async () => {
|
|||||||
name: formState.name,
|
name: formState.name,
|
||||||
gender: formState.gender,
|
gender: formState.gender,
|
||||||
birthDate: formState.birthDate,
|
birthDate: formState.birthDate,
|
||||||
classId: formState.classId,
|
classId,
|
||||||
parentName: formState.parentName,
|
parentName: formState.parentName,
|
||||||
parentPhone: formState.parentPhone,
|
parentPhone: formState.parentPhone,
|
||||||
});
|
});
|
||||||
@ -525,7 +532,7 @@ const handleModalOk = async () => {
|
|||||||
name: formState.name,
|
name: formState.name,
|
||||||
gender: formState.gender,
|
gender: formState.gender,
|
||||||
birthDate: formState.birthDate,
|
birthDate: formState.birthDate,
|
||||||
classId: formState.classId,
|
classId,
|
||||||
parentName: formState.parentName,
|
parentName: formState.parentName,
|
||||||
parentPhone: formState.parentPhone,
|
parentPhone: formState.parentPhone,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
<div class="class-avatar" :style="{ background: getGradeGradient(cls.grade) }">
|
<div class="class-avatar" :style="{ background: getGradeGradient(cls.grade) }">
|
||||||
<span class="avatar-text">{{ getGradeInitial(cls.grade) }}</span>
|
<span class="avatar-text">{{ getGradeInitial(cls.grade) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-name">{{ cls.name }}</div>
|
<div class="class-name">{{ cls.name || '未命名班级' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 统计信息 -->
|
<!-- 统计信息 -->
|
||||||
|
|||||||
@ -182,21 +182,32 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-form-item label="选择目标" required v-if="createForm.targetType === 'CLASS'">
|
<a-form-item label="选择班级" required v-if="createForm.targetType === 'CLASS'">
|
||||||
<a-select v-model:value="createForm.targetIds" mode="multiple" placeholder="请选择班级" style="width: 100%;">
|
<a-select v-model:value="createForm.targetIds" mode="multiple" placeholder="请选择班级" style="width: 100%;">
|
||||||
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
|
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
|
||||||
{{ cls.name }} ({{ cls.grade }})
|
{{ cls.name }} ({{ cls.grade }})
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="选择学生" required v-if="createForm.targetType === 'STUDENT'">
|
<template v-if="createForm.targetType === 'STUDENT'">
|
||||||
<a-select v-model:value="createForm.targetIds" mode="multiple" placeholder="请选择学生" style="width: 100%;"
|
<a-form-item label="选择班级" required>
|
||||||
:filter-option="filterStudentOption" show-search>
|
<a-select v-model:value="selectedClassIdForStudent" placeholder="请先选择班级" style="width: 100%;" allow-clear
|
||||||
<a-select-option v-for="student in students" :key="student.id" :value="student.id">
|
@change="onStudentClassChange">
|
||||||
{{ student.name }} - {{ student.class?.name }}
|
<a-select-option v-for="cls in classes" :key="cls.id" :value="cls.id">
|
||||||
</a-select-option>
|
{{ cls.name }} ({{ cls.grade }})
|
||||||
</a-select>
|
</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="选择学生" required>
|
||||||
|
<a-select v-model:value="createForm.targetIds" mode="multiple" placeholder="请选择学生" style="width: 100%;"
|
||||||
|
:filter-option="filterStudentOption" show-search :loading="studentsLoading"
|
||||||
|
:disabled="!selectedClassIdForStudent">
|
||||||
|
<a-select-option v-for="student in students" :key="student.id" :value="student.id">
|
||||||
|
{{ student.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
<a-form-item label="关联课程">
|
<a-form-item label="关联课程">
|
||||||
<a-select v-model:value="createForm.relatedCourseId" placeholder="可选,关联课程包" style="width: 100%;" allowClear
|
<a-select v-model:value="createForm.relatedCourseId" placeholder="可选,关联课程包" style="width: 100%;" allowClear
|
||||||
show-search :filter-option="filterCourseOption">
|
show-search :filter-option="filterCourseOption">
|
||||||
@ -253,8 +264,9 @@
|
|||||||
</a-tag>
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="completion-content">
|
<div class="completion-content">
|
||||||
<div v-if="completion.photos && completion.photos.length > 0" class="content-preview">
|
<div v-if="completion.photos && completion.photos.length > 0" class="content-photos">
|
||||||
<PictureOutlined /> {{ completion.photos.length }} 张照片
|
<img v-for="(photo, idx) in completion.photos" :key="idx" :src="photo" alt="照片"
|
||||||
|
class="photo-thumb" @click="previewImage(photo)" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="completion.videoUrl" class="content-preview">
|
<div v-if="completion.videoUrl" class="content-preview">
|
||||||
<VideoCameraOutlined /> 有视频
|
<VideoCameraOutlined /> 有视频
|
||||||
@ -269,8 +281,8 @@
|
|||||||
<a-tooltip :title="completion.parentFeedback || completion.feedback?.comment">
|
<a-tooltip :title="completion.parentFeedback || completion.feedback?.comment">
|
||||||
<span class="feedback-text">{{ (completion.parentFeedback || completion.feedback?.comment ||
|
<span class="feedback-text">{{ (completion.parentFeedback || completion.feedback?.comment ||
|
||||||
'').substring(0, 30) }}{{ (completion.parentFeedback || completion.feedback?.comment || '').length >
|
'').substring(0, 30) }}{{ (completion.parentFeedback || completion.feedback?.comment || '').length >
|
||||||
30
|
30
|
||||||
? '...' : '' }}</span>
|
? '...' : '' }}</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="no-feedback">暂无反馈</span>
|
<span v-else class="no-feedback">暂无反馈</span>
|
||||||
@ -312,9 +324,11 @@
|
|||||||
style="margin-bottom: 24px; padding: 16px; background: #fafafa; border-radius: 8px;">
|
style="margin-bottom: 24px; padding: 16px; background: #fafafa; border-radius: 8px;">
|
||||||
<h4 style="margin-bottom: 12px;">提交内容</h4>
|
<h4 style="margin-bottom: 12px;">提交内容</h4>
|
||||||
<div v-if="selectedCompletionForFeedback.photos && selectedCompletionForFeedback.photos.length > 0"
|
<div v-if="selectedCompletionForFeedback.photos && selectedCompletionForFeedback.photos.length > 0"
|
||||||
style="margin-bottom: 12px;">
|
class="review-photos" style="margin-bottom: 12px;">
|
||||||
<PictureOutlined style="margin-right: 8px;" />
|
<div class="photos-grid">
|
||||||
<span>{{ selectedCompletionForFeedback.photos.length }} 张照片</span>
|
<img v-for="(photo, idx) in selectedCompletionForFeedback.photos" :key="idx" :src="photo" alt="照片"
|
||||||
|
@click="previewImage(photo)" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedCompletionForFeedback.videoUrl" style="margin-bottom: 12px;">
|
<div v-if="selectedCompletionForFeedback.videoUrl" style="margin-bottom: 12px;">
|
||||||
<VideoCameraOutlined style="margin-right: 8px;" />
|
<VideoCameraOutlined style="margin-right: 8px;" />
|
||||||
@ -355,11 +369,17 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 图片预览 -->
|
||||||
|
<a-image :style="{ display: 'none' }" :preview="{
|
||||||
|
visible: imagePreviewVisible,
|
||||||
|
onVisibleChange: (visible: boolean) => { imagePreviewVisible = visible; },
|
||||||
|
}" :src="previewImageUrl" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, computed } from 'vue';
|
import { ref, reactive, onMounted, computed, watch } from 'vue';
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
CheckSquareOutlined,
|
CheckSquareOutlined,
|
||||||
@ -395,7 +415,7 @@ import {
|
|||||||
updateCompletionFeedback,
|
updateCompletionFeedback,
|
||||||
sendTaskReminder,
|
sendTaskReminder,
|
||||||
getTeacherClasses,
|
getTeacherClasses,
|
||||||
getTeacherStudents,
|
getTeacherClassStudents,
|
||||||
getTeacherCourses,
|
getTeacherCourses,
|
||||||
getTaskTemplates,
|
getTaskTemplates,
|
||||||
type TeacherTask,
|
type TeacherTask,
|
||||||
@ -438,6 +458,8 @@ const isEdit = ref(false);
|
|||||||
const editTaskId = ref<number | null>(null);
|
const editTaskId = ref<number | null>(null);
|
||||||
const classes = ref<any[]>([]);
|
const classes = ref<any[]>([]);
|
||||||
const students = ref<any[]>([]);
|
const students = ref<any[]>([]);
|
||||||
|
const studentsLoading = ref(false);
|
||||||
|
const selectedClassIdForStudent = ref<number | undefined>();
|
||||||
const courses = ref<any[]>([]);
|
const courses = ref<any[]>([]);
|
||||||
|
|
||||||
// 模板相关
|
// 模板相关
|
||||||
@ -506,8 +528,8 @@ const getCompletedCount = (task: TeacherTask) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const filterStudentOption = (input: string, option: any) => {
|
const filterStudentOption = (input: string, option: any) => {
|
||||||
const text = option.children[0].children.toLowerCase();
|
const student = students.value.find((s: any) => s.id === option.value);
|
||||||
return text.includes(input.toLowerCase());
|
return student ? student.name.toLowerCase().includes(input.toLowerCase()) : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterCourseOption = (input: string, option: any) => {
|
const filterCourseOption = (input: string, option: any) => {
|
||||||
@ -533,22 +555,68 @@ const loadTasks = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载班级、学生、课程
|
// 目标类型切换时重置相关状态
|
||||||
|
watch(() => createForm.targetType, () => {
|
||||||
|
createForm.targetIds = [];
|
||||||
|
selectedClassIdForStudent.value = undefined;
|
||||||
|
students.value = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载班级、课程(学生按班级加载)
|
||||||
const loadOptions = async () => {
|
const loadOptions = async () => {
|
||||||
try {
|
try {
|
||||||
const [classesData, studentsData, coursesData] = await Promise.all([
|
const [classesData, coursesData] = await Promise.all([
|
||||||
getTeacherClasses(),
|
getTeacherClasses(),
|
||||||
getTeacherStudents({ pageNum: 1, pageSize: 500 }),
|
|
||||||
getTeacherCourses({ pageNum: 1, pageSize: 100 }),
|
getTeacherCourses({ pageNum: 1, pageSize: 100 }),
|
||||||
]);
|
]);
|
||||||
classes.value = classesData;
|
classes.value = classesData;
|
||||||
students.value = studentsData.items || [];
|
|
||||||
courses.value = coursesData.items || [];
|
courses.value = coursesData.items || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载选项失败', error);
|
console.error('加载选项失败', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 选择班级后加载该班级学生(目标类型为学生时)
|
||||||
|
const onStudentClassChange = async (value: unknown) => {
|
||||||
|
const classId = value != null && !Number.isNaN(Number(value)) ? Number(value) : undefined;
|
||||||
|
createForm.targetIds = [];
|
||||||
|
if (!classId) {
|
||||||
|
students.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
studentsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await getTeacherClassStudents(classId, { pageNum: 1, pageSize: 500 });
|
||||||
|
students.value = (res as any).list || res.items || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载班级学生失败', error);
|
||||||
|
students.value = [];
|
||||||
|
} finally {
|
||||||
|
studentsLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑时加载所有班级学生(用于回显已选学生)
|
||||||
|
const loadAllStudentsForEdit = async () => {
|
||||||
|
studentsLoading.value = true;
|
||||||
|
try {
|
||||||
|
const allStudents: any[] = [];
|
||||||
|
for (const cls of classes.value) {
|
||||||
|
const res = await getTeacherClassStudents(cls.id, { pageNum: 1, pageSize: 500 });
|
||||||
|
const items = (res as any).list || res.items || [];
|
||||||
|
items.forEach((s: any) => {
|
||||||
|
if (!allStudents.find(x => x.id === s.id)) allStudents.push({ ...s, class: { name: cls.name, grade: cls.grade } });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
students.value = allStudents;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载学生失败', error);
|
||||||
|
students.value = [];
|
||||||
|
} finally {
|
||||||
|
studentsLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
const onPageChange = (page: number) => {
|
||||||
currentPage.value = page;
|
currentPage.value = page;
|
||||||
loadTasks();
|
loadTasks();
|
||||||
@ -567,10 +635,11 @@ const openCreateModal = async () => {
|
|||||||
createForm.relatedCourseId = undefined;
|
createForm.relatedCourseId = undefined;
|
||||||
createForm.relatedBookName = ''; // 重置绘本名称
|
createForm.relatedBookName = ''; // 重置绘本名称
|
||||||
createForm.dateRange = null;
|
createForm.dateRange = null;
|
||||||
|
selectedClassIdForStudent.value = undefined;
|
||||||
|
students.value = [];
|
||||||
createModalVisible.value = true;
|
createModalVisible.value = true;
|
||||||
|
|
||||||
// 加载模板列表
|
await Promise.all([loadOptions(), loadTemplates()]);
|
||||||
await loadTemplates();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载模板列表
|
// 加载模板列表
|
||||||
@ -624,24 +693,26 @@ const getTaskTypeText = (type: string) => {
|
|||||||
const openEditModal = async (task: TeacherTask) => {
|
const openEditModal = async (task: TeacherTask) => {
|
||||||
isEdit.value = true;
|
isEdit.value = true;
|
||||||
editTaskId.value = task.id;
|
editTaskId.value = task.id;
|
||||||
|
selectedClassIdForStudent.value = undefined;
|
||||||
createModalVisible.value = true;
|
createModalVisible.value = true;
|
||||||
try {
|
try {
|
||||||
await loadOptions(); // 确保班级、学生、课程选项已加载
|
await loadOptions();
|
||||||
const detail = await getTeacherTask(task.id);
|
const detail = await getTeacherTask(task.id);
|
||||||
// 详情接口返回字段映射到表单(与新增一致)
|
|
||||||
createForm.title = detail.title || '';
|
createForm.title = detail.title || '';
|
||||||
createForm.description = detail.description || '';
|
createForm.description = detail.description || '';
|
||||||
createForm.taskType = (detail.type || detail.taskType || 'READING') as 'READING' | 'ACTIVITY' | 'HOMEWORK';
|
createForm.taskType = (detail.type || detail.taskType || 'READING') as 'READING' | 'ACTIVITY' | 'HOMEWORK';
|
||||||
createForm.targetType = (detail.targetType?.toUpperCase?.() || 'CLASS') as 'CLASS' | 'STUDENT';
|
createForm.targetType = (detail.targetType?.toUpperCase?.() || 'CLASS') as 'CLASS' | 'STUDENT';
|
||||||
createForm.targetIds = detail.targetIds || [];
|
createForm.targetIds = detail.targetIds || [];
|
||||||
// 关联课程:统一为 number,避免 API 返回 string 导致 a-select 无法匹配
|
|
||||||
const rawCourseId = detail.courseId ?? detail.relatedCourseId;
|
const rawCourseId = detail.courseId ?? detail.relatedCourseId;
|
||||||
createForm.relatedCourseId = rawCourseId;
|
createForm.relatedCourseId = rawCourseId;
|
||||||
createForm.relatedBookName = detail.relatedBookName || '';
|
createForm.relatedBookName = detail.relatedBookName || '';
|
||||||
// 任务时间:dueDate 为空时用 startDate 作为结束日期,保证能回显
|
|
||||||
createForm.dateRange = detail.startDate
|
createForm.dateRange = detail.startDate
|
||||||
? [dayjs(detail.startDate), dayjs(detail.dueDate || detail.startDate)]
|
? [dayjs(detail.startDate), dayjs(detail.dueDate || detail.startDate)]
|
||||||
: null;
|
: null;
|
||||||
|
// 编辑学生任务时加载全部学生用于回显
|
||||||
|
if (createForm.targetType === 'STUDENT' && createForm.targetIds.length > 0) {
|
||||||
|
await loadAllStudentsForEdit();
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '加载任务详情失败');
|
message.error(error.message || '加载任务详情失败');
|
||||||
createModalVisible.value = false;
|
createModalVisible.value = false;
|
||||||
@ -656,7 +727,7 @@ const handleCreate = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEdit.value && createForm.targetIds.length === 0) {
|
if (!isEdit.value && createForm.targetIds.length === 0) {
|
||||||
message.warning('请选择目标班级或学生');
|
message.warning(createForm.targetType === 'STUDENT' ? '请先选择班级并选择学生' : '请选择目标班级');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,6 +909,14 @@ const onCompletionPageChange = (page: number) => {
|
|||||||
loadCompletions();
|
loadCompletions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 图片预览
|
||||||
|
const imagePreviewVisible = ref(false);
|
||||||
|
const previewImageUrl = ref('');
|
||||||
|
const previewImage = (url: string) => {
|
||||||
|
previewImageUrl.value = url;
|
||||||
|
imagePreviewVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadTasks();
|
loadTasks();
|
||||||
loadOptions();
|
loadOptions();
|
||||||
@ -1127,5 +1206,46 @@ onMounted(() => {
|
|||||||
color: #999;
|
color: #999;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.completion-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.content-photos {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.photo-thumb {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-preview {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评价弹窗 - 提交内容照片网格
|
||||||
|
.review-photos .photos-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
package com.reading.platform.controller.parent;
|
package com.reading.platform.controller.parent;
|
||||||
|
|
||||||
import com.reading.platform.common.mapper.StudentMapper;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.response.StudentResponse;
|
import com.reading.platform.dto.response.ChildInfoResponse;
|
||||||
import com.reading.platform.entity.Student;
|
import com.reading.platform.dto.response.ChildProfileResponse;
|
||||||
import com.reading.platform.service.StudentService;
|
import com.reading.platform.dto.response.LessonRecordResponse;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
|
import com.reading.platform.service.ParentChildService;
|
||||||
|
import com.reading.platform.service.ParentLessonService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -17,30 +22,45 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/parent/children")
|
@RequestMapping("/api/v1/parent/children")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.PARENT)
|
||||||
public class ParentChildController {
|
public class ParentChildController {
|
||||||
|
|
||||||
private final StudentService studentService;
|
private final ParentChildService parentChildService;
|
||||||
private final StudentMapper studentMapper;
|
private final ParentAuthService parentAuthService;
|
||||||
|
private final ParentLessonService parentLessonService;
|
||||||
|
|
||||||
@Operation(summary = "Get my children")
|
@Operation(summary = "Get my children")
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<List<StudentResponse>> getMyChildren() {
|
public Result<List<ChildInfoResponse>> getMyChildren() {
|
||||||
Long parentId = SecurityUtils.getCurrentUserId();
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
List<Student> students = studentService.getStudentsByParentId(parentId);
|
List<ChildInfoResponse> children = parentChildService.getChildrenForParent(parentId);
|
||||||
return Result.success(studentMapper.toVO(students));
|
return Result.success(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get child by ID")
|
@Operation(summary = "Get child by ID")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<StudentResponse> getChild(@PathVariable Long id) {
|
public Result<ChildProfileResponse> getChild(@PathVariable Long id) {
|
||||||
Student student = studentService.getStudentById(id);
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
return Result.success(studentMapper.toVO(student));
|
ChildProfileResponse profile = parentChildService.getChildProfile(parentId, id);
|
||||||
|
return Result.success(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get child lesson records (reading history)")
|
||||||
|
@GetMapping("/{id}/lessons")
|
||||||
|
public Result<PageResult<LessonRecordResponse>> getChildLessons(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, id);
|
||||||
|
PageResult<LessonRecordResponse> result = parentLessonService.getLessonsByStudentId(id, parentId, pageNum, pageSize);
|
||||||
|
return Result.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get child growth records")
|
@Operation(summary = "Get child growth records")
|
||||||
@GetMapping("/{id}/growth")
|
@GetMapping("/{id}/growth")
|
||||||
public Result<Object> getChildGrowth(@PathVariable Long id) {
|
public Result<Object> getChildGrowth(@PathVariable Long id) {
|
||||||
// TODO: 实现获取成长记录
|
// 已迁移至 ParentGrowthController: GET /parent/growth-records/student/:studentId
|
||||||
return Result.success(null);
|
return Result.success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,81 +1,114 @@
|
|||||||
package com.reading.platform.controller.parent;
|
package com.reading.platform.controller.parent;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.mapper.GrowthRecordMapper;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.reading.platform.entity.Clazz;
|
||||||
|
import com.reading.platform.entity.GrowthRecord;
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.request.GrowthRecordCreateRequest;
|
import com.reading.platform.dto.response.GrowthRecordForParentResponse;
|
||||||
import com.reading.platform.dto.request.GrowthRecordUpdateRequest;
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
import com.reading.platform.dto.response.GrowthRecordResponse;
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.entity.GrowthRecord;
|
import com.reading.platform.service.ClassService;
|
||||||
import com.reading.platform.service.GrowthRecordService;
|
import com.reading.platform.service.GrowthRecordService;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Tag(name = "Parent - Growth Record", description = "Growth Record APIs for Parent")
|
/**
|
||||||
|
* 家长端成长档案 - 只读
|
||||||
|
* 家长仅可查看孩子的成长档案,不可创建/修改/删除
|
||||||
|
* 返回格式对齐前端 GrowthRecord:images 为数组,recordType,class
|
||||||
|
*/
|
||||||
|
@Tag(name = "Parent - Growth Record", description = "Growth Record APIs for Parent (read-only)")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/parent/growth-records")
|
@RequestMapping("/api/v1/parent/growth-records")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.PARENT)
|
||||||
public class ParentGrowthController {
|
public class ParentGrowthController {
|
||||||
|
|
||||||
private final GrowthRecordService growthRecordService;
|
private final GrowthRecordService growthRecordService;
|
||||||
private final GrowthRecordMapper growthRecordMapper;
|
private final ParentAuthService parentAuthService;
|
||||||
|
private final ClassService classService;
|
||||||
@Operation(summary = "Create growth record")
|
private final ObjectMapper objectMapper;
|
||||||
@PostMapping
|
|
||||||
public Result<GrowthRecordResponse> createGrowthRecord(@Valid @RequestBody GrowthRecordCreateRequest request) {
|
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
|
||||||
Long userId = SecurityUtils.getCurrentUserId();
|
|
||||||
GrowthRecord record = growthRecordService.createGrowthRecord(tenantId, userId, "parent", request);
|
|
||||||
return Result.success(growthRecordMapper.toVO(record));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "Update growth record")
|
|
||||||
@PutMapping("/{id}")
|
|
||||||
public Result<GrowthRecordResponse> updateGrowthRecord(@PathVariable Long id, @RequestBody GrowthRecordUpdateRequest request) {
|
|
||||||
GrowthRecord record = growthRecordService.updateGrowthRecord(id, request);
|
|
||||||
return Result.success(growthRecordMapper.toVO(record));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(summary = "Get growth record by ID")
|
@Operation(summary = "Get growth record by ID")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<GrowthRecordResponse> getGrowthRecord(@PathVariable Long id) {
|
public Result<GrowthRecordForParentResponse> getGrowthRecord(@PathVariable Long id) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
GrowthRecord record = growthRecordService.getGrowthRecordById(id);
|
GrowthRecord record = growthRecordService.getGrowthRecordById(id);
|
||||||
return Result.success(growthRecordMapper.toVO(record));
|
parentAuthService.validateParentStudentRelation(parentId, record.getStudentId());
|
||||||
|
return Result.success(toParentResponse(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get growth records by student ID")
|
@Operation(summary = "Get growth records by student ID")
|
||||||
@GetMapping("/student/{studentId}")
|
@GetMapping("/student/{studentId}")
|
||||||
public Result<PageResult<GrowthRecordResponse>> getGrowthRecordsByStudent(
|
public Result<PageResult<GrowthRecordForParentResponse>> getGrowthRecordsByStudent(
|
||||||
@PathVariable Long studentId,
|
@PathVariable Long studentId,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String type) {
|
@RequestParam(required = false) String type) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, studentId);
|
||||||
Page<GrowthRecord> page = growthRecordService.getGrowthRecordsByStudentId(studentId, pageNum, pageSize, type);
|
Page<GrowthRecord> page = growthRecordService.getGrowthRecordsByStudentId(studentId, pageNum, pageSize, type);
|
||||||
List<GrowthRecordResponse> voList = growthRecordMapper.toVO(page.getRecords());
|
List<GrowthRecordForParentResponse> voList = page.getRecords().stream()
|
||||||
|
.map(this::toParentResponse)
|
||||||
|
.toList();
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get recent growth records")
|
@Operation(summary = "Get recent growth records")
|
||||||
@GetMapping("/student/{studentId}/recent")
|
@GetMapping("/student/{studentId}/recent")
|
||||||
public Result<List<GrowthRecordResponse>> getRecentGrowthRecords(
|
public Result<List<GrowthRecordForParentResponse>> getRecentGrowthRecords(
|
||||||
@PathVariable Long studentId,
|
@PathVariable Long studentId,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
@RequestParam(required = false, defaultValue = "10") Integer limit) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, studentId);
|
||||||
List<GrowthRecord> records = growthRecordService.getRecentGrowthRecords(studentId, limit);
|
List<GrowthRecord> records = growthRecordService.getRecentGrowthRecords(studentId, limit);
|
||||||
return Result.success(growthRecordMapper.toVO(records));
|
return Result.success(records.stream().map(this::toParentResponse).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Delete growth record")
|
private GrowthRecordForParentResponse toParentResponse(GrowthRecord record) {
|
||||||
@DeleteMapping("/{id}")
|
List<String> images = parseImages(record.getImages());
|
||||||
public Result<Void> deleteGrowthRecord(@PathVariable Long id) {
|
GrowthRecordForParentResponse.ClassInfo classInfo = null;
|
||||||
growthRecordService.deleteGrowthRecord(id);
|
if (record.getStudentId() != null) {
|
||||||
return Result.success();
|
Clazz clazz = classService.getPrimaryClassByStudentId(record.getStudentId());
|
||||||
|
if (clazz != null) {
|
||||||
|
classInfo = GrowthRecordForParentResponse.ClassInfo.builder()
|
||||||
|
.id(clazz.getId())
|
||||||
|
.name(clazz.getName())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GrowthRecordForParentResponse.builder()
|
||||||
|
.id(record.getId())
|
||||||
|
.title(record.getTitle())
|
||||||
|
.content(record.getContent())
|
||||||
|
.images(images)
|
||||||
|
.recordDate(record.getRecordDate())
|
||||||
|
.recordType(record.getType())
|
||||||
|
.classInfo(classInfo)
|
||||||
|
.createdAt(record.getCreatedAt())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> parseImages(String imagesJson) {
|
||||||
|
if (!StringUtils.hasText(imagesJson)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(imagesJson, new TypeReference<List<String>>() {});
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
package com.reading.platform.controller.parent;
|
package com.reading.platform.controller.parent;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.mapper.NotificationMapper;
|
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.response.NotificationResponse;
|
import com.reading.platform.dto.response.NotificationForParentResponse;
|
||||||
import com.reading.platform.entity.Notification;
|
import com.reading.platform.entity.Notification;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
import com.reading.platform.service.NotificationService;
|
import com.reading.platform.service.NotificationService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -14,35 +15,51 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Tag(name = "Parent - Notification", description = "Notification APIs for Parent")
|
@Tag(name = "Parent - Notification", description = "Notification APIs for Parent")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/parent/notifications")
|
@RequestMapping("/api/v1/parent/notifications")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.PARENT)
|
||||||
public class ParentNotificationController {
|
public class ParentNotificationController {
|
||||||
|
|
||||||
private final NotificationService notificationService;
|
private final NotificationService notificationService;
|
||||||
private final NotificationMapper notificationMapper;
|
|
||||||
|
|
||||||
@Operation(summary = "Get notification by ID")
|
@Operation(summary = "Get notification by ID")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public Result<NotificationResponse> getNotification(@PathVariable Long id) {
|
public Result<NotificationForParentResponse> getNotification(@PathVariable Long id) {
|
||||||
Notification notification = notificationService.getNotificationById(id);
|
Notification notification = notificationService.getNotificationById(id);
|
||||||
return Result.success(notificationMapper.toVO(notification));
|
return Result.success(toParentResponse(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get my notifications")
|
@Operation(summary = "Get my notifications")
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<PageResult<NotificationResponse>> getMyNotifications(
|
public Result<PageResult<NotificationForParentResponse>> getMyNotifications(
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) Integer isRead) {
|
@RequestParam(required = false) Integer isRead,
|
||||||
|
@RequestParam(required = false) String notificationType) {
|
||||||
Long userId = SecurityUtils.getCurrentUserId();
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
Page<Notification> page = notificationService.getMyNotifications(userId, "parent", pageNum, pageSize, isRead);
|
Page<Notification> page = notificationService.getMyNotifications(userId, "parent", pageNum, pageSize, isRead, notificationType);
|
||||||
List<NotificationResponse> voList = notificationMapper.toVO(page.getRecords());
|
List<NotificationForParentResponse> voList = page.getRecords().stream()
|
||||||
|
.map(this::toParentResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NotificationForParentResponse toParentResponse(Notification n) {
|
||||||
|
return NotificationForParentResponse.builder()
|
||||||
|
.id(n.getId())
|
||||||
|
.title(n.getTitle())
|
||||||
|
.content(n.getContent())
|
||||||
|
.notificationType(n.getType())
|
||||||
|
.isRead(n.getIsRead() != null && n.getIsRead() == 1)
|
||||||
|
.readAt(n.getReadAt())
|
||||||
|
.createdAt(n.getCreatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "Mark notification as read")
|
@Operation(summary = "Mark notification as read")
|
||||||
@PostMapping("/{id}/read")
|
@PostMapping("/{id}/read")
|
||||||
public Result<Void> markAsRead(@PathVariable Long id) {
|
public Result<Void> markAsRead(@PathVariable Long id) {
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
package com.reading.platform.controller.parent;
|
package com.reading.platform.controller.parent;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
||||||
import com.reading.platform.common.mapper.TaskMapper;
|
import com.reading.platform.common.mapper.TaskMapper;
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.dto.request.TaskCompleteRequest;
|
||||||
import com.reading.platform.dto.request.TaskSubmitRequest;
|
import com.reading.platform.dto.request.TaskSubmitRequest;
|
||||||
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
||||||
import com.reading.platform.dto.response.TaskFeedbackResponse;
|
import com.reading.platform.dto.response.TaskFeedbackResponse;
|
||||||
import com.reading.platform.dto.response.TaskResponse;
|
import com.reading.platform.dto.response.TaskResponse;
|
||||||
|
import com.reading.platform.dto.response.TaskWithCompletionResponse;
|
||||||
import com.reading.platform.entity.Task;
|
import com.reading.platform.entity.Task;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
import com.reading.platform.service.TaskFeedbackService;
|
import com.reading.platform.service.TaskFeedbackService;
|
||||||
import com.reading.platform.service.TaskService;
|
import com.reading.platform.service.TaskService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -24,11 +30,13 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/parent/tasks")
|
@RequestMapping("/api/v1/parent/tasks")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.PARENT)
|
||||||
public class ParentTaskController {
|
public class ParentTaskController {
|
||||||
|
|
||||||
private final TaskService taskService;
|
private final TaskService taskService;
|
||||||
private final TaskMapper taskMapper;
|
private final TaskMapper taskMapper;
|
||||||
private final TaskFeedbackService taskFeedbackService;
|
private final TaskFeedbackService taskFeedbackService;
|
||||||
|
private final ParentAuthService parentAuthService;
|
||||||
|
|
||||||
@Operation(summary = "获取任务详情")
|
@Operation(summary = "获取任务详情")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
@ -37,27 +45,28 @@ public class ParentTaskController {
|
|||||||
return Result.success(taskMapper.toVO(task));
|
return Result.success(taskMapper.toVO(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取孩子的任务列表")
|
@Operation(summary = "获取孩子的任务列表(含完成信息与教师评价)")
|
||||||
@GetMapping("/student/{studentId}")
|
@GetMapping("/student/{studentId}")
|
||||||
public Result<PageResult<TaskResponse>> getTasksByStudent(
|
public Result<PageResult<TaskWithCompletionResponse>> getTasksByStudent(
|
||||||
@PathVariable Long studentId,
|
@PathVariable Long studentId,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status) {
|
||||||
Page<Task> page = taskService.getTasksByStudentId(studentId, pageNum, pageSize, status);
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
List<TaskResponse> voList = taskMapper.toVO(page.getRecords());
|
parentAuthService.validateParentStudentRelation(parentId, studentId);
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
PageResult<TaskWithCompletionResponse> result = taskService.getTasksWithCompletionByStudentId(studentId, pageNum, pageSize, status);
|
||||||
|
return Result.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取我的任务列表")
|
@Operation(summary = "获取我的任务列表(聚合多孩子任务)")
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<PageResult<TaskResponse>> getMyTasks(
|
public Result<PageResult<TaskWithCompletionResponse>> getMyTasks(
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status) {
|
||||||
Long parentId = SecurityUtils.getCurrentUserId();
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
// TODO: 根据 parentId 获取关联学生的任务列表
|
PageResult<TaskWithCompletionResponse> result = taskService.getMyTasksForParent(parentId, pageNum, pageSize, status);
|
||||||
return Result.success(PageResult.of(List.of(), 0L, Long.valueOf(pageNum == null ? 1 : pageNum), Long.valueOf(pageSize == null ? 10 : pageSize)));
|
return Result.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============== 提交与评价 API ===============
|
// =============== 提交与评价 API ===============
|
||||||
@ -67,6 +76,8 @@ public class ParentTaskController {
|
|||||||
public Result<TaskCompletionDetailResponse> submitTask(
|
public Result<TaskCompletionDetailResponse> submitTask(
|
||||||
@PathVariable Long taskId,
|
@PathVariable Long taskId,
|
||||||
@Valid @RequestBody TaskSubmitRequest request) {
|
@Valid @RequestBody TaskSubmitRequest request) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, request.getStudentId());
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
TaskCompletionDetailResponse response = taskService.submitTaskCompletion(taskId, request, tenantId);
|
TaskCompletionDetailResponse response = taskService.submitTaskCompletion(taskId, request, tenantId);
|
||||||
return Result.success(response);
|
return Result.success(response);
|
||||||
@ -77,6 +88,8 @@ public class ParentTaskController {
|
|||||||
public Result<TaskCompletionDetailResponse> updateSubmission(
|
public Result<TaskCompletionDetailResponse> updateSubmission(
|
||||||
@PathVariable Long taskId,
|
@PathVariable Long taskId,
|
||||||
@Valid @RequestBody TaskSubmitRequest request) {
|
@Valid @RequestBody TaskSubmitRequest request) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, request.getStudentId());
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
TaskCompletionDetailResponse response = taskService.submitTaskCompletion(taskId, request, tenantId);
|
TaskCompletionDetailResponse response = taskService.submitTaskCompletion(taskId, request, tenantId);
|
||||||
return Result.success(response);
|
return Result.success(response);
|
||||||
@ -85,6 +98,8 @@ public class ParentTaskController {
|
|||||||
@Operation(summary = "获取教师评价")
|
@Operation(summary = "获取教师评价")
|
||||||
@GetMapping("/completions/{completionId}/feedback")
|
@GetMapping("/completions/{completionId}/feedback")
|
||||||
public Result<TaskFeedbackResponse> getFeedback(@PathVariable Long completionId) {
|
public Result<TaskFeedbackResponse> getFeedback(@PathVariable Long completionId) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentCanAccessCompletion(parentId, completionId);
|
||||||
TaskFeedbackResponse response = taskFeedbackService.getFeedbackByCompletionId(completionId);
|
TaskFeedbackResponse response = taskFeedbackService.getFeedbackByCompletionId(completionId);
|
||||||
return Result.success(response);
|
return Result.success(response);
|
||||||
}
|
}
|
||||||
@ -92,6 +107,8 @@ public class ParentTaskController {
|
|||||||
@Operation(summary = "获取提交详情")
|
@Operation(summary = "获取提交详情")
|
||||||
@GetMapping("/completions/{completionId}")
|
@GetMapping("/completions/{completionId}")
|
||||||
public Result<TaskCompletionDetailResponse> getCompletionDetail(@PathVariable Long completionId) {
|
public Result<TaskCompletionDetailResponse> getCompletionDetail(@PathVariable Long completionId) {
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentCanAccessCompletion(parentId, completionId);
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
TaskCompletionDetailResponse response = taskService.getCompletionDetail(completionId, tenantId);
|
TaskCompletionDetailResponse response = taskService.getCompletionDetail(completionId, tenantId);
|
||||||
return Result.success(response);
|
return Result.success(response);
|
||||||
@ -99,14 +116,23 @@ public class ParentTaskController {
|
|||||||
|
|
||||||
// =============== 兼容旧接口 ===============
|
// =============== 兼容旧接口 ===============
|
||||||
|
|
||||||
@Operation(summary = "完成任务(旧接口,兼容使用)")
|
@Operation(summary = "完成任务(旧接口,兼容使用,支持 JSON body)")
|
||||||
@PostMapping("/{id}/complete")
|
@PostMapping("/{id}/complete")
|
||||||
public Result<Void> completeTask(
|
public Result<Void> completeTask(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam Long studentId,
|
@RequestBody(required = false) TaskCompleteRequest body,
|
||||||
|
@RequestParam(required = false) Long studentId,
|
||||||
@RequestParam(required = false) String content,
|
@RequestParam(required = false) String content,
|
||||||
@RequestParam(required = false) String attachments) {
|
@RequestParam(required = false) String attachments) {
|
||||||
taskService.completeTask(id, studentId, content, attachments);
|
Long sid = (body != null && body.getStudentId() != null) ? body.getStudentId() : studentId;
|
||||||
|
String cnt = (body != null && body.getContent() != null) ? body.getContent() : content;
|
||||||
|
String att = (body != null && body.getAttachments() != null) ? body.getAttachments() : attachments;
|
||||||
|
if (sid == null) {
|
||||||
|
throw new BusinessException(ErrorCode.INVALID_PARAMETER, "学生ID不能为空");
|
||||||
|
}
|
||||||
|
Long parentId = SecurityUtils.getCurrentUserId();
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, sid);
|
||||||
|
taskService.completeTask(id, sid, cnt, att);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,34 @@
|
|||||||
package com.reading.platform.controller.school;
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.mapper.ClassMapper;
|
import com.reading.platform.common.mapper.ClassMapper;
|
||||||
import com.reading.platform.common.mapper.ClassTeacherMapper;
|
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.request.ClassCreateRequest;
|
import com.reading.platform.dto.request.ClassCreateRequest;
|
||||||
import com.reading.platform.dto.request.ClassUpdateRequest;
|
import com.reading.platform.dto.request.ClassUpdateRequest;
|
||||||
|
import com.reading.platform.common.mapper.StudentMapper;
|
||||||
import com.reading.platform.dto.response.ClassResponse;
|
import com.reading.platform.dto.response.ClassResponse;
|
||||||
import com.reading.platform.dto.response.ClassTeacherResponse;
|
import com.reading.platform.dto.response.ClassTeacherResponse;
|
||||||
|
import com.reading.platform.dto.response.StudentResponse;
|
||||||
import com.reading.platform.entity.Clazz;
|
import com.reading.platform.entity.Clazz;
|
||||||
import com.reading.platform.entity.ClassTeacher;
|
import com.reading.platform.entity.ClassTeacher;
|
||||||
|
import com.reading.platform.entity.Lesson;
|
||||||
|
import com.reading.platform.entity.Student;
|
||||||
|
import com.reading.platform.entity.Teacher;
|
||||||
|
import com.reading.platform.mapper.ClassTeacherMapper;
|
||||||
|
import com.reading.platform.mapper.LessonMapper;
|
||||||
import com.reading.platform.service.ClassService;
|
import com.reading.platform.service.ClassService;
|
||||||
|
import com.reading.platform.service.StudentService;
|
||||||
|
import com.reading.platform.service.TeacherService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Tag(name = "School - Class", description = "Class Management APIs for School")
|
@Tag(name = "School - Class", description = "Class Management APIs for School")
|
||||||
@ -30,6 +40,10 @@ public class SchoolClassController {
|
|||||||
private final ClassService classService;
|
private final ClassService classService;
|
||||||
private final ClassMapper classMapper;
|
private final ClassMapper classMapper;
|
||||||
private final ClassTeacherMapper classTeacherMapper;
|
private final ClassTeacherMapper classTeacherMapper;
|
||||||
|
private final StudentService studentService;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
private final TeacherService teacherService;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
|
||||||
@Operation(summary = "Create class")
|
@Operation(summary = "Create class")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@ -59,13 +73,40 @@ public class SchoolClassController {
|
|||||||
@GetMapping
|
@GetMapping
|
||||||
public Result<PageResult<ClassResponse>> getClassPage(
|
public Result<PageResult<ClassResponse>> getClassPage(
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "100") Integer pageSize,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String grade,
|
@RequestParam(required = false) String grade,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Page<Clazz> page = classService.getClassPage(tenantId, pageNum, pageSize, keyword, grade, status);
|
Page<Clazz> page = classService.getClassPage(tenantId, pageNum, pageSize, keyword, grade, status);
|
||||||
List<ClassResponse> voList = classMapper.toVO(page.getRecords());
|
List<ClassResponse> voList = classMapper.toVO(page.getRecords());
|
||||||
|
for (ClassResponse vo : voList) {
|
||||||
|
int studentCount = studentService.getStudentListByClassId(vo.getId()).size();
|
||||||
|
long lessonCount = 0;
|
||||||
|
try {
|
||||||
|
lessonCount = lessonMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Lesson>().eq(Lesson::getClassId, vo.getId()));
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
vo.setStudentCount(studentCount);
|
||||||
|
vo.setLessonCount((int) lessonCount);
|
||||||
|
|
||||||
|
List<ClassTeacher> classTeachers = classTeacherMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ClassTeacher>().eq(ClassTeacher::getClassId, vo.getId()));
|
||||||
|
List<ClassTeacherResponse> teacherList = new ArrayList<>();
|
||||||
|
for (ClassTeacher ct : classTeachers) {
|
||||||
|
Teacher t = teacherService.getTeacherById(ct.getTeacherId());
|
||||||
|
teacherList.add(ClassTeacherResponse.builder()
|
||||||
|
.id(ct.getId())
|
||||||
|
.classId(ct.getClassId())
|
||||||
|
.teacherId(ct.getTeacherId())
|
||||||
|
.role(ct.getRole())
|
||||||
|
.teacherName(t != null ? t.getName() : null)
|
||||||
|
.isPrimary("班主任".equals(ct.getRole()) || "主班".equals(ct.getRole()))
|
||||||
|
.createdAt(ct.getCreatedAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
vo.setTeachers(teacherList);
|
||||||
|
}
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,25 +136,39 @@ public class SchoolClassController {
|
|||||||
|
|
||||||
@Operation(summary = "Get students of class")
|
@Operation(summary = "Get students of class")
|
||||||
@GetMapping("/{id}/students")
|
@GetMapping("/{id}/students")
|
||||||
public Result<PageResult<ClassResponse>> getClassStudents(
|
public Result<PageResult<StudentResponse>> getClassStudents(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
// 验证班级属于当前租户
|
@RequestParam(required = false) String keyword) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
classService.getClassByIdWithTenantCheck(id, tenantId);
|
classService.getClassByIdWithTenantCheck(id, tenantId);
|
||||||
// TODO: 实现获取班级学生
|
Page<Student> page = studentService.getStudentsByClassId(id, pageNum, pageSize, keyword);
|
||||||
return Result.success(PageResult.of(List.of(), 0L, Long.valueOf(pageNum == null ? 1 : pageNum), Long.valueOf(pageSize == null ? 10 : pageSize)));
|
List<StudentResponse> voList = studentMapper.toVO(page.getRecords());
|
||||||
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get teachers of class")
|
@Operation(summary = "Get teachers of class")
|
||||||
@GetMapping("/{id}/teachers")
|
@GetMapping("/{id}/teachers")
|
||||||
public Result<List<ClassTeacherResponse>> getClassTeachers(@PathVariable Long id) {
|
public Result<List<ClassTeacherResponse>> getClassTeachers(@PathVariable Long id) {
|
||||||
// 验证班级属于当前租户
|
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
classService.getClassByIdWithTenantCheck(id, tenantId);
|
classService.getClassByIdWithTenantCheck(id, tenantId);
|
||||||
// TODO: 实现获取班级教师
|
List<ClassTeacher> classTeachers = classTeacherMapper.selectList(
|
||||||
return Result.success(List.of());
|
new LambdaQueryWrapper<ClassTeacher>().eq(ClassTeacher::getClassId, id));
|
||||||
|
List<ClassTeacherResponse> teacherList = new ArrayList<>();
|
||||||
|
for (ClassTeacher ct : classTeachers) {
|
||||||
|
Teacher t = teacherService.getTeacherById(ct.getTeacherId());
|
||||||
|
teacherList.add(ClassTeacherResponse.builder()
|
||||||
|
.id(ct.getId())
|
||||||
|
.classId(ct.getClassId())
|
||||||
|
.teacherId(ct.getTeacherId())
|
||||||
|
.role(ct.getRole())
|
||||||
|
.teacherName(t != null ? t.getName() : null)
|
||||||
|
.isPrimary("班主任".equals(ct.getRole()) || "主班".equals(ct.getRole()))
|
||||||
|
.createdAt(ct.getCreatedAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return Result.success(teacherList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Update class teacher role")
|
@Operation(summary = "Update class teacher role")
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
package com.reading.platform.controller.school;
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.mapper.ParentMapper;
|
import com.reading.platform.common.mapper.ParentMapper;
|
||||||
import com.reading.platform.common.mapper.ParentStudentMapper;
|
import com.reading.platform.mapper.ParentStudentMapper;
|
||||||
import com.reading.platform.common.response.PageResult;
|
import com.reading.platform.common.response.PageResult;
|
||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.request.ParentCreateRequest;
|
import com.reading.platform.dto.request.ParentCreateRequest;
|
||||||
import com.reading.platform.dto.request.ParentUpdateRequest;
|
import com.reading.platform.dto.request.ParentUpdateRequest;
|
||||||
|
import com.reading.platform.dto.response.ChildInfoResponse;
|
||||||
import com.reading.platform.dto.response.ParentResponse;
|
import com.reading.platform.dto.response.ParentResponse;
|
||||||
import com.reading.platform.dto.response.ParentStudentResponse;
|
import com.reading.platform.dto.response.ParentStudentResponse;
|
||||||
import com.reading.platform.entity.Parent;
|
import com.reading.platform.entity.Parent;
|
||||||
|
import com.reading.platform.entity.ParentStudent;
|
||||||
|
import com.reading.platform.service.ParentChildService;
|
||||||
import com.reading.platform.service.ParentService;
|
import com.reading.platform.service.ParentService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -27,6 +31,7 @@ import java.util.List;
|
|||||||
public class SchoolParentController {
|
public class SchoolParentController {
|
||||||
|
|
||||||
private final ParentService parentService;
|
private final ParentService parentService;
|
||||||
|
private final ParentChildService parentChildService;
|
||||||
private final ParentMapper parentMapper;
|
private final ParentMapper parentMapper;
|
||||||
private final ParentStudentMapper parentStudentMapper;
|
private final ParentStudentMapper parentStudentMapper;
|
||||||
|
|
||||||
@ -64,6 +69,12 @@ public class SchoolParentController {
|
|||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Page<Parent> page = parentService.getParentPage(tenantId, pageNum, pageSize, keyword, status);
|
Page<Parent> page = parentService.getParentPage(tenantId, pageNum, pageSize, keyword, status);
|
||||||
List<ParentResponse> voList = parentMapper.toVO(page.getRecords());
|
List<ParentResponse> voList = parentMapper.toVO(page.getRecords());
|
||||||
|
for (ParentResponse vo : voList) {
|
||||||
|
long count = parentStudentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<ParentStudent>()
|
||||||
|
.eq(ParentStudent::getParentId, vo.getId()));
|
||||||
|
vo.setChildrenCount((int) count);
|
||||||
|
}
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +106,29 @@ public class SchoolParentController {
|
|||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Add child to parent (alias for bind student)")
|
||||||
|
@PostMapping("/{parentId}/children/{studentId}")
|
||||||
|
public Result<Void> addChild(
|
||||||
|
@PathVariable Long parentId,
|
||||||
|
@PathVariable Long studentId,
|
||||||
|
@RequestBody(required = false) java.util.Map<String, Object> body) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
String relationship = null;
|
||||||
|
Boolean isPrimary = null;
|
||||||
|
if (body != null) {
|
||||||
|
if (body.containsKey("relationship")) {
|
||||||
|
Object v = body.get("relationship");
|
||||||
|
relationship = v != null ? v.toString() : null;
|
||||||
|
}
|
||||||
|
if (body.containsKey("isPrimary")) {
|
||||||
|
Object v = body.get("isPrimary");
|
||||||
|
isPrimary = v instanceof Boolean ? (Boolean) v : (v != null && Boolean.parseBoolean(v.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentService.bindStudentWithTenantCheck(parentId, studentId, tenantId, relationship, isPrimary);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "Unbind student from parent")
|
@Operation(summary = "Unbind student from parent")
|
||||||
@DeleteMapping("/{parentId}/students/{studentId}")
|
@DeleteMapping("/{parentId}/students/{studentId}")
|
||||||
public Result<Void> unbindStudent(@PathVariable Long parentId, @PathVariable Long studentId) {
|
public Result<Void> unbindStudent(@PathVariable Long parentId, @PathVariable Long studentId) {
|
||||||
@ -103,11 +137,21 @@ public class SchoolParentController {
|
|||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Remove child from parent (alias for unbind student)")
|
||||||
|
@DeleteMapping("/{parentId}/children/{studentId}")
|
||||||
|
public Result<Void> removeChild(@PathVariable Long parentId, @PathVariable Long studentId) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
parentService.unbindStudentWithTenantCheck(parentId, studentId, tenantId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get children of parent")
|
@Operation(summary = "Get children of parent")
|
||||||
@GetMapping("/{id}/children")
|
@GetMapping("/{id}/children")
|
||||||
public Result<List<ParentStudentResponse>> getParentChildren(@PathVariable Long id) {
|
public Result<List<ChildInfoResponse>> getParentChildren(@PathVariable Long id) {
|
||||||
// TODO: 实现获取家长孩子列表
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
return Result.success(List.of());
|
parentService.getParentByIdWithTenantCheck(id, tenantId);
|
||||||
|
List<ChildInfoResponse> children = parentChildService.getChildrenForParent(id);
|
||||||
|
return Result.success(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import com.reading.platform.dto.request.StudentCreateRequest;
|
|||||||
import com.reading.platform.dto.request.StudentUpdateRequest;
|
import com.reading.platform.dto.request.StudentUpdateRequest;
|
||||||
import com.reading.platform.dto.response.StudentResponse;
|
import com.reading.platform.dto.response.StudentResponse;
|
||||||
import com.reading.platform.entity.Student;
|
import com.reading.platform.entity.Student;
|
||||||
|
import com.reading.platform.service.ClassService;
|
||||||
import com.reading.platform.service.StudentService;
|
import com.reading.platform.service.StudentService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -25,6 +26,7 @@ import java.util.List;
|
|||||||
public class SchoolStudentController {
|
public class SchoolStudentController {
|
||||||
|
|
||||||
private final StudentService studentService;
|
private final StudentService studentService;
|
||||||
|
private final ClassService classService;
|
||||||
private final StudentMapper studentMapper;
|
private final StudentMapper studentMapper;
|
||||||
|
|
||||||
@Operation(summary = "Create student")
|
@Operation(summary = "Create student")
|
||||||
@ -48,7 +50,10 @@ public class SchoolStudentController {
|
|||||||
public Result<StudentResponse> getStudent(@PathVariable Long id) {
|
public Result<StudentResponse> getStudent(@PathVariable Long id) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Student student = studentService.getStudentByIdWithTenantCheck(id, tenantId);
|
Student student = studentService.getStudentByIdWithTenantCheck(id, tenantId);
|
||||||
return Result.success(studentMapper.toVO(student));
|
StudentResponse vo = studentMapper.toVO(student);
|
||||||
|
var clazz = classService.getPrimaryClassByStudentId(id);
|
||||||
|
vo.setClassId(clazz != null ? clazz.getId() : null);
|
||||||
|
return Result.success(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "Get student page")
|
@Operation(summary = "Get student page")
|
||||||
@ -58,10 +63,15 @@ public class SchoolStudentController {
|
|||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String keyword,
|
@RequestParam(required = false) String keyword,
|
||||||
@RequestParam(required = false) String grade,
|
@RequestParam(required = false) String grade,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) Long classId) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
Page<Student> page = studentService.getStudentPage(tenantId, pageNum, pageSize, keyword, grade, status);
|
Page<Student> page = studentService.getStudentPage(tenantId, pageNum, pageSize, keyword, grade, status, classId);
|
||||||
List<StudentResponse> voList = studentMapper.toVO(page.getRecords());
|
List<StudentResponse> voList = studentMapper.toVO(page.getRecords());
|
||||||
|
for (StudentResponse vo : voList) {
|
||||||
|
var clazz = classService.getPrimaryClassByStudentId(vo.getId());
|
||||||
|
vo.setClassId(clazz != null ? clazz.getId() : null);
|
||||||
|
}
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.reading.platform.controller.teacher;
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.reading.platform.common.enums.CourseStatus;
|
import com.reading.platform.common.enums.CourseStatus;
|
||||||
import com.reading.platform.common.mapper.ClassMapper;
|
import com.reading.platform.common.mapper.ClassMapper;
|
||||||
@ -10,16 +11,18 @@ import com.reading.platform.common.response.PageResult;
|
|||||||
import com.reading.platform.common.response.Result;
|
import com.reading.platform.common.response.Result;
|
||||||
import com.reading.platform.common.security.SecurityUtils;
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
import com.reading.platform.dto.response.ClassResponse;
|
import com.reading.platform.dto.response.ClassResponse;
|
||||||
|
import com.reading.platform.dto.response.ClassStudentsResponse;
|
||||||
import com.reading.platform.dto.response.CourseResponse;
|
import com.reading.platform.dto.response.CourseResponse;
|
||||||
import com.reading.platform.dto.response.LessonTagResponse;
|
import com.reading.platform.dto.response.LessonTagResponse;
|
||||||
import com.reading.platform.dto.response.StudentResponse;
|
import com.reading.platform.dto.response.StudentResponse;
|
||||||
import com.reading.platform.dto.response.TeacherResponse;
|
import com.reading.platform.dto.response.TeacherResponse;
|
||||||
import com.reading.platform.entity.ClassTeacher;
|
|
||||||
import com.reading.platform.entity.Clazz;
|
import com.reading.platform.entity.Clazz;
|
||||||
|
import com.reading.platform.entity.Lesson;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.entity.CoursePackage;
|
||||||
import com.reading.platform.entity.Student;
|
import com.reading.platform.entity.Student;
|
||||||
import com.reading.platform.entity.Teacher;
|
import com.reading.platform.entity.Teacher;
|
||||||
import com.reading.platform.entity.CourseLesson;
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import com.reading.platform.mapper.LessonMapper;
|
||||||
import com.reading.platform.service.ClassService;
|
import com.reading.platform.service.ClassService;
|
||||||
import com.reading.platform.service.CourseLessonService;
|
import com.reading.platform.service.CourseLessonService;
|
||||||
import com.reading.platform.service.CoursePackageService;
|
import com.reading.platform.service.CoursePackageService;
|
||||||
@ -50,13 +53,45 @@ public class TeacherCourseController {
|
|||||||
private final ClassMapper classMapper;
|
private final ClassMapper classMapper;
|
||||||
private final StudentMapper studentMapper;
|
private final StudentMapper studentMapper;
|
||||||
private final TeacherMapper teacherMapper;
|
private final TeacherMapper teacherMapper;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
|
||||||
@Operation(summary = "获取教师的班级列表")
|
@Operation(summary = "获取教师的班级列表")
|
||||||
@GetMapping("/classes")
|
@GetMapping("/classes")
|
||||||
public Result<List<ClassResponse>> getClasses() {
|
public Result<List<ClassResponse>> getClasses() {
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
List<Clazz> classes = classService.getActiveClassesByTenantId(tenantId);
|
List<Clazz> classes = classService.getClassesByTeacherId(teacherId, tenantId);
|
||||||
return Result.success(classMapper.toVO(classes));
|
|
||||||
|
List<ClassResponse> voList = classMapper.toVO(classes);
|
||||||
|
for (ClassResponse vo : voList) {
|
||||||
|
int studentCount = studentService.getStudentListByClassId(vo.getId()).size();
|
||||||
|
long lessonCount = 0;
|
||||||
|
try {
|
||||||
|
lessonCount = lessonMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Lesson>().eq(Lesson::getClassId, vo.getId()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略 lesson 表不存在等异常
|
||||||
|
}
|
||||||
|
String role = classService.getTeacherRoleInClass(vo.getId(), teacherId);
|
||||||
|
vo.setStudentCount(studentCount);
|
||||||
|
vo.setLessonCount((int) lessonCount);
|
||||||
|
vo.setMyRole(mapRoleToFrontend(role));
|
||||||
|
vo.setIsPrimary(isPrimaryRole(role));
|
||||||
|
}
|
||||||
|
return Result.success(voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String mapRoleToFrontend(String role) {
|
||||||
|
if (role == null) return "ASSIST";
|
||||||
|
return switch (role) {
|
||||||
|
case "班主任", "主班" -> "MAIN";
|
||||||
|
case "保育员" -> "CARE";
|
||||||
|
default -> "ASSIST";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPrimaryRole(String role) {
|
||||||
|
return role != null && ("班主任".equals(role) || "主班".equals(role));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "根据 ID 获取课程")
|
@Operation(summary = "根据 ID 获取课程")
|
||||||
@ -138,14 +173,41 @@ public class TeacherCourseController {
|
|||||||
|
|
||||||
@Operation(summary = "获取班级学生列表")
|
@Operation(summary = "获取班级学生列表")
|
||||||
@GetMapping("/classes/{id}/students")
|
@GetMapping("/classes/{id}/students")
|
||||||
public Result<PageResult<StudentResponse>> getClassStudents(
|
public Result<ClassStudentsResponse> getClassStudents(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||||
@RequestParam(required = false) String keyword) {
|
@RequestParam(required = false) String keyword) {
|
||||||
Page<Student> page = studentService.getStudentsByClassId(id, pageNum, pageSize);
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Clazz clazz = classService.getClassByIdWithTenantCheck(id, tenantId);
|
||||||
|
Page<Student> page = studentService.getStudentsByClassId(id, pageNum, pageSize, keyword);
|
||||||
List<StudentResponse> voList = studentMapper.toVO(page.getRecords());
|
List<StudentResponse> voList = studentMapper.toVO(page.getRecords());
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
|
||||||
|
int studentCount = studentService.getStudentListByClassId(id).size();
|
||||||
|
long lessonCount = 0;
|
||||||
|
try {
|
||||||
|
lessonCount = lessonMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Lesson>().eq(Lesson::getClassId, id));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略 lesson 表不存在等异常
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassStudentsResponse.ClassInfo classInfo = ClassStudentsResponse.ClassInfo.builder()
|
||||||
|
.id(clazz.getId())
|
||||||
|
.name(clazz.getName())
|
||||||
|
.grade(clazz.getGrade())
|
||||||
|
.studentCount(studentCount)
|
||||||
|
.lessonCount((int) lessonCount)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ClassStudentsResponse response = ClassStudentsResponse.builder()
|
||||||
|
.list(voList)
|
||||||
|
.total(page.getTotal())
|
||||||
|
.pageNum(page.getCurrent())
|
||||||
|
.pageSize(page.getSize())
|
||||||
|
.classInfo(classInfo)
|
||||||
|
.build();
|
||||||
|
return Result.success(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取班级教师列表")
|
@Operation(summary = "获取班级教师列表")
|
||||||
|
|||||||
@ -66,9 +66,13 @@ public class TeacherTaskController {
|
|||||||
@RequestParam(required = false) String taskType,
|
@RequestParam(required = false) String taskType,
|
||||||
@RequestParam(required = false) String status) {
|
@RequestParam(required = false) String status) {
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
String typeFilter = (type != null && !type.isEmpty()) ? type : taskType;
|
String typeFilter = (type != null && !type.isEmpty()) ? type : taskType;
|
||||||
Page<Task> page = taskService.getTaskPage(tenantId, pageNum, pageSize, keyword, typeFilter, status);
|
Page<Task> page = taskService.getTaskPage(tenantId, pageNum, pageSize, keyword, typeFilter, status, teacherId);
|
||||||
List<TaskResponse> voList = taskMapper.toVO(page.getRecords());
|
List<TaskResponse> voList = taskMapper.toVO(page.getRecords());
|
||||||
|
for (int i = 0; i < voList.size(); i++) {
|
||||||
|
taskService.enrichTaskResponseWithStats(voList.get(i), page.getRecords().get(i).getId());
|
||||||
|
}
|
||||||
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
return Result.success(PageResult.of(voList, page.getTotal(), page.getCurrent(), page.getSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,4 +39,7 @@ public class StudentUpdateRequest {
|
|||||||
@Schema(description = "状态")
|
@Schema(description = "状态")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "所在班级 ID,更新时调用 assignStudents 调整班级")
|
||||||
|
private Long classId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务完成请求(旧接口,兼容家长端 JSON 提交)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "任务完成请求")
|
||||||
|
public class TaskCompleteRequest {
|
||||||
|
|
||||||
|
@Schema(description = "学生ID", required = true)
|
||||||
|
private Long studentId;
|
||||||
|
|
||||||
|
@Schema(description = "完成内容/反馈")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "附件")
|
||||||
|
private String attachments;
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子列表响应
|
||||||
|
* 对齐前端 ChildInfo 结构
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端孩子列表响应")
|
||||||
|
public class ChildInfoResponse {
|
||||||
|
|
||||||
|
@Schema(description = "学生ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private String gender;
|
||||||
|
|
||||||
|
@Schema(description = "出生日期")
|
||||||
|
private LocalDate birthDate;
|
||||||
|
|
||||||
|
@Schema(description = "与家长关系:FATHER/MOTHER/GRANDFATHER/GRANDMOTHER/OTHER")
|
||||||
|
private String relationship;
|
||||||
|
|
||||||
|
@JsonProperty("class")
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
private ClassInfo classInfo;
|
||||||
|
|
||||||
|
@Schema(description = "阅读次数(student_record 数量)")
|
||||||
|
private Integer readingCount;
|
||||||
|
|
||||||
|
@Schema(description = "上课次数(lesson 记录数)")
|
||||||
|
private Integer lessonCount;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
public static class ClassInfo {
|
||||||
|
@Schema(description = "班级ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "班级名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "年级")
|
||||||
|
private String grade;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子详情响应
|
||||||
|
* 对齐前端 ChildProfile 结构(ChildInfo + stats)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端孩子详情响应")
|
||||||
|
public class ChildProfileResponse {
|
||||||
|
|
||||||
|
@Schema(description = "学生ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private String gender;
|
||||||
|
|
||||||
|
@Schema(description = "出生日期")
|
||||||
|
private LocalDate birthDate;
|
||||||
|
|
||||||
|
@Schema(description = "与家长关系")
|
||||||
|
private String relationship;
|
||||||
|
|
||||||
|
@JsonProperty("class")
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
private ChildInfoResponse.ClassInfo classInfo;
|
||||||
|
|
||||||
|
@Schema(description = "阅读次数")
|
||||||
|
private Integer readingCount;
|
||||||
|
|
||||||
|
@Schema(description = "上课次数")
|
||||||
|
private Integer lessonCount;
|
||||||
|
|
||||||
|
@Schema(description = "统计信息")
|
||||||
|
private StatsInfo stats;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "统计信息")
|
||||||
|
public static class StatsInfo {
|
||||||
|
@Schema(description = "阅读记录数")
|
||||||
|
private Integer lessonRecords;
|
||||||
|
|
||||||
|
@Schema(description = "成长档案数")
|
||||||
|
private Integer growthRecords;
|
||||||
|
|
||||||
|
@Schema(description = "任务完成数")
|
||||||
|
private Integer taskCompletions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -41,4 +41,19 @@ public class ClassResponse {
|
|||||||
|
|
||||||
@Schema(description = "更新时间")
|
@Schema(description = "更新时间")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Schema(description = "学生数量")
|
||||||
|
private Integer studentCount;
|
||||||
|
|
||||||
|
@Schema(description = "授课次数")
|
||||||
|
private Integer lessonCount;
|
||||||
|
|
||||||
|
@Schema(description = "当前教师在该班级的角色:MAIN-主班, ASSIST-配班, CARE-保育员")
|
||||||
|
private String myRole;
|
||||||
|
|
||||||
|
@Schema(description = "当前教师是否班主任")
|
||||||
|
private Boolean isPrimary;
|
||||||
|
|
||||||
|
@Schema(description = "班级教师列表(学校端列表展示)")
|
||||||
|
private java.util.List<ClassTeacherResponse> teachers;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班级学生列表响应(含班级信息)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "班级学生列表响应")
|
||||||
|
public class ClassStudentsResponse {
|
||||||
|
|
||||||
|
@Schema(description = "学生列表")
|
||||||
|
private List<StudentResponse> list;
|
||||||
|
|
||||||
|
@Schema(description = "总记录数")
|
||||||
|
private Long total;
|
||||||
|
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Long pageNum;
|
||||||
|
|
||||||
|
@Schema(description = "每页大小")
|
||||||
|
private Long pageSize;
|
||||||
|
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
private ClassInfo classInfo;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "班级简要信息")
|
||||||
|
public static class ClassInfo {
|
||||||
|
@Schema(description = "班级 ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "班级名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "年级")
|
||||||
|
private String grade;
|
||||||
|
|
||||||
|
@Schema(description = "学生数量")
|
||||||
|
private Integer studentCount;
|
||||||
|
|
||||||
|
@Schema(description = "授课次数")
|
||||||
|
private Integer lessonCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,6 +27,12 @@ public class ClassTeacherResponse {
|
|||||||
@Schema(description = "角色")
|
@Schema(description = "角色")
|
||||||
private String role;
|
private String role;
|
||||||
|
|
||||||
|
@Schema(description = "教师姓名")
|
||||||
|
private String teacherName;
|
||||||
|
|
||||||
|
@Schema(description = "是否班主任")
|
||||||
|
private Boolean isPrimary;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端成长档案响应
|
||||||
|
* 对齐前端 GrowthRecord 结构:images 为数组,recordType,class
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端成长档案响应")
|
||||||
|
public class GrowthRecordForParentResponse {
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "图片URL列表")
|
||||||
|
private List<String> images;
|
||||||
|
|
||||||
|
@Schema(description = "记录日期")
|
||||||
|
private LocalDate recordDate;
|
||||||
|
|
||||||
|
@Schema(description = "记录类型")
|
||||||
|
private String recordType;
|
||||||
|
|
||||||
|
@JsonProperty("class")
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
private ClassInfo classInfo;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "班级信息")
|
||||||
|
public static class ClassInfo {
|
||||||
|
@Schema(description = "班级ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "班级名称")
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端阅读记录响应(孩子在园上课记录)
|
||||||
|
* 对齐前端 LessonRecord 结构
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端阅读记录响应")
|
||||||
|
public class LessonRecordResponse {
|
||||||
|
|
||||||
|
@Schema(description = "记录ID(student_record.id)")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "课程信息")
|
||||||
|
private LessonInfo lesson;
|
||||||
|
|
||||||
|
@Schema(description = "专注度评分")
|
||||||
|
private Integer focus;
|
||||||
|
|
||||||
|
@Schema(description = "参与度评分")
|
||||||
|
private Integer participation;
|
||||||
|
|
||||||
|
@Schema(description = "兴趣度评分")
|
||||||
|
private Integer interest;
|
||||||
|
|
||||||
|
@Schema(description = "理解度评分")
|
||||||
|
private Integer understanding;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String notes;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "课程信息")
|
||||||
|
public static class LessonInfo {
|
||||||
|
@Schema(description = "课程ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "开始时间")
|
||||||
|
private LocalDateTime startDatetime;
|
||||||
|
|
||||||
|
@Schema(description = "结束时间")
|
||||||
|
private LocalDateTime endDatetime;
|
||||||
|
|
||||||
|
@Schema(description = "实际时长(分钟)")
|
||||||
|
private Integer actualDuration;
|
||||||
|
|
||||||
|
@Schema(description = "关联课程包")
|
||||||
|
private CourseInfo course;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "课程包信息")
|
||||||
|
public static class CourseInfo {
|
||||||
|
@Schema(description = "课程包ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "课程名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "绘本名称")
|
||||||
|
private String pictureBookName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端通知响应
|
||||||
|
* 对齐前端 Notification:notificationType、isRead 为 boolean
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端通知响应")
|
||||||
|
public class NotificationForParentResponse {
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@JsonProperty("notificationType")
|
||||||
|
@Schema(description = "通知类型")
|
||||||
|
private String notificationType;
|
||||||
|
|
||||||
|
@Schema(description = "是否已读")
|
||||||
|
private Boolean isRead;
|
||||||
|
|
||||||
|
@Schema(description = "阅读时间")
|
||||||
|
private LocalDateTime readAt;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@ -50,4 +50,7 @@ public class ParentResponse {
|
|||||||
|
|
||||||
@Schema(description = "更新时间")
|
@Schema(description = "更新时间")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Schema(description = "关联孩子数量")
|
||||||
|
private Integer childrenCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,9 @@ public class StudentResponse {
|
|||||||
@Schema(description = "状态")
|
@Schema(description = "状态")
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "所在班级 ID(从 student_class_history 当前关联获取)")
|
||||||
|
private Long classId;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
|||||||
@ -66,4 +66,10 @@ public class TaskResponse {
|
|||||||
|
|
||||||
@Schema(description = "更新时间")
|
@Schema(description = "更新时间")
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Schema(description = "目标人数(任务分配的学生总数)")
|
||||||
|
private Integer targetCount;
|
||||||
|
|
||||||
|
@Schema(description = "已完成人数(已提交或已评价)")
|
||||||
|
private Integer completionCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端任务列表响应(含完成信息与教师评价)
|
||||||
|
* 对齐前端 TaskWithCompletion 结构
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "家长端任务列表响应(含完成信息)")
|
||||||
|
public class TaskWithCompletionResponse {
|
||||||
|
|
||||||
|
@Schema(description = "任务ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "学生ID(用于多孩子聚合时标识任务归属)")
|
||||||
|
private Long studentId;
|
||||||
|
|
||||||
|
@Schema(description = "完成状态:PENDING-待提交/SUBMITTED-已提交/REVIEWED-已评价")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "提交时间")
|
||||||
|
private LocalDateTime submittedAt;
|
||||||
|
|
||||||
|
@Schema(description = "评价时间")
|
||||||
|
private LocalDateTime reviewedAt;
|
||||||
|
|
||||||
|
@Schema(description = "照片URL列表")
|
||||||
|
private List<String> photos;
|
||||||
|
|
||||||
|
@Schema(description = "视频URL")
|
||||||
|
private String videoUrl;
|
||||||
|
|
||||||
|
@Schema(description = "语音URL")
|
||||||
|
private String audioUrl;
|
||||||
|
|
||||||
|
@Schema(description = "阅读心得/完成内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "教师评价")
|
||||||
|
private TaskFeedbackResponse teacherFeedback;
|
||||||
|
|
||||||
|
@Schema(description = "任务基本信息")
|
||||||
|
private TaskInfo task;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "任务基本信息")
|
||||||
|
public static class TaskInfo {
|
||||||
|
@Schema(description = "任务ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "任务标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "任务描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "任务类型")
|
||||||
|
private String taskType;
|
||||||
|
|
||||||
|
@Schema(description = "开始日期")
|
||||||
|
private LocalDate startDate;
|
||||||
|
|
||||||
|
@Schema(description = "截止日期")
|
||||||
|
private LocalDate endDate;
|
||||||
|
|
||||||
|
@Schema(description = "关联绘本名称")
|
||||||
|
private String relatedBookName;
|
||||||
|
|
||||||
|
@Schema(description = "关联课程")
|
||||||
|
private CourseInfo course;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "关联课程信息")
|
||||||
|
public static class CourseInfo {
|
||||||
|
@Schema(description = "课程ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "课程名称")
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ public interface TaskFeedbackMapper extends BaseMapper<TaskFeedback> {
|
|||||||
/**
|
/**
|
||||||
* 根据完成记录ID查询评价
|
* 根据完成记录ID查询评价
|
||||||
*/
|
*/
|
||||||
@Select("SELECT * FROM task_feedback WHERE completion_id = #{completionId} AND deleted_at IS NULL")
|
@Select("SELECT * FROM task_feedback WHERE completion_id = #{completionId} AND deleted = 0")
|
||||||
Optional<TaskFeedback> findByCompletionId(@Param("completionId") Long completionId);
|
Optional<TaskFeedback> findByCompletionId(@Param("completionId") Long completionId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +25,7 @@ public interface TaskFeedbackMapper extends BaseMapper<TaskFeedback> {
|
|||||||
*/
|
*/
|
||||||
@Select("SELECT tf.* FROM task_feedback tf " +
|
@Select("SELECT tf.* FROM task_feedback tf " +
|
||||||
"INNER JOIN task_completion tc ON tf.completion_id = tc.id " +
|
"INNER JOIN task_completion tc ON tf.completion_id = tc.id " +
|
||||||
"WHERE tf.task_id = #{taskId} AND tc.student_id = #{studentId} AND tf.deleted_at IS NULL")
|
"WHERE tf.task_id = #{taskId} AND tc.student_id = #{studentId} AND tf.deleted = 0")
|
||||||
Optional<TaskFeedback> findByTaskIdAndStudentId(@Param("taskId") Long taskId, @Param("studentId") Long studentId);
|
Optional<TaskFeedback> findByTaskIdAndStudentId(@Param("taskId") Long taskId, @Param("studentId") Long studentId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,16 +72,31 @@ public interface ClassService extends com.baomidou.mybatisplus.extension.service
|
|||||||
*/
|
*/
|
||||||
void assignStudentsWithTenantCheck(Long classId, Long tenantId, List<Long> studentIds);
|
void assignStudentsWithTenantCheck(Long classId, Long tenantId, List<Long> studentIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将学生调至指定班级(结束当前班级关联,建立新关联)
|
||||||
|
*/
|
||||||
|
void assignStudentToClass(Long studentId, Long classId, Long tenantId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取班级教师 ID 列表
|
* 获取班级教师 ID 列表
|
||||||
*/
|
*/
|
||||||
List<Long> getTeacherIdsByClassId(Long classId);
|
List<Long> getTeacherIdsByClassId(Long classId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取教师在班级中的角色信息(role 字段)
|
||||||
|
*/
|
||||||
|
String getTeacherRoleInClass(Long classId, Long teacherId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取租户下活跃班级列表
|
* 获取租户下活跃班级列表
|
||||||
*/
|
*/
|
||||||
List<Clazz> getActiveClassesByTenantId(Long tenantId);
|
List<Clazz> getActiveClassesByTenantId(Long tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取教师教授的班级列表(租户下、且教师已分配)
|
||||||
|
*/
|
||||||
|
List<Clazz> getClassesByTeacherId(Long teacherId, Long tenantId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取学生当前所在班级
|
* 获取学生当前所在班级
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -17,6 +17,12 @@ public interface NotificationService extends com.baomidou.mybatisplus.extension.
|
|||||||
|
|
||||||
Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead);
|
Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的通知(支持按类型筛选)
|
||||||
|
* @param notificationType 可选,通知类型筛选
|
||||||
|
*/
|
||||||
|
Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead, String notificationType);
|
||||||
|
|
||||||
void markAsRead(Long id);
|
void markAsRead(Long id);
|
||||||
|
|
||||||
void markAllAsRead(Long recipientId, String recipientType);
|
void markAllAsRead(Long recipientId, String recipientType);
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端鉴权服务
|
||||||
|
* 用于校验家长与学生的亲子关系
|
||||||
|
*/
|
||||||
|
public interface ParentAuthService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验家长与学生的亲子关系
|
||||||
|
* 若不存在关系则抛出 BusinessException(403)
|
||||||
|
*
|
||||||
|
* @param parentId 家长ID
|
||||||
|
* @param studentId 学生ID
|
||||||
|
*/
|
||||||
|
void validateParentStudentRelation(Long parentId, Long studentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验家长是否有权访问指定完成记录(通过 completion 关联的 studentId 校验)
|
||||||
|
*
|
||||||
|
* @param parentId 家长ID
|
||||||
|
* @param completionId 完成记录ID
|
||||||
|
*/
|
||||||
|
void validateParentCanAccessCompletion(Long parentId, Long completionId);
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.reading.platform.dto.response.ChildInfoResponse;
|
||||||
|
import com.reading.platform.dto.response.ChildProfileResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子信息服务
|
||||||
|
*/
|
||||||
|
public interface ParentChildService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取家长的孩子列表(含 relationship、class、readingCount、lessonCount)
|
||||||
|
*/
|
||||||
|
List<ChildInfoResponse> getChildrenForParent(Long parentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取孩子详情(含 stats)
|
||||||
|
*/
|
||||||
|
ChildProfileResponse getChildProfile(Long parentId, Long studentId);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.dto.response.LessonRecordResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端阅读记录服务
|
||||||
|
*/
|
||||||
|
public interface ParentLessonService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取学生的上课记录(家长端,需校验亲子关系)
|
||||||
|
*
|
||||||
|
* @param studentId 学生ID
|
||||||
|
* @param parentId 家长ID(用于校验)
|
||||||
|
* @param pageNum 页码
|
||||||
|
* @param pageSize 每页数量
|
||||||
|
* @return 分页的阅读记录
|
||||||
|
*/
|
||||||
|
PageResult<LessonRecordResponse> getLessonsByStudentId(Long studentId, Long parentId, Integer pageNum, Integer pageSize);
|
||||||
|
}
|
||||||
@ -39,14 +39,20 @@ public interface StudentService extends com.baomidou.mybatisplus.extension.servi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询学生
|
* 分页查询学生
|
||||||
|
* @param classId 可选,按班级筛选
|
||||||
*/
|
*/
|
||||||
Page<Student> getStudentPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String grade, String status);
|
Page<Student> getStudentPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String grade, String status, Long classId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据班级 ID 查询学生
|
* 根据班级 ID 查询学生
|
||||||
*/
|
*/
|
||||||
Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize);
|
Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据班级 ID 查询学生(支持关键词搜索)
|
||||||
|
*/
|
||||||
|
Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize, String keyword);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据班级 ID 查询学生列表(不分页,用于课后记录等场景)
|
* 根据班级 ID 查询学生列表(不分页,用于课后记录等场景)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import com.reading.platform.dto.request.TaskSubmitRequest;
|
|||||||
import com.reading.platform.dto.request.TaskUpdateRequest;
|
import com.reading.platform.dto.request.TaskUpdateRequest;
|
||||||
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
||||||
import com.reading.platform.dto.response.TaskResponse;
|
import com.reading.platform.dto.response.TaskResponse;
|
||||||
|
import com.reading.platform.dto.response.TaskWithCompletionResponse;
|
||||||
import com.reading.platform.entity.Task;
|
import com.reading.platform.entity.Task;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -28,8 +29,24 @@ public interface TaskService extends com.baomidou.mybatisplus.extension.service.
|
|||||||
|
|
||||||
Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status);
|
Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询任务(支持按创建人筛选)
|
||||||
|
* @param creatorId 可选,仅返回该创建人的任务
|
||||||
|
*/
|
||||||
|
Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId);
|
||||||
|
|
||||||
Page<Task> getTasksByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status);
|
Page<Task> getTasksByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端:获取学生的任务列表(含完成信息与教师评价)
|
||||||
|
*/
|
||||||
|
PageResult<TaskWithCompletionResponse> getTasksWithCompletionByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端:获取我的任务列表(聚合多孩子任务)
|
||||||
|
*/
|
||||||
|
PageResult<TaskWithCompletionResponse> getMyTasksForParent(Long parentId, Integer pageNum, Integer pageSize, String status);
|
||||||
|
|
||||||
void deleteTask(Long id);
|
void deleteTask(Long id);
|
||||||
|
|
||||||
void deleteTaskWithTenantCheck(Long id, Long tenantId);
|
void deleteTaskWithTenantCheck(Long id, Long tenantId);
|
||||||
@ -76,4 +93,9 @@ public interface TaskService extends com.baomidou.mybatisplus.extension.service.
|
|||||||
*/
|
*/
|
||||||
TaskResponse getTaskDetailWithTargets(Long taskId, Long tenantId);
|
TaskResponse getTaskDetailWithTargets(Long taskId, Long tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充任务的目标人数与已完成人数(教师端列表统计用)
|
||||||
|
*/
|
||||||
|
void enrichTaskResponseWithStats(TaskResponse response, Long taskId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -236,6 +236,33 @@ public class ClassServiceImpl extends com.baomidou.mybatisplus.extension.service
|
|||||||
assignStudents(classId, studentIds);
|
assignStudents(classId, studentIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void assignStudentToClass(Long studentId, Long classId, Long tenantId) {
|
||||||
|
log.info("将学生调班,学生 ID: {}, 目标班级 ID: {}", studentId, classId);
|
||||||
|
getClassByIdWithTenantCheck(classId, tenantId);
|
||||||
|
// 结束该学生当前所有活跃的班级关联
|
||||||
|
List<StudentClassHistory> existing = studentClassHistoryMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
|
.eq(StudentClassHistory::getStudentId, studentId)
|
||||||
|
.in(StudentClassHistory::getStatus, "active", "ACTIVE")
|
||||||
|
.and(w -> w.isNull(StudentClassHistory::getEndDate)
|
||||||
|
.or()
|
||||||
|
.ge(StudentClassHistory::getEndDate, LocalDate.now()))
|
||||||
|
);
|
||||||
|
for (StudentClassHistory h : existing) {
|
||||||
|
h.setEndDate(LocalDate.now());
|
||||||
|
studentClassHistoryMapper.updateById(h);
|
||||||
|
}
|
||||||
|
// 建立新班级关联
|
||||||
|
StudentClassHistory history = new StudentClassHistory();
|
||||||
|
history.setStudentId(studentId);
|
||||||
|
history.setClassId(classId);
|
||||||
|
history.setStartDate(LocalDate.now());
|
||||||
|
history.setStatus("active");
|
||||||
|
studentClassHistoryMapper.insert(history);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> getTeacherIdsByClassId(Long classId) {
|
public List<Long> getTeacherIdsByClassId(Long classId) {
|
||||||
log.debug("获取班级教师 ID 列表,班级 ID: {}", classId);
|
log.debug("获取班级教师 ID 列表,班级 ID: {}", classId);
|
||||||
@ -251,6 +278,16 @@ public class ClassServiceImpl extends com.baomidou.mybatisplus.extension.service
|
|||||||
return teacherIds;
|
return teacherIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTeacherRoleInClass(Long classId, Long teacherId) {
|
||||||
|
ClassTeacher ct = classTeacherMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<ClassTeacher>()
|
||||||
|
.eq(ClassTeacher::getClassId, classId)
|
||||||
|
.eq(ClassTeacher::getTeacherId, teacherId)
|
||||||
|
);
|
||||||
|
return ct != null ? ct.getRole() : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Clazz> getActiveClassesByTenantId(Long tenantId) {
|
public List<Clazz> getActiveClassesByTenantId(Long tenantId) {
|
||||||
log.debug("获取租户活跃班级列表,租户 ID: {}", tenantId);
|
log.debug("获取租户活跃班级列表,租户 ID: {}", tenantId);
|
||||||
@ -262,6 +299,19 @@ public class ClassServiceImpl extends com.baomidou.mybatisplus.extension.service
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Clazz> getClassesByTeacherId(Long teacherId, Long tenantId) {
|
||||||
|
log.debug("获取教师教授的班级列表,教师 ID: {}, 租户 ID: {}", teacherId, tenantId);
|
||||||
|
|
||||||
|
List<Clazz> allClasses = getActiveClassesByTenantId(tenantId);
|
||||||
|
return allClasses.stream()
|
||||||
|
.filter(clazz -> {
|
||||||
|
List<Long> teacherIds = getTeacherIdsByClassId(clazz.getId());
|
||||||
|
return teacherIds.contains(teacherId);
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Clazz getPrimaryClassByStudentId(Long studentId) {
|
public Clazz getPrimaryClassByStudentId(Long studentId) {
|
||||||
log.debug("获取学生当前所在班级,学生 ID: {}", studentId);
|
log.debug("获取学生当前所在班级,学生 ID: {}", studentId);
|
||||||
@ -270,8 +320,10 @@ public class ClassServiceImpl extends com.baomidou.mybatisplus.extension.service
|
|||||||
StudentClassHistory history = studentClassHistoryMapper.selectOne(
|
StudentClassHistory history = studentClassHistoryMapper.selectOne(
|
||||||
new LambdaQueryWrapper<StudentClassHistory>()
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
.eq(StudentClassHistory::getStudentId, studentId)
|
.eq(StudentClassHistory::getStudentId, studentId)
|
||||||
.eq(StudentClassHistory::getStatus, "active")
|
.in(StudentClassHistory::getStatus, "active", "ACTIVE")
|
||||||
.isNull(StudentClassHistory::getEndDate)
|
.and(w -> w.isNull(StudentClassHistory::getEndDate)
|
||||||
|
.or()
|
||||||
|
.ge(StudentClassHistory::getEndDate, LocalDate.now()))
|
||||||
.orderByDesc(StudentClassHistory::getStartDate)
|
.orderByDesc(StudentClassHistory::getStartDate)
|
||||||
.last("LIMIT 1")
|
.last("LIMIT 1")
|
||||||
);
|
);
|
||||||
|
|||||||
@ -73,6 +73,11 @@ public class NotificationServiceImpl extends ServiceImpl<NotificationMapper, Not
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead) {
|
public Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead) {
|
||||||
|
return getMyNotifications(recipientId, recipientType, pageNum, pageSize, isRead, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Notification> getMyNotifications(Long recipientId, String recipientType, Integer pageNum, Integer pageSize, Integer isRead, String notificationType) {
|
||||||
Page<Notification> page = new Page<>(pageNum, pageSize);
|
Page<Notification> page = new Page<>(pageNum, pageSize);
|
||||||
LambdaQueryWrapper<Notification> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Notification> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
@ -85,6 +90,9 @@ public class NotificationServiceImpl extends ServiceImpl<NotificationMapper, Not
|
|||||||
if (isRead != null) {
|
if (isRead != null) {
|
||||||
wrapper.eq(Notification::getIsRead, isRead);
|
wrapper.eq(Notification::getIsRead, isRead);
|
||||||
}
|
}
|
||||||
|
if (notificationType != null && !notificationType.isEmpty()) {
|
||||||
|
wrapper.eq(Notification::getType, notificationType);
|
||||||
|
}
|
||||||
wrapper.orderByDesc(Notification::getCreatedAt);
|
wrapper.orderByDesc(Notification::getCreatedAt);
|
||||||
|
|
||||||
return notificationMapper.selectPage(page, wrapper);
|
return notificationMapper.selectPage(page, wrapper);
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.reading.platform.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.entity.ParentStudent;
|
||||||
|
import com.reading.platform.entity.TaskCompletion;
|
||||||
|
import com.reading.platform.mapper.ParentStudentMapper;
|
||||||
|
import com.reading.platform.mapper.TaskCompletionMapper;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端鉴权服务实现
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ParentAuthServiceImpl implements ParentAuthService {
|
||||||
|
|
||||||
|
private final ParentStudentMapper parentStudentMapper;
|
||||||
|
private final TaskCompletionMapper taskCompletionMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateParentStudentRelation(Long parentId, Long studentId) {
|
||||||
|
if (parentId == null || studentId == null) {
|
||||||
|
throw new BusinessException(ErrorCode.INVALID_PARAMETER, "家长ID或学生ID不能为空");
|
||||||
|
}
|
||||||
|
Long count = parentStudentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<ParentStudent>()
|
||||||
|
.eq(ParentStudent::getParentId, parentId)
|
||||||
|
.eq(ParentStudent::getStudentId, studentId)
|
||||||
|
);
|
||||||
|
if (count == null || count == 0) {
|
||||||
|
throw new BusinessException(ErrorCode.PERMISSION_DENIED, "无权访问该学生的数据");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateParentCanAccessCompletion(Long parentId, Long completionId) {
|
||||||
|
TaskCompletion completion = taskCompletionMapper.selectById(completionId);
|
||||||
|
if (completion == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "完成记录不存在");
|
||||||
|
}
|
||||||
|
validateParentStudentRelation(parentId, completion.getStudentId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
package com.reading.platform.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.dto.response.ChildInfoResponse;
|
||||||
|
import com.reading.platform.dto.response.ChildProfileResponse;
|
||||||
|
import com.reading.platform.entity.*;
|
||||||
|
import com.reading.platform.mapper.GrowthRecordMapper;
|
||||||
|
import com.reading.platform.mapper.ParentStudentMapper;
|
||||||
|
import com.reading.platform.mapper.StudentMapper;
|
||||||
|
import com.reading.platform.mapper.StudentRecordMapper;
|
||||||
|
import com.reading.platform.mapper.TaskCompletionMapper;
|
||||||
|
import com.reading.platform.service.ClassService;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
|
import com.reading.platform.service.ParentChildService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端孩子信息服务实现
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ParentChildServiceImpl implements ParentChildService {
|
||||||
|
|
||||||
|
private final ParentStudentMapper parentStudentMapper;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
private final StudentRecordMapper studentRecordMapper;
|
||||||
|
private final TaskCompletionMapper taskCompletionMapper;
|
||||||
|
private final GrowthRecordMapper growthRecordMapper;
|
||||||
|
private final ParentAuthService parentAuthService;
|
||||||
|
private final ClassService classService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ChildInfoResponse> getChildrenForParent(Long parentId) {
|
||||||
|
List<ParentStudent> parentStudents = parentStudentMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ParentStudent>()
|
||||||
|
.eq(ParentStudent::getParentId, parentId)
|
||||||
|
);
|
||||||
|
if (parentStudents.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> studentIds = parentStudents.stream()
|
||||||
|
.map(ParentStudent::getStudentId)
|
||||||
|
.toList();
|
||||||
|
Map<Long, String> relationshipMap = parentStudents.stream()
|
||||||
|
.collect(Collectors.toMap(ParentStudent::getStudentId, ps -> ps.getRelationship() != null ? ps.getRelationship() : "OTHER"));
|
||||||
|
|
||||||
|
List<Student> students = studentMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<Student>()
|
||||||
|
.in(Student::getId, studentIds)
|
||||||
|
.eq(Student::getStatus, "active")
|
||||||
|
);
|
||||||
|
|
||||||
|
List<ChildInfoResponse> result = new ArrayList<>();
|
||||||
|
for (Student student : students) {
|
||||||
|
ChildInfoResponse.ClassInfo classInfo = buildClassInfo(student.getId());
|
||||||
|
int readingCount = countStudentRecords(student.getId());
|
||||||
|
int lessonCount = readingCount; // student_record 即上课记录
|
||||||
|
|
||||||
|
result.add(ChildInfoResponse.builder()
|
||||||
|
.id(student.getId())
|
||||||
|
.name(student.getName())
|
||||||
|
.gender(student.getGender())
|
||||||
|
.birthDate(student.getBirthDate())
|
||||||
|
.relationship(relationshipMap.getOrDefault(student.getId(), "OTHER"))
|
||||||
|
.classInfo(classInfo)
|
||||||
|
.readingCount(readingCount)
|
||||||
|
.lessonCount(lessonCount)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChildProfileResponse getChildProfile(Long parentId, Long studentId) {
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, studentId);
|
||||||
|
|
||||||
|
Student student = studentMapper.selectById(studentId);
|
||||||
|
if (student == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParentStudent ps = parentStudentMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<ParentStudent>()
|
||||||
|
.eq(ParentStudent::getParentId, parentId)
|
||||||
|
.eq(ParentStudent::getStudentId, studentId)
|
||||||
|
);
|
||||||
|
String relationship = ps != null && ps.getRelationship() != null ? ps.getRelationship() : "OTHER";
|
||||||
|
|
||||||
|
ChildInfoResponse.ClassInfo classInfo = buildClassInfo(studentId);
|
||||||
|
int readingCount = countStudentRecords(studentId);
|
||||||
|
int lessonCount = readingCount;
|
||||||
|
|
||||||
|
int growthRecords = growthRecordMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<GrowthRecord>()
|
||||||
|
.eq(GrowthRecord::getStudentId, studentId)
|
||||||
|
).intValue();
|
||||||
|
int taskCompletions = taskCompletionMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<TaskCompletion>()
|
||||||
|
.eq(TaskCompletion::getStudentId, studentId)
|
||||||
|
.in(TaskCompletion::getStatus, "SUBMITTED", "REVIEWED")
|
||||||
|
).intValue();
|
||||||
|
|
||||||
|
return ChildProfileResponse.builder()
|
||||||
|
.id(student.getId())
|
||||||
|
.name(student.getName())
|
||||||
|
.gender(student.getGender())
|
||||||
|
.birthDate(student.getBirthDate())
|
||||||
|
.relationship(relationship)
|
||||||
|
.classInfo(classInfo)
|
||||||
|
.readingCount(readingCount)
|
||||||
|
.lessonCount(lessonCount)
|
||||||
|
.stats(ChildProfileResponse.StatsInfo.builder()
|
||||||
|
.lessonRecords(readingCount)
|
||||||
|
.growthRecords(growthRecords)
|
||||||
|
.taskCompletions(taskCompletions)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChildInfoResponse.ClassInfo buildClassInfo(Long studentId) {
|
||||||
|
Clazz clazz = classService.getPrimaryClassByStudentId(studentId);
|
||||||
|
if (clazz == null) {
|
||||||
|
return ChildInfoResponse.ClassInfo.builder()
|
||||||
|
.id(0L)
|
||||||
|
.name("未分班")
|
||||||
|
.grade("-")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
return ChildInfoResponse.ClassInfo.builder()
|
||||||
|
.id(clazz.getId())
|
||||||
|
.name(clazz.getName())
|
||||||
|
.grade(clazz.getGrade() != null ? clazz.getGrade() : "-")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countStudentRecords(Long studentId) {
|
||||||
|
return studentRecordMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<StudentRecord>()
|
||||||
|
.eq(StudentRecord::getStudentId, studentId)
|
||||||
|
).intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package com.reading.platform.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.dto.response.LessonRecordResponse;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.entity.Lesson;
|
||||||
|
import com.reading.platform.entity.StudentRecord;
|
||||||
|
import com.reading.platform.mapper.CoursePackageMapper;
|
||||||
|
import com.reading.platform.mapper.LessonMapper;
|
||||||
|
import com.reading.platform.mapper.StudentRecordMapper;
|
||||||
|
import com.reading.platform.service.ParentAuthService;
|
||||||
|
import com.reading.platform.service.ParentLessonService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 家长端阅读记录服务实现
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ParentLessonServiceImpl implements ParentLessonService {
|
||||||
|
|
||||||
|
private final StudentRecordMapper studentRecordMapper;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
private final CoursePackageMapper coursePackageMapper;
|
||||||
|
private final ParentAuthService parentAuthService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<LessonRecordResponse> getLessonsByStudentId(Long studentId, Long parentId, Integer pageNum, Integer pageSize) {
|
||||||
|
parentAuthService.validateParentStudentRelation(parentId, studentId);
|
||||||
|
|
||||||
|
Page<StudentRecord> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<StudentRecord> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(StudentRecord::getStudentId, studentId)
|
||||||
|
.orderByDesc(StudentRecord::getCreatedAt);
|
||||||
|
Page<StudentRecord> recordPage = studentRecordMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
|
List<LessonRecordResponse> responses = new ArrayList<>();
|
||||||
|
for (StudentRecord record : recordPage.getRecords()) {
|
||||||
|
Lesson lesson = lessonMapper.selectById(record.getLessonId());
|
||||||
|
if (lesson == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LessonRecordResponse.LessonInfo.LessonInfoBuilder lessonInfoBuilder = LessonRecordResponse.LessonInfo.builder()
|
||||||
|
.id(lesson.getId())
|
||||||
|
.startDatetime(lesson.getStartDatetime())
|
||||||
|
.endDatetime(lesson.getEndDatetime())
|
||||||
|
.actualDuration(lesson.getActualDuration());
|
||||||
|
|
||||||
|
if (lesson.getCourseId() != null) {
|
||||||
|
CoursePackage course = coursePackageMapper.selectById(lesson.getCourseId());
|
||||||
|
if (course != null) {
|
||||||
|
lessonInfoBuilder.course(LessonRecordResponse.CourseInfo.builder()
|
||||||
|
.id(course.getId())
|
||||||
|
.name(course.getName())
|
||||||
|
.pictureBookName(course.getPictureBookName())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.add(LessonRecordResponse.builder()
|
||||||
|
.id(record.getId())
|
||||||
|
.lesson(lessonInfoBuilder.build())
|
||||||
|
.focus(record.getFocus())
|
||||||
|
.participation(record.getParticipation())
|
||||||
|
.interest(record.getInterest())
|
||||||
|
.understanding(record.getUnderstanding())
|
||||||
|
.notes(record.getNotes())
|
||||||
|
.createdAt(record.getCreatedAt())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageResult.of(responses, recordPage.getTotal(), recordPage.getCurrent(), recordPage.getSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,6 +12,7 @@ import com.reading.platform.entity.StudentClassHistory;
|
|||||||
import com.reading.platform.mapper.ParentStudentMapper;
|
import com.reading.platform.mapper.ParentStudentMapper;
|
||||||
import com.reading.platform.mapper.StudentClassHistoryMapper;
|
import com.reading.platform.mapper.StudentClassHistoryMapper;
|
||||||
import com.reading.platform.mapper.StudentMapper;
|
import com.reading.platform.mapper.StudentMapper;
|
||||||
|
import com.reading.platform.service.ClassService;
|
||||||
import com.reading.platform.service.StudentService;
|
import com.reading.platform.service.StudentService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -35,6 +36,7 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
private final StudentMapper studentMapper;
|
private final StudentMapper studentMapper;
|
||||||
private final ParentStudentMapper parentStudentMapper;
|
private final ParentStudentMapper parentStudentMapper;
|
||||||
private final StudentClassHistoryMapper studentClassHistoryMapper;
|
private final StudentClassHistoryMapper studentClassHistoryMapper;
|
||||||
|
private final ClassService classService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -99,6 +101,10 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
|
|
||||||
studentMapper.updateById(student);
|
studentMapper.updateById(student);
|
||||||
|
|
||||||
|
if (request.getClassId() != null) {
|
||||||
|
classService.assignStudentToClass(id, request.getClassId(), student.getTenantId());
|
||||||
|
}
|
||||||
|
|
||||||
log.info("学生更新成功,ID: {}", id);
|
log.info("学生更新成功,ID: {}", id);
|
||||||
return student;
|
return student;
|
||||||
}
|
}
|
||||||
@ -135,7 +141,10 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Student> getStudentPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String grade, String status) {
|
public Page<Student> getStudentPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String grade, String status, Long classId) {
|
||||||
|
if (classId != null) {
|
||||||
|
return getStudentsByClassId(classId, pageNum, pageSize, keyword);
|
||||||
|
}
|
||||||
log.debug("分页查询学生,页码:{},每页数量:{}", pageNum, pageSize);
|
log.debug("分页查询学生,页码:{},每页数量:{}", pageNum, pageSize);
|
||||||
|
|
||||||
Page<Student> page = new Page<>(pageNum, pageSize);
|
Page<Student> page = new Page<>(pageNum, pageSize);
|
||||||
@ -163,18 +172,24 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize) {
|
public Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize) {
|
||||||
|
return getStudentsByClassId(classId, pageNum, pageSize, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize, String keyword) {
|
||||||
log.debug("根据班级查询学生,班级 ID: {},页码:{}", classId, pageNum);
|
log.debug("根据班级查询学生,班级 ID: {},页码:{}", classId, pageNum);
|
||||||
|
|
||||||
Page<Student> page = new Page<>(pageNum, pageSize);
|
Page<Student> page = new Page<>(pageNum, pageSize);
|
||||||
|
|
||||||
// 获取班级中活跃的学生 ID
|
// 获取班级中活跃的学生 ID
|
||||||
|
// 条件:status 为 active/ACTIVE,且 (end_date 为空 或 end_date >= 今天)
|
||||||
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
||||||
new LambdaQueryWrapper<StudentClassHistory>()
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
.eq(StudentClassHistory::getClassId, classId)
|
.eq(StudentClassHistory::getClassId, classId)
|
||||||
.eq(StudentClassHistory::getStatus, "active")
|
.in(StudentClassHistory::getStatus, "active", "ACTIVE")
|
||||||
.isNull(StudentClassHistory::getEndDate)
|
.and(w -> w.isNull(StudentClassHistory::getEndDate)
|
||||||
.or()
|
.or()
|
||||||
.ge(StudentClassHistory::getEndDate, LocalDate.now())
|
.ge(StudentClassHistory::getEndDate, LocalDate.now()))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (histories.isEmpty()) {
|
if (histories.isEmpty()) {
|
||||||
@ -183,6 +198,7 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
|
|
||||||
List<Long> studentIds = histories.stream()
|
List<Long> studentIds = histories.stream()
|
||||||
.map(StudentClassHistory::getStudentId)
|
.map(StudentClassHistory::getStudentId)
|
||||||
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (studentIds.isEmpty()) {
|
if (studentIds.isEmpty()) {
|
||||||
@ -191,8 +207,16 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
|
|
||||||
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.in(Student::getId, studentIds)
|
wrapper.in(Student::getId, studentIds)
|
||||||
.eq(Student::getStatus, "active")
|
.eq(Student::getStatus, "active");
|
||||||
.orderByAsc(Student::getName);
|
|
||||||
|
if (StringUtils.hasText(keyword)) {
|
||||||
|
wrapper.and(w -> w
|
||||||
|
.like(Student::getName, keyword)
|
||||||
|
.or()
|
||||||
|
.like(Student::getStudentNo, keyword)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
wrapper.orderByAsc(Student::getName);
|
||||||
|
|
||||||
return studentMapper.selectPage(page, wrapper);
|
return studentMapper.selectPage(page, wrapper);
|
||||||
}
|
}
|
||||||
@ -204,10 +228,10 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
||||||
new LambdaQueryWrapper<StudentClassHistory>()
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
.eq(StudentClassHistory::getClassId, classId)
|
.eq(StudentClassHistory::getClassId, classId)
|
||||||
.eq(StudentClassHistory::getStatus, "active")
|
.in(StudentClassHistory::getStatus, "active", "ACTIVE")
|
||||||
.isNull(StudentClassHistory::getEndDate)
|
.and(w -> w.isNull(StudentClassHistory::getEndDate)
|
||||||
.or()
|
.or()
|
||||||
.ge(StudentClassHistory::getEndDate, LocalDate.now())
|
.ge(StudentClassHistory::getEndDate, LocalDate.now()))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (histories.isEmpty()) {
|
if (histories.isEmpty()) {
|
||||||
@ -278,8 +302,10 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
||||||
new LambdaQueryWrapper<StudentClassHistory>()
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
.in(StudentClassHistory::getClassId, classIds)
|
.in(StudentClassHistory::getClassId, classIds)
|
||||||
.eq(StudentClassHistory::getStatus, "active")
|
.in(StudentClassHistory::getStatus, "active", "ACTIVE")
|
||||||
.isNull(StudentClassHistory::getEndDate)
|
.and(w -> w.isNull(StudentClassHistory::getEndDate)
|
||||||
|
.or()
|
||||||
|
.ge(StudentClassHistory::getEndDate, LocalDate.now()))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (histories.isEmpty()) {
|
if (histories.isEmpty()) {
|
||||||
|
|||||||
@ -14,7 +14,10 @@ import com.reading.platform.dto.request.TaskUpdateRequest;
|
|||||||
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
import com.reading.platform.dto.response.TaskCompletionDetailResponse;
|
||||||
import com.reading.platform.dto.response.TaskFeedbackResponse;
|
import com.reading.platform.dto.response.TaskFeedbackResponse;
|
||||||
import com.reading.platform.dto.response.TaskResponse;
|
import com.reading.platform.dto.response.TaskResponse;
|
||||||
|
import com.reading.platform.dto.response.TaskWithCompletionResponse;
|
||||||
import com.reading.platform.entity.*;
|
import com.reading.platform.entity.*;
|
||||||
|
import com.reading.platform.mapper.CoursePackageMapper;
|
||||||
|
import com.reading.platform.mapper.ParentStudentMapper;
|
||||||
import com.reading.platform.mapper.TaskCompletionMapper;
|
import com.reading.platform.mapper.TaskCompletionMapper;
|
||||||
import com.reading.platform.mapper.TaskMapper;
|
import com.reading.platform.mapper.TaskMapper;
|
||||||
import com.reading.platform.mapper.TaskTargetMapper;
|
import com.reading.platform.mapper.TaskTargetMapper;
|
||||||
@ -29,7 +32,9 @@ import java.time.LocalDate;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -40,6 +45,8 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
private final TaskMapper taskMapper;
|
private final TaskMapper taskMapper;
|
||||||
private final TaskTargetMapper taskTargetMapper;
|
private final TaskTargetMapper taskTargetMapper;
|
||||||
private final TaskCompletionMapper taskCompletionMapper;
|
private final TaskCompletionMapper taskCompletionMapper;
|
||||||
|
private final CoursePackageMapper coursePackageMapper;
|
||||||
|
private final ParentStudentMapper parentStudentMapper;
|
||||||
private final StudentService studentService;
|
private final StudentService studentService;
|
||||||
private final ClassService classService;
|
private final ClassService classService;
|
||||||
private final TaskFeedbackService taskFeedbackService;
|
private final TaskFeedbackService taskFeedbackService;
|
||||||
@ -164,11 +171,19 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status) {
|
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status) {
|
||||||
|
return getTaskPage(tenantId, pageNum, pageSize, keyword, type, status, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Task> getTaskPage(Long tenantId, Integer pageNum, Integer pageSize, String keyword, String type, String status, Long creatorId) {
|
||||||
Page<Task> page = new Page<>(pageNum, pageSize);
|
Page<Task> page = new Page<>(pageNum, pageSize);
|
||||||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
wrapper.eq(Task::getTenantId, tenantId);
|
wrapper.eq(Task::getTenantId, tenantId);
|
||||||
|
|
||||||
|
if (creatorId != null) {
|
||||||
|
wrapper.eq(Task::getCreatorId, creatorId);
|
||||||
|
}
|
||||||
if (StringUtils.hasText(keyword)) {
|
if (StringUtils.hasText(keyword)) {
|
||||||
wrapper.like(Task::getTitle, keyword);
|
wrapper.like(Task::getTitle, keyword);
|
||||||
}
|
}
|
||||||
@ -187,18 +202,32 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
public Page<Task> getTasksByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status) {
|
public Page<Task> getTasksByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status) {
|
||||||
Page<Task> page = new Page<>(pageNum, pageSize);
|
Page<Task> page = new Page<>(pageNum, pageSize);
|
||||||
|
|
||||||
// Get task IDs from task_completions for this student
|
// 从 task_target 获取任务:分配给该学生(target_type=STUDENT) 或 学生所在班级(target_type=CLASS)
|
||||||
List<TaskCompletion> completions = taskCompletionMapper.selectList(
|
List<Long> taskIds = new ArrayList<>();
|
||||||
new LambdaQueryWrapper<TaskCompletion>().eq(TaskCompletion::getStudentId, studentId)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (completions.isEmpty()) {
|
// 1. 直接分配给学生的任务
|
||||||
return page;
|
List<TaskTarget> studentTargets = taskTargetMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TaskTarget>()
|
||||||
|
.in(TaskTarget::getTargetType, "STUDENT", "student")
|
||||||
|
.eq(TaskTarget::getTargetId, studentId)
|
||||||
|
);
|
||||||
|
taskIds.addAll(studentTargets.stream().map(TaskTarget::getTaskId).distinct().toList());
|
||||||
|
|
||||||
|
// 2. 学生所在班级的任务
|
||||||
|
Clazz primaryClass = classService.getPrimaryClassByStudentId(studentId);
|
||||||
|
if (primaryClass != null) {
|
||||||
|
List<TaskTarget> classTargets = taskTargetMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TaskTarget>()
|
||||||
|
.in(TaskTarget::getTargetType, "CLASS", "class")
|
||||||
|
.eq(TaskTarget::getTargetId, primaryClass.getId())
|
||||||
|
);
|
||||||
|
taskIds.addAll(classTargets.stream().map(TaskTarget::getTaskId).distinct().toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Long> taskIds = completions.stream()
|
taskIds = taskIds.stream().distinct().toList();
|
||||||
.map(TaskCompletion::getTaskId)
|
if (taskIds.isEmpty()) {
|
||||||
.toList();
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.in(Task::getId, taskIds);
|
wrapper.in(Task::getId, taskIds);
|
||||||
@ -211,6 +240,86 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
return taskMapper.selectPage(page, wrapper);
|
return taskMapper.selectPage(page, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<TaskWithCompletionResponse> getTasksWithCompletionByStudentId(Long studentId, Integer pageNum, Integer pageSize, String status) {
|
||||||
|
Page<Task> page = getTasksByStudentId(studentId, pageNum, pageSize, status);
|
||||||
|
List<TaskWithCompletionResponse> responses = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Task task : page.getRecords()) {
|
||||||
|
TaskCompletion completion = taskCompletionMapper.selectOne(
|
||||||
|
new LambdaQueryWrapper<TaskCompletion>()
|
||||||
|
.eq(TaskCompletion::getTaskId, task.getId())
|
||||||
|
.eq(TaskCompletion::getStudentId, studentId)
|
||||||
|
);
|
||||||
|
|
||||||
|
TaskWithCompletionResponse.TaskInfo.TaskInfoBuilder taskInfoBuilder = TaskWithCompletionResponse.TaskInfo.builder()
|
||||||
|
.id(task.getId())
|
||||||
|
.title(task.getTitle())
|
||||||
|
.description(task.getDescription())
|
||||||
|
.taskType(task.getType())
|
||||||
|
.startDate(task.getStartDate())
|
||||||
|
.endDate(task.getDueDate())
|
||||||
|
.relatedBookName(task.getRelatedBookName());
|
||||||
|
|
||||||
|
if (task.getCourseId() != null) {
|
||||||
|
CoursePackage course = coursePackageMapper.selectById(task.getCourseId());
|
||||||
|
if (course != null) {
|
||||||
|
taskInfoBuilder.course(TaskWithCompletionResponse.CourseInfo.builder()
|
||||||
|
.id(course.getId())
|
||||||
|
.name(course.getName())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskWithCompletionResponse.TaskWithCompletionResponseBuilder builder = TaskWithCompletionResponse.builder()
|
||||||
|
.id(task.getId())
|
||||||
|
.studentId(studentId)
|
||||||
|
.task(taskInfoBuilder.build());
|
||||||
|
|
||||||
|
if (completion != null) {
|
||||||
|
builder.status(completion.getStatus())
|
||||||
|
.submittedAt(completion.getSubmittedAt())
|
||||||
|
.reviewedAt(completion.getReviewedAt())
|
||||||
|
.photos(parsePhotos(completion.getPhotos()))
|
||||||
|
.videoUrl(completion.getVideoUrl())
|
||||||
|
.audioUrl(completion.getAudioUrl())
|
||||||
|
.content(completion.getContent());
|
||||||
|
|
||||||
|
TaskFeedbackResponse feedback = taskFeedbackService.getFeedbackByCompletionId(completion.getId());
|
||||||
|
builder.teacherFeedback(feedback);
|
||||||
|
} else {
|
||||||
|
builder.status("PENDING");
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.add(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageResult.of(responses, page.getTotal(), page.getCurrent(), page.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<TaskWithCompletionResponse> getMyTasksForParent(Long parentId, Integer pageNum, Integer pageSize, String status) {
|
||||||
|
List<Long> studentIds = studentService.getStudentsByParentId(parentId).stream()
|
||||||
|
.map(Student::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (studentIds.isEmpty()) {
|
||||||
|
return PageResult.empty();
|
||||||
|
}
|
||||||
|
List<TaskWithCompletionResponse> all = new ArrayList<>();
|
||||||
|
for (Long studentId : studentIds) {
|
||||||
|
PageResult<TaskWithCompletionResponse> page = getTasksWithCompletionByStudentId(studentId, 1, 500, status);
|
||||||
|
if (page.getList() != null) {
|
||||||
|
all.addAll(page.getList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all.sort(Comparator.comparing(r -> r.getTask().getEndDate() != null ? r.getTask().getEndDate().atStartOfDay() : java.time.LocalDateTime.MIN, Comparator.reverseOrder()));
|
||||||
|
long total = all.size();
|
||||||
|
int from = (int) ((pageNum - 1) * pageSize);
|
||||||
|
int to = Math.min(from + pageSize, all.size());
|
||||||
|
List<TaskWithCompletionResponse> paged = from < all.size() ? all.subList(from, to) : Collections.emptyList();
|
||||||
|
return PageResult.of(paged, total, (long) pageNum, (long) pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteTask(Long id) {
|
public void deleteTask(Long id) {
|
||||||
@ -285,6 +394,11 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
// 验证任务存在且属于该租户
|
// 验证任务存在且属于该租户
|
||||||
Task task = getTaskByIdWithTenantCheck(taskId, tenantId);
|
Task task = getTaskByIdWithTenantCheck(taskId, tenantId);
|
||||||
|
|
||||||
|
// status=PENDING 待提交:返回参与任务且与家长关联、尚未提交的学生(无 task_completion 记录)
|
||||||
|
if ("PENDING".equalsIgnoreCase(status)) {
|
||||||
|
return getPendingCompletions(taskId, tenantId, pageNum, pageSize, task);
|
||||||
|
}
|
||||||
|
|
||||||
Page<TaskCompletion> page = new Page<>(pageNum, pageSize);
|
Page<TaskCompletion> page = new Page<>(pageNum, pageSize);
|
||||||
LambdaQueryWrapper<TaskCompletion> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<TaskCompletion> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(TaskCompletion::getTaskId, taskId);
|
wrapper.eq(TaskCompletion::getTaskId, taskId);
|
||||||
@ -303,6 +417,105 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
return PageResult.of(responses, completionPage.getTotal(), completionPage.getCurrent(), completionPage.getSize());
|
return PageResult.of(responses, completionPage.getTotal(), completionPage.getCurrent(), completionPage.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取待提交学生列表:参与任务(班级或指定学生)且与家长关联、尚未提交的学生
|
||||||
|
*/
|
||||||
|
private PageResult<TaskCompletionDetailResponse> getPendingCompletions(Long taskId, Long tenantId, Integer pageNum, Integer pageSize, Task task) {
|
||||||
|
List<Long> targetStudentIds = getTargetStudentIdsForTask(taskId);
|
||||||
|
if (targetStudentIds.isEmpty()) {
|
||||||
|
return PageResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅保留与家长关联的学生(parent_student 有记录)
|
||||||
|
List<Long> studentIdsWithParent = parentStudentMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ParentStudent>()
|
||||||
|
.in(ParentStudent::getStudentId, targetStudentIds)
|
||||||
|
).stream().map(ParentStudent::getStudentId).distinct().toList();
|
||||||
|
|
||||||
|
if (studentIdsWithParent.isEmpty()) {
|
||||||
|
return PageResult.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除已提交或已评价的学生(已有 task_completion 且 status 非 PENDING)
|
||||||
|
List<TaskCompletion> existing = taskCompletionMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TaskCompletion>()
|
||||||
|
.eq(TaskCompletion::getTaskId, taskId)
|
||||||
|
.in(TaskCompletion::getStudentId, studentIdsWithParent)
|
||||||
|
.in(TaskCompletion::getStatus, "SUBMITTED", "REVIEWED", "submitted", "reviewed")
|
||||||
|
);
|
||||||
|
List<Long> submittedStudentIds = existing.stream().map(TaskCompletion::getStudentId).distinct().toList();
|
||||||
|
List<Long> pendingStudentIds = studentIdsWithParent.stream()
|
||||||
|
.filter(id -> !submittedStudentIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
int from = (pageNum - 1) * pageSize;
|
||||||
|
int to = Math.min(from + pageSize, pendingStudentIds.size());
|
||||||
|
List<Long> pagedIds = from < pendingStudentIds.size() ? pendingStudentIds.subList(from, to) : List.of();
|
||||||
|
|
||||||
|
List<TaskCompletionDetailResponse> responses = pagedIds.stream()
|
||||||
|
.map(studentId -> buildPendingCompletionResponse(taskId, task.getTitle(), studentId))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return PageResult.of(responses, (long) pendingStudentIds.size(), (long) pageNum, (long) pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务目标学生 ID 列表(班级学生或指定学生)
|
||||||
|
*/
|
||||||
|
private List<Long> getTargetStudentIdsForTask(Long taskId) {
|
||||||
|
List<TaskTarget> targets = taskTargetMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TaskTarget>().eq(TaskTarget::getTaskId, taskId)
|
||||||
|
);
|
||||||
|
if (targets.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> studentIds = new ArrayList<>();
|
||||||
|
for (TaskTarget t : targets) {
|
||||||
|
String tt = t.getTargetType() != null ? t.getTargetType().toUpperCase() : "";
|
||||||
|
if ("CLASS".equals(tt) || "class".equals(t.getTargetType())) {
|
||||||
|
studentIds.addAll(studentService.getStudentListByClassId(t.getTargetId()).stream()
|
||||||
|
.map(Student::getId).toList());
|
||||||
|
} else if ("STUDENT".equals(tt) || "student".equals(t.getTargetType())) {
|
||||||
|
studentIds.add(t.getTargetId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return studentIds.stream().distinct().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建待提交的完成详情(无 task_completion 记录)
|
||||||
|
*/
|
||||||
|
private TaskCompletionDetailResponse buildPendingCompletionResponse(Long taskId, String taskTitle, Long studentId) {
|
||||||
|
Student student = studentService.getById(studentId);
|
||||||
|
if (student == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TaskCompletionDetailResponse.StudentInfo studentInfo = TaskCompletionDetailResponse.StudentInfo.builder()
|
||||||
|
.id(student.getId())
|
||||||
|
.name(student.getName())
|
||||||
|
.avatar(student.getAvatarUrl())
|
||||||
|
.gender(student.getGender())
|
||||||
|
.build();
|
||||||
|
Clazz clazz = classService.getPrimaryClassByStudentId(student.getId());
|
||||||
|
if (clazz != null) {
|
||||||
|
studentInfo.setClassInfo(TaskCompletionDetailResponse.ClassInfo.builder()
|
||||||
|
.id(clazz.getId())
|
||||||
|
.name(clazz.getName())
|
||||||
|
.grade(clazz.getGrade())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return TaskCompletionDetailResponse.builder()
|
||||||
|
.id(null)
|
||||||
|
.taskId(taskId)
|
||||||
|
.taskTitle(taskTitle)
|
||||||
|
.student(studentInfo)
|
||||||
|
.status("PENDING")
|
||||||
|
.statusText("待提交")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TaskCompletionDetailResponse getCompletionDetail(Long completionId, Long tenantId) {
|
public TaskCompletionDetailResponse getCompletionDetail(Long completionId, Long tenantId) {
|
||||||
TaskCompletion completion = taskCompletionMapper.selectById(completionId);
|
TaskCompletion completion = taskCompletionMapper.selectById(completionId);
|
||||||
@ -563,7 +776,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
String targetType = targets.isEmpty() ? null : targets.get(0).getTargetType();
|
String targetType = targets.isEmpty() ? null : targets.get(0).getTargetType();
|
||||||
List<Long> targetIds = targets.stream().map(TaskTarget::getTargetId).toList();
|
List<Long> targetIds = targets.stream().map(TaskTarget::getTargetId).toList();
|
||||||
|
|
||||||
return TaskResponse.builder()
|
TaskResponse response = TaskResponse.builder()
|
||||||
.id(task.getId())
|
.id(task.getId())
|
||||||
.tenantId(task.getTenantId())
|
.tenantId(task.getTenantId())
|
||||||
.title(task.getTitle())
|
.title(task.getTitle())
|
||||||
@ -582,6 +795,33 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
|
|||||||
.createdAt(task.getCreatedAt())
|
.createdAt(task.getCreatedAt())
|
||||||
.updatedAt(task.getUpdatedAt())
|
.updatedAt(task.getUpdatedAt())
|
||||||
.build();
|
.build();
|
||||||
|
enrichTaskResponseWithStats(response, taskId);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enrichTaskResponseWithStats(TaskResponse response, Long taskId) {
|
||||||
|
// targetCount: 任务分配的目标人数(班级学生数或直接指定学生数)
|
||||||
|
int targetCount = 0;
|
||||||
|
List<TaskTarget> targets = taskTargetMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<TaskTarget>().eq(TaskTarget::getTaskId, taskId));
|
||||||
|
for (TaskTarget t : targets) {
|
||||||
|
String tt = t.getTargetType() != null ? t.getTargetType().toUpperCase() : "";
|
||||||
|
if ("CLASS".equals(tt)) {
|
||||||
|
List<Student> students = studentService.getStudentListByClassId(t.getTargetId());
|
||||||
|
targetCount += students != null ? students.size() : 0;
|
||||||
|
} else if ("STUDENT".equals(tt)) {
|
||||||
|
targetCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.setTargetCount(targetCount);
|
||||||
|
|
||||||
|
// completionCount: 已提交或已评价的人数
|
||||||
|
long completionCount = taskCompletionMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<TaskCompletion>()
|
||||||
|
.eq(TaskCompletion::getTaskId, taskId)
|
||||||
|
.in(TaskCompletion::getStatus, "SUBMITTED", "REVIEWED"));
|
||||||
|
response.setCompletionCount((int) completionCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- 任务完成表添加 photos 字段
|
||||||
|
-- 版本: V45
|
||||||
|
-- 日期: 2026-03-20
|
||||||
|
-- 说明: TaskCompletion 实体有 photos 字段,但表结构缺失
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
ALTER TABLE task_completion ADD COLUMN photos VARCHAR(2000) DEFAULT NULL COMMENT '照片URL数组(JSON)';
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- 任务完成表添加 submitted_at、reviewed_at 字段
|
||||||
|
-- 版本: V46
|
||||||
|
-- 日期: 2026-03-20
|
||||||
|
-- 说明: TaskCompletion 实体有这些字段,但表结构缺失
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
ALTER TABLE task_completion ADD COLUMN submitted_at DATETIME DEFAULT NULL COMMENT '提交时间';
|
||||||
|
ALTER TABLE task_completion ADD COLUMN reviewed_at DATETIME DEFAULT NULL COMMENT '评价时间';
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
-- =====================================================
|
||||||
|
-- 修复 task_feedback 表与 BaseEntity 字段对齐
|
||||||
|
-- 版本: V47
|
||||||
|
-- 日期: 2026-03-20
|
||||||
|
-- 说明: 添加 create_by、update_by,将 deleted_at 改为 deleted
|
||||||
|
-- 解决 POST /api/v1/teacher/tasks/completions/{id}/feedback 报错
|
||||||
|
-- "Unknown column 'create_by' in 'field list'"
|
||||||
|
-- =====================================================
|
||||||
|
|
||||||
|
-- 1. 添加 create_by、update_by 审计字段
|
||||||
|
ALTER TABLE task_feedback ADD COLUMN create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人';
|
||||||
|
ALTER TABLE task_feedback ADD COLUMN update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人';
|
||||||
|
|
||||||
|
-- 2. 添加 deleted 字段(与 BaseEntity 一致)
|
||||||
|
ALTER TABLE task_feedback ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT '删除标识(0-未删除,1-已删除)';
|
||||||
|
|
||||||
|
-- 3. 迁移 deleted_at 数据到 deleted
|
||||||
|
UPDATE task_feedback SET deleted = 1 WHERE deleted_at IS NOT NULL;
|
||||||
|
|
||||||
|
-- 4. 删除 deleted_at 列
|
||||||
|
ALTER TABLE task_feedback DROP COLUMN deleted_at;
|
||||||
Loading…
Reference in New Issue
Block a user