fix: 课后记录页面前后端对齐 - 修改 getStudentRecords 返回 { lesson, students } 结构 - 修改 batchSaveStudentRecords 接受 { records } 请求体 - 新增 StudentRecordsResponse/StudentWithRecordResponse/BatchStudentRecordsRequest - 前端增强数据容错
Made-with: Cursor
This commit is contained in:
parent
463c3d9922
commit
c93d325cee
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<div class="records-content" v-if="students.length > 0">
|
<div class="records-content" v-if="(students ?? []).length > 0">
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息 -->
|
||||||
<div class="tip-banner">
|
<div class="tip-banner">
|
||||||
<BulbOutlined />
|
<BulbOutlined />
|
||||||
@ -292,24 +292,26 @@ const loadRecords = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const data = await getStudentRecords(lessonId.value);
|
const data = await getStudentRecords(lessonId.value);
|
||||||
lessonInfo.value = data.lesson;
|
// 兼容后端返回格式:{ lesson, students } 或异常结构
|
||||||
students.value = data.students;
|
const studentList = Array.isArray(data?.students) ? data.students : [];
|
||||||
|
lessonInfo.value = data?.lesson ?? null;
|
||||||
|
students.value = studentList;
|
||||||
|
|
||||||
// 初始化记录数据
|
// 初始化记录数据
|
||||||
for (const student of data.students) {
|
for (const student of studentList) {
|
||||||
records[student.id] = {
|
records[student.id] = {
|
||||||
focus: student.record?.focus || 0,
|
focus: student.record?.focus ?? 0,
|
||||||
participation: student.record?.participation || 0,
|
participation: student.record?.participation ?? 0,
|
||||||
interest: student.record?.interest || 0,
|
interest: student.record?.interest ?? 0,
|
||||||
understanding: student.record?.understanding || 0,
|
understanding: student.record?.understanding ?? 0,
|
||||||
notes: student.record?.notes || '',
|
notes: student.record?.notes ?? '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载课程详情获取更多课程信息
|
// 加载课程详情获取更多课程信息
|
||||||
await loadLessonDetail();
|
await loadLessonDetail();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '获取学生记录失败');
|
message.error(error?.message || '获取学生记录失败');
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,11 +10,13 @@ import com.reading.platform.common.security.SecurityUtils;
|
|||||||
import com.reading.platform.dto.request.LessonCreateRequest;
|
import com.reading.platform.dto.request.LessonCreateRequest;
|
||||||
import com.reading.platform.dto.request.LessonProgressRequest;
|
import com.reading.platform.dto.request.LessonProgressRequest;
|
||||||
import com.reading.platform.dto.request.LessonUpdateRequest;
|
import com.reading.platform.dto.request.LessonUpdateRequest;
|
||||||
|
import com.reading.platform.dto.request.BatchStudentRecordsRequest;
|
||||||
import com.reading.platform.dto.request.StudentRecordRequest;
|
import com.reading.platform.dto.request.StudentRecordRequest;
|
||||||
import com.reading.platform.dto.response.ClassResponse;
|
import com.reading.platform.dto.response.ClassResponse;
|
||||||
import com.reading.platform.dto.response.CourseResponse;
|
import com.reading.platform.dto.response.CourseResponse;
|
||||||
import com.reading.platform.dto.response.LessonDetailResponse;
|
import com.reading.platform.dto.response.LessonDetailResponse;
|
||||||
import com.reading.platform.dto.response.LessonResponse;
|
import com.reading.platform.dto.response.LessonResponse;
|
||||||
|
import com.reading.platform.dto.response.StudentRecordsResponse;
|
||||||
import com.reading.platform.dto.response.StudentRecordResponse;
|
import com.reading.platform.dto.response.StudentRecordResponse;
|
||||||
import com.reading.platform.entity.Clazz;
|
import com.reading.platform.entity.Clazz;
|
||||||
import com.reading.platform.entity.CoursePackage;
|
import com.reading.platform.entity.CoursePackage;
|
||||||
@ -156,10 +158,9 @@ public class TeacherLessonController {
|
|||||||
|
|
||||||
@Operation(summary = "获取学生记录")
|
@Operation(summary = "获取学生记录")
|
||||||
@GetMapping("/{id}/students/records")
|
@GetMapping("/{id}/students/records")
|
||||||
public Result<List<StudentRecordResponse>> getStudentRecords(@PathVariable Long id) {
|
public Result<StudentRecordsResponse> getStudentRecords(@PathVariable Long id) {
|
||||||
List<StudentRecord> records = lessonService.getStudentRecords(id);
|
StudentRecordsResponse response = lessonService.getStudentRecords(id);
|
||||||
List<StudentRecordResponse> voList = studentRecordMapper.toVO(records);
|
return Result.success(response);
|
||||||
return Result.success(voList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "保存学生记录")
|
@Operation(summary = "保存学生记录")
|
||||||
@ -176,7 +177,8 @@ public class TeacherLessonController {
|
|||||||
@PostMapping("/{id}/students/batch-records")
|
@PostMapping("/{id}/students/batch-records")
|
||||||
public Result<List<StudentRecordResponse>> batchSaveStudentRecords(
|
public Result<List<StudentRecordResponse>> batchSaveStudentRecords(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody List<StudentRecordRequest> requests) {
|
@RequestBody BatchStudentRecordsRequest request) {
|
||||||
|
List<StudentRecordRequest> requests = request.getRecords() != null ? request.getRecords() : List.of();
|
||||||
List<StudentRecord> records = lessonService.batchSaveStudentRecords(id, requests);
|
List<StudentRecord> records = lessonService.batchSaveStudentRecords(id, requests);
|
||||||
List<StudentRecordResponse> voList = studentRecordMapper.toVO(records);
|
List<StudentRecordResponse> voList = studentRecordMapper.toVO(records);
|
||||||
return Result.success(voList);
|
return Result.success(voList);
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.reading.platform.dto.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存学生记录请求(与前端 { records } 结构对齐)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "批量保存学生记录请求")
|
||||||
|
public class BatchStudentRecordsRequest {
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
@Schema(description = "记录列表", required = true)
|
||||||
|
private List<StudentRecordRequest> records;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课后记录列表响应(lesson + students 结构,与前端对齐)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "课后记录列表响应")
|
||||||
|
public class StudentRecordsResponse {
|
||||||
|
|
||||||
|
@Schema(description = "课时信息")
|
||||||
|
private LessonInfo lesson;
|
||||||
|
|
||||||
|
@Schema(description = "学生列表(含记录)")
|
||||||
|
private List<StudentWithRecordResponse> students;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "课时简要信息")
|
||||||
|
public static class LessonInfo {
|
||||||
|
@Schema(description = "课时 ID")
|
||||||
|
private Long id;
|
||||||
|
@Schema(description = "状态")
|
||||||
|
private String status;
|
||||||
|
@Schema(description = "班级名称")
|
||||||
|
private String className;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 学生及其课后记录(用于课后记录页面)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "学生及其课后记录")
|
||||||
|
public class StudentWithRecordResponse {
|
||||||
|
|
||||||
|
@Schema(description = "学生 ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "学生姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "性别")
|
||||||
|
private String gender;
|
||||||
|
|
||||||
|
@Schema(description = "课后记录,未记录时为 null")
|
||||||
|
private StudentRecordResponse record;
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import com.reading.platform.dto.request.LessonCreateRequest;
|
|||||||
import com.reading.platform.dto.request.LessonUpdateRequest;
|
import com.reading.platform.dto.request.LessonUpdateRequest;
|
||||||
import com.reading.platform.dto.request.LessonProgressRequest;
|
import com.reading.platform.dto.request.LessonProgressRequest;
|
||||||
import com.reading.platform.dto.request.StudentRecordRequest;
|
import com.reading.platform.dto.request.StudentRecordRequest;
|
||||||
|
import com.reading.platform.dto.response.StudentRecordsResponse;
|
||||||
import com.reading.platform.entity.Lesson;
|
import com.reading.platform.entity.Lesson;
|
||||||
import com.reading.platform.entity.LessonFeedback;
|
import com.reading.platform.entity.LessonFeedback;
|
||||||
import com.reading.platform.entity.StudentRecord;
|
import com.reading.platform.entity.StudentRecord;
|
||||||
@ -38,9 +39,9 @@ public interface LessonService extends com.baomidou.mybatisplus.extension.servic
|
|||||||
List<Lesson> getTodayLessons(Long tenantId);
|
List<Lesson> getTodayLessons(Long tenantId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取学生记录列表
|
* 获取学生记录列表(含课时信息、班级学生及记录,与前端结构对齐)
|
||||||
*/
|
*/
|
||||||
List<StudentRecord> getStudentRecords(Long lessonId);
|
StudentRecordsResponse getStudentRecords(Long lessonId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存学生记录
|
* 保存学生记录
|
||||||
|
|||||||
@ -47,6 +47,11 @@ public interface StudentService extends com.baomidou.mybatisplus.extension.servi
|
|||||||
*/
|
*/
|
||||||
Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize);
|
Page<Student> getStudentsByClassId(Long classId, Integer pageNum, Integer pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据班级 ID 查询学生列表(不分页,用于课后记录等场景)
|
||||||
|
*/
|
||||||
|
List<Student> getStudentListByClassId(Long classId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据班级 ID 列表查询学生
|
* 根据班级 ID 列表查询学生
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,17 +10,23 @@ import com.reading.platform.dto.request.LessonCreateRequest;
|
|||||||
import com.reading.platform.dto.request.LessonUpdateRequest;
|
import com.reading.platform.dto.request.LessonUpdateRequest;
|
||||||
import com.reading.platform.dto.request.LessonProgressRequest;
|
import com.reading.platform.dto.request.LessonProgressRequest;
|
||||||
import com.reading.platform.dto.request.StudentRecordRequest;
|
import com.reading.platform.dto.request.StudentRecordRequest;
|
||||||
|
import com.reading.platform.dto.response.StudentRecordsResponse;
|
||||||
|
import com.reading.platform.dto.response.StudentWithRecordResponse;
|
||||||
import com.reading.platform.common.enums.LessonStatus;
|
import com.reading.platform.common.enums.LessonStatus;
|
||||||
|
import com.reading.platform.entity.Clazz;
|
||||||
import com.reading.platform.entity.Lesson;
|
import com.reading.platform.entity.Lesson;
|
||||||
|
import com.reading.platform.entity.Student;
|
||||||
import com.reading.platform.entity.LessonFeedback;
|
import com.reading.platform.entity.LessonFeedback;
|
||||||
import com.reading.platform.entity.SchedulePlan;
|
import com.reading.platform.entity.SchedulePlan;
|
||||||
import com.reading.platform.entity.StudentRecord;
|
import com.reading.platform.entity.StudentRecord;
|
||||||
|
import com.reading.platform.mapper.ClazzMapper;
|
||||||
import com.reading.platform.mapper.LessonFeedbackMapper;
|
import com.reading.platform.mapper.LessonFeedbackMapper;
|
||||||
import com.reading.platform.mapper.CoursePackageMapper;
|
import com.reading.platform.mapper.CoursePackageMapper;
|
||||||
import com.reading.platform.mapper.LessonMapper;
|
import com.reading.platform.mapper.LessonMapper;
|
||||||
import com.reading.platform.mapper.SchedulePlanMapper;
|
import com.reading.platform.mapper.SchedulePlanMapper;
|
||||||
import com.reading.platform.mapper.StudentRecordMapper;
|
import com.reading.platform.mapper.StudentRecordMapper;
|
||||||
import com.reading.platform.service.LessonService;
|
import com.reading.platform.service.LessonService;
|
||||||
|
import com.reading.platform.service.StudentService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -32,6 +38,8 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -44,6 +52,9 @@ public class LessonServiceImpl extends ServiceImpl<LessonMapper, Lesson>
|
|||||||
private final LessonFeedbackMapper lessonFeedbackMapper;
|
private final LessonFeedbackMapper lessonFeedbackMapper;
|
||||||
private final SchedulePlanMapper schedulePlanMapper;
|
private final SchedulePlanMapper schedulePlanMapper;
|
||||||
private final CoursePackageMapper coursePackageMapper;
|
private final CoursePackageMapper coursePackageMapper;
|
||||||
|
private final ClazzMapper clazzMapper;
|
||||||
|
private final StudentService studentService;
|
||||||
|
private final com.reading.platform.common.mapper.StudentRecordMapper studentRecordVoMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@ -222,13 +233,48 @@ public class LessonServiceImpl extends ServiceImpl<LessonMapper, Lesson>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<StudentRecord> getStudentRecords(Long lessonId) {
|
public StudentRecordsResponse getStudentRecords(Long lessonId) {
|
||||||
log.debug("获取学生记录列表,课程 ID: {}", lessonId);
|
log.debug("获取学生记录列表,课程 ID: {}", lessonId);
|
||||||
|
|
||||||
return studentRecordMapper.selectList(
|
Lesson lesson = lessonMapper.selectById(lessonId);
|
||||||
|
if (lesson == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND, "授课记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
String className = "";
|
||||||
|
if (lesson.getClassId() != null) {
|
||||||
|
Clazz clazz = clazzMapper.selectById(lesson.getClassId());
|
||||||
|
className = clazz != null ? clazz.getName() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Student> students = lesson.getClassId() != null
|
||||||
|
? studentService.getStudentListByClassId(lesson.getClassId())
|
||||||
|
: List.of();
|
||||||
|
|
||||||
|
List<StudentRecord> records = studentRecordMapper.selectList(
|
||||||
new LambdaQueryWrapper<StudentRecord>()
|
new LambdaQueryWrapper<StudentRecord>()
|
||||||
.eq(StudentRecord::getLessonId, lessonId)
|
.eq(StudentRecord::getLessonId, lessonId)
|
||||||
);
|
);
|
||||||
|
Map<Long, com.reading.platform.dto.response.StudentRecordResponse> recordMap = records.stream()
|
||||||
|
.collect(Collectors.toMap(StudentRecord::getStudentId, studentRecordVoMapper::toVO, (a, b) -> a));
|
||||||
|
|
||||||
|
List<StudentWithRecordResponse> studentWithRecords = students.stream()
|
||||||
|
.map(s -> StudentWithRecordResponse.builder()
|
||||||
|
.id(s.getId())
|
||||||
|
.name(s.getName())
|
||||||
|
.gender(s.getGender())
|
||||||
|
.record(recordMap.get(s.getId()))
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return StudentRecordsResponse.builder()
|
||||||
|
.lesson(StudentRecordsResponse.LessonInfo.builder()
|
||||||
|
.id(lesson.getId())
|
||||||
|
.status(lesson.getStatus())
|
||||||
|
.className(className)
|
||||||
|
.build())
|
||||||
|
.students(studentWithRecords)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -197,6 +197,36 @@ public class StudentServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
|||||||
return studentMapper.selectPage(page, wrapper);
|
return studentMapper.selectPage(page, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Student> getStudentListByClassId(Long classId) {
|
||||||
|
log.debug("根据班级查询学生列表,班级 ID: {}", classId);
|
||||||
|
|
||||||
|
List<StudentClassHistory> histories = studentClassHistoryMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<StudentClassHistory>()
|
||||||
|
.eq(StudentClassHistory::getClassId, classId)
|
||||||
|
.eq(StudentClassHistory::getStatus, "active")
|
||||||
|
.isNull(StudentClassHistory::getEndDate)
|
||||||
|
.or()
|
||||||
|
.ge(StudentClassHistory::getEndDate, LocalDate.now())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (histories.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> studentIds = histories.stream()
|
||||||
|
.map(StudentClassHistory::getStudentId)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return studentMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<Student>()
|
||||||
|
.in(Student::getId, studentIds)
|
||||||
|
.eq(Student::getStatus, "active")
|
||||||
|
.orderByAsc(Student::getName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteStudent(Long id) {
|
public void deleteStudent(Long id) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user