|
|
|
|
@ -14,8 +14,9 @@ import com.sztzjy.linkCommerce.mapper.StudentTrainingAnswerMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.mapper.UserinfoMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.mapper.WeightMapper;
|
|
|
|
|
import com.sztzjy.linkCommerce.entity.dto.TeacherTaskReviewDTO;
|
|
|
|
|
import com.sztzjy.linkCommerce.entity.dto.*;
|
|
|
|
|
import com.sztzjy.linkCommerce.service.ScoreRankService;
|
|
|
|
|
import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService;
|
|
|
|
|
import com.sztzjy.linkCommerce.service.TeacherScoreService;
|
|
|
|
|
@ -61,6 +62,8 @@ public class TeaScoreController {
|
|
|
|
|
@Autowired(required = false)
|
|
|
|
|
SchoolDefaultTaskService schoolDefaultTaskService;
|
|
|
|
|
@Autowired
|
|
|
|
|
UserinfoMapper userinfoMapper;
|
|
|
|
|
@Autowired
|
|
|
|
|
TeacherScoreService teacherScoreService;
|
|
|
|
|
|
|
|
|
|
@GetMapping("/ownedTeachingClasses")
|
|
|
|
|
@ -156,6 +159,44 @@ public class TeaScoreController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GetMapping("/taskReview")
|
|
|
|
|
@ApiOperation("查询实训任务评阅工作台")
|
|
|
|
|
public ResultEntity<TeacherTaskReviewPageDTO> taskReview(@RequestParam String classId,
|
|
|
|
|
@RequestParam(required = false) String projectName,
|
|
|
|
|
@RequestParam(required = false) String taskId,
|
|
|
|
|
HttpServletRequest request) {
|
|
|
|
|
try {
|
|
|
|
|
teacherScoreService.requireAccessibleClass(currentUser(request), classId);
|
|
|
|
|
return new ResultEntity<>(HttpStatus.OK, "查询任务评阅成功",
|
|
|
|
|
buildTaskReviewPage(classId, projectName, taskId));
|
|
|
|
|
} catch (com.sztzjy.linkCommerce.config.exception.handler.ServiceException e) {
|
|
|
|
|
return new ResultEntity<>(e.getCode(), e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/batchSaveTaskTeacherScore")
|
|
|
|
|
@ApiOperation("批量保存教师任务评分")
|
|
|
|
|
@Transactional
|
|
|
|
|
public ResultEntity<?> batchSaveTaskTeacherScore(@RequestBody BatchTaskTeacherScoreRequest body,
|
|
|
|
|
HttpServletRequest request) {
|
|
|
|
|
try {
|
|
|
|
|
JwtUser teacher = currentUser(request);
|
|
|
|
|
validateBatchTaskScoreRequest(body, teacher);
|
|
|
|
|
List<StudentTrainingAnswer> answers = new ArrayList<>();
|
|
|
|
|
for (String studentUserId : body.getStudentUserIds()) {
|
|
|
|
|
answers.add(requireScorableTaskAnswer(body.getClassId(), studentUserId, body.getTaskId()));
|
|
|
|
|
}
|
|
|
|
|
BigDecimal score = body.getTeacherScore().setScale(2, BigDecimal.ROUND_HALF_UP);
|
|
|
|
|
Date scoredAt = new Date();
|
|
|
|
|
for (StudentTrainingAnswer answer : answers) {
|
|
|
|
|
studentTrainingAnswerMapper.updateTeacherScore(answer.getId(), score, teacher.getUserId(), scoredAt);
|
|
|
|
|
}
|
|
|
|
|
return new ResultEntity<>(HttpStatus.OK, "批量任务评分保存成功", body.getStudentUserIds());
|
|
|
|
|
} catch (com.sztzjy.linkCommerce.config.exception.handler.ServiceException e) {
|
|
|
|
|
return new ResultEntity<>(e.getCode(), e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping("/selectStuRankAndScore")
|
|
|
|
|
@ApiOperation("查询学生成绩和排名")
|
|
|
|
|
@AnonymousAccess
|
|
|
|
|
@ -571,6 +612,140 @@ public class TeaScoreController {
|
|
|
|
|
return reviews;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TeacherTaskReviewPageDTO buildTaskReviewPage(String classId, String projectName, String taskId) {
|
|
|
|
|
TeacherTaskReviewPageDTO page = new TeacherTaskReviewPageDTO();
|
|
|
|
|
List<TeacherTaskReviewOptionDTO> allTasks = new ArrayList<>();
|
|
|
|
|
Set<String> projectNames = new LinkedHashSet<>();
|
|
|
|
|
Set<String> seenTaskIds = new HashSet<>();
|
|
|
|
|
for (TaskAllocation allocation : resolveEnabledTaskAllocations(classId)) {
|
|
|
|
|
if (allocation == null || StringUtils.isBlank(allocation.getModule())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
TrainingTask task = trainingTaskMapper == null ? null : trainingTaskMapper.selectByTaskKey(allocation.getModule());
|
|
|
|
|
if (task == null || StringUtils.isBlank(task.getId()) || !seenTaskIds.add(task.getId())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
TeacherTaskReviewOptionDTO option = reviewOption(task);
|
|
|
|
|
allTasks.add(option);
|
|
|
|
|
if (StringUtils.isNotBlank(option.getProjectName())) {
|
|
|
|
|
projectNames.add(option.getProjectName());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
page.setProjects(new ArrayList<>(projectNames));
|
|
|
|
|
List<TeacherTaskReviewOptionDTO> filteredTasks = allTasks.stream()
|
|
|
|
|
.filter(task -> StringUtils.isBlank(projectName) || StringUtils.equals(projectName, task.getProjectName()))
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
page.setTasks(filteredTasks);
|
|
|
|
|
TeacherTaskReviewOptionDTO selected = filteredTasks.stream()
|
|
|
|
|
.filter(task -> StringUtils.equals(task.getTaskId(), taskId))
|
|
|
|
|
.findFirst().orElse(filteredTasks.isEmpty() ? null : filteredTasks.get(0));
|
|
|
|
|
page.setSelectedTask(selected);
|
|
|
|
|
if (selected == null) {
|
|
|
|
|
return page;
|
|
|
|
|
}
|
|
|
|
|
List<TeacherTaskReviewStudentDTO> students = new ArrayList<>();
|
|
|
|
|
for (TeachingClassStudent membership : teachingClassStudentMapper.selectByTeachingClassId(classId)) {
|
|
|
|
|
if (membership == null || StringUtils.isBlank(membership.getStudentUserId())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
StudentTrainingAnswer answer = studentTrainingAnswerMapper == null ? null
|
|
|
|
|
: studentTrainingAnswerMapper.selectByStudentClassAndTask(membership.getStudentUserId(), classId, selected.getTaskId());
|
|
|
|
|
students.add(reviewStudent(membership.getStudentUserId(), answer));
|
|
|
|
|
}
|
|
|
|
|
students.sort(Comparator.comparing((TeacherTaskReviewStudentDTO student) -> student.getTeacherScore() != null)
|
|
|
|
|
.thenComparing(student -> StringUtils.defaultString(student.getName()))
|
|
|
|
|
.thenComparing(student -> StringUtils.defaultString(student.getUsername())));
|
|
|
|
|
page.setStudents(students);
|
|
|
|
|
return page;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TeacherTaskReviewOptionDTO reviewOption(TrainingTask task) {
|
|
|
|
|
TeacherTaskReviewOptionDTO option = new TeacherTaskReviewOptionDTO();
|
|
|
|
|
option.setTaskId(task.getId());
|
|
|
|
|
option.setTaskKey(task.getTaskKey());
|
|
|
|
|
option.setProjectName(task.getProjectName());
|
|
|
|
|
option.setTaskName(task.getTaskName());
|
|
|
|
|
option.setSteps(task.getSteps());
|
|
|
|
|
return option;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TeacherTaskReviewStudentDTO reviewStudent(String studentUserId, StudentTrainingAnswer answer) {
|
|
|
|
|
TeacherTaskReviewStudentDTO row = new TeacherTaskReviewStudentDTO();
|
|
|
|
|
row.setStudentUserId(studentUserId);
|
|
|
|
|
Userinfo student = userinfoMapper == null ? null : userinfoMapper.selectByPrimaryKey(studentUserId);
|
|
|
|
|
row.setName(student == null ? "" : StringUtils.defaultString(student.getName()));
|
|
|
|
|
row.setUsername(student == null ? "" : StringUtils.defaultString(student.getUsername()));
|
|
|
|
|
row.setProgressStatus(answer == null ? "NOT_STARTED" : answer.getProgressStatus());
|
|
|
|
|
row.setSubmitted(answer != null && Boolean.TRUE.equals(answer.getSubmitted()));
|
|
|
|
|
row.setScorable(isCompleted(answer));
|
|
|
|
|
if (answer != null) {
|
|
|
|
|
row.setAiAssessmentScore(answer.getAiAssessmentScore());
|
|
|
|
|
row.setTeacherScore(answer.getTeacherScore());
|
|
|
|
|
row.setStep1Answer(answer.getStep1Answer());
|
|
|
|
|
row.setStep2Answer(answer.getStep2Answer());
|
|
|
|
|
row.setStep3Answer(answer.getStep3Answer());
|
|
|
|
|
row.setStep4Answer(answer.getStep4Answer());
|
|
|
|
|
row.setDynamicStepAnswers(answer.getDynamicStepAnswers());
|
|
|
|
|
}
|
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void validateBatchTaskScoreRequest(BatchTaskTeacherScoreRequest body, JwtUser teacher) {
|
|
|
|
|
if (body == null || StringUtils.isBlank(body.getClassId()) || StringUtils.isBlank(body.getTaskId())) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"教学班和实训任务不能为空");
|
|
|
|
|
}
|
|
|
|
|
teacherScoreService.requireOwnedClass(teacher, body.getClassId());
|
|
|
|
|
if (!isEnabledAllocatedTask(body.getClassId(), body.getTaskId())) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"该实训任务未发布到当前教学班");
|
|
|
|
|
}
|
|
|
|
|
if (body.getStudentUserIds() == null || body.getStudentUserIds().isEmpty()) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"请选择至少一名学生");
|
|
|
|
|
}
|
|
|
|
|
Set<String> distinctIds = new LinkedHashSet<>();
|
|
|
|
|
for (String studentUserId : body.getStudentUserIds()) {
|
|
|
|
|
if (StringUtils.isBlank(studentUserId) || !distinctIds.add(studentUserId)) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"所选学生数据无效");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (body.getTeacherScore() == null || body.getTeacherScore().compareTo(BigDecimal.ZERO) < 0
|
|
|
|
|
|| body.getTeacherScore().compareTo(new BigDecimal("100")) > 0
|
|
|
|
|
|| body.getTeacherScore().stripTrailingZeros().scale() > 0) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"教师评分必须为0至100之间的整数");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isEnabledAllocatedTask(String classId, String taskId) {
|
|
|
|
|
for (TaskAllocation allocation : resolveEnabledTaskAllocations(classId)) {
|
|
|
|
|
if (allocation == null || StringUtils.isBlank(allocation.getModule())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
TrainingTask task = trainingTaskMapper == null ? null : trainingTaskMapper.selectByTaskKey(allocation.getModule());
|
|
|
|
|
if (task != null && StringUtils.equals(taskId, task.getId())) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private StudentTrainingAnswer requireScorableTaskAnswer(String classId, String studentUserId, String taskId) {
|
|
|
|
|
if (teachingClassStudentMapper.countByStudentUserIdAndTeachingClassId(studentUserId, classId) == 0) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"该学生不在当前教学班");
|
|
|
|
|
}
|
|
|
|
|
StudentTrainingAnswer answer = studentTrainingAnswerMapper == null ? null
|
|
|
|
|
: studentTrainingAnswerMapper.selectByStudentClassAndTask(studentUserId, classId, taskId);
|
|
|
|
|
if (!isCompleted(answer)) {
|
|
|
|
|
throw new com.sztzjy.linkCommerce.config.exception.handler.ServiceException(HttpStatus.BAD_REQUEST,
|
|
|
|
|
"仅已完成的实训任务可以评分");
|
|
|
|
|
}
|
|
|
|
|
return answer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retained for existing direct controller tests; the HTTP route is the JWT-scoped overload above.
|
|
|
|
|
public void exportStuScore(HttpServletResponse response, String schoolId, String classId, String userName, String name) {
|
|
|
|
|
StuRankExample example = new StuRankExample();
|
|
|
|
|
|