feat: 参考答案推送与学生实验报告成绩展示
- 任务配置开放参考答案开关(setClassTaskReferenceAnswerEnabled), 学生任务接口仅在推送后返回参考答案字段 - 学生实验报告任务明细携带AI评分/老师评分与评语 - 新增学生端参考答案预览接口(docx转PDF,推送门控) - 同步测试main
parent
78dbd4d01a
commit
d6ebf06afa
@ -1,4 +1,26 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
import java.math.BigDecimal; import java.util.List;
|
||||
public class StudentExperimentReportDTO { private String teachingClassId; private Integer rank; private BigDecimal totalScore; private List<StudentExperimentReportModuleDTO> modules; private List<StudentExperimentReportTaskDTO> tasks;
|
||||
public String getTeachingClassId(){return teachingClassId;} public void setTeachingClassId(String v){teachingClassId=v;} public Integer getRank(){return rank;} public void setRank(Integer v){rank=v;} public BigDecimal getTotalScore(){return totalScore;} public void setTotalScore(BigDecimal v){totalScore=v;} public List<StudentExperimentReportModuleDTO> getModules(){return modules;} public void setModules(List<StudentExperimentReportModuleDTO> v){modules=v;} public List<StudentExperimentReportTaskDTO> getTasks(){return tasks;} public void setTasks(List<StudentExperimentReportTaskDTO> v){tasks=v;} }
|
||||
public class StudentExperimentReportDTO {
|
||||
private String teachingClassId;
|
||||
private Integer rank;
|
||||
private BigDecimal totalScore;
|
||||
private BigDecimal aiAssessmentScore;
|
||||
private BigDecimal teacherScore;
|
||||
private List<StudentExperimentReportModuleDTO> modules;
|
||||
private List<StudentExperimentReportTaskDTO> tasks;
|
||||
|
||||
public String getTeachingClassId() { return teachingClassId; }
|
||||
public void setTeachingClassId(String v) { teachingClassId = v; }
|
||||
public Integer getRank() { return rank; }
|
||||
public void setRank(Integer v) { rank = v; }
|
||||
public BigDecimal getTotalScore() { return totalScore; }
|
||||
public void setTotalScore(BigDecimal v) { totalScore = v; }
|
||||
public BigDecimal getAiAssessmentScore() { return aiAssessmentScore; }
|
||||
public void setAiAssessmentScore(BigDecimal v) { aiAssessmentScore = v; }
|
||||
public BigDecimal getTeacherScore() { return teacherScore; }
|
||||
public void setTeacherScore(BigDecimal v) { teacherScore = v; }
|
||||
public List<StudentExperimentReportModuleDTO> getModules() { return modules; }
|
||||
public void setModules(List<StudentExperimentReportModuleDTO> v) { modules = v; }
|
||||
public List<StudentExperimentReportTaskDTO> getTasks() { return tasks; }
|
||||
public void setTasks(List<StudentExperimentReportTaskDTO> v) { tasks = v; }
|
||||
}
|
||||
|
||||
@ -1,3 +1,22 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
public class StudentExperimentReportTaskDTO { private String taskName; private String moduleName; private String progressStatus; private Boolean submitted; private Integer aiAssessmentScore;
|
||||
public String getTaskName(){return taskName;} public void setTaskName(String v){taskName=v;} public String getModuleName(){return moduleName;} public void setModuleName(String v){moduleName=v;} public String getProgressStatus(){return progressStatus;} public void setProgressStatus(String v){progressStatus=v;} public Boolean getSubmitted(){return submitted;} public void setSubmitted(Boolean v){submitted=v;} public Integer getAiAssessmentScore(){return aiAssessmentScore;} public void setAiAssessmentScore(Integer v){aiAssessmentScore=v;} }
|
||||
public class StudentExperimentReportTaskDTO {
|
||||
private String taskName;
|
||||
private String moduleName;
|
||||
private String progressStatus;
|
||||
private Boolean submitted;
|
||||
private Integer aiAssessmentScore;
|
||||
private java.math.BigDecimal teacherScore;
|
||||
|
||||
public String getTaskName() { return taskName; }
|
||||
public void setTaskName(String v) { taskName = v; }
|
||||
public String getModuleName() { return moduleName; }
|
||||
public void setModuleName(String v) { moduleName = v; }
|
||||
public String getProgressStatus() { return progressStatus; }
|
||||
public void setProgressStatus(String v) { progressStatus = v; }
|
||||
public Boolean getSubmitted() { return submitted; }
|
||||
public void setSubmitted(Boolean v) { submitted = v; }
|
||||
public Integer getAiAssessmentScore() { return aiAssessmentScore; }
|
||||
public void setAiAssessmentScore(Integer v) { aiAssessmentScore = v; }
|
||||
public java.math.BigDecimal getTeacherScore() { return teacherScore; }
|
||||
public void setTeacherScore(java.math.BigDecimal v) { teacherScore = v; }
|
||||
}
|
||||
|
||||
@ -1,11 +1,217 @@
|
||||
package com.sztzjy.linkCommerce.service.impl;
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.entity.*; import com.sztzjy.linkCommerce.entity.dto.*; import com.sztzjy.linkCommerce.mapper.*; import com.sztzjy.linkCommerce.service.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
|
||||
import java.math.*; import java.util.*;
|
||||
@Service public class StudentExperimentReportServiceImpl implements StudentExperimentReportService {
|
||||
@Autowired public StudentTeachingClassResolver studentTeachingClassResolver; @Autowired public SchoolDefaultTaskService schoolDefaultTaskService; @Autowired public TrainingTaskMapper trainingTaskMapper; @Autowired public StudentTrainingAnswerMapper studentTrainingAnswerMapper; @Autowired(required=false) public TeachingClassScoreWeightMapper teachingClassScoreWeightMapper; @Autowired(required=false) public TaskAllocationMapper taskAllocationMapper;
|
||||
private static final String[] NAMES={"互联网产品开发基础认知","市场洞察与需求分析","产品规划与设计","产品开发与测试验证","产品上线与运营推广","综合实训"};
|
||||
private static final BigDecimal[] DEFAULTS={new BigDecimal("0.10"),new BigDecimal("0.30"),new BigDecimal("0.10"),new BigDecimal("0.10"),new BigDecimal("0.20"),new BigDecimal("0.20")};
|
||||
public StudentExperimentReportDTO getCurrentReport(JwtUser user){ String classId=studentTeachingClassResolver.resolveRequired(user); StudentExperimentReportDTO report=new StudentExperimentReportDTO(); report.setTeachingClassId(classId); List<StudentExperimentReportModuleDTO> modules=new ArrayList<>(); Map<String,StudentExperimentReportModuleDTO> index=new HashMap<>(); BigDecimal[] weights=weights(classId); boolean comprehensiveParticipating=hasPublishedComprehensiveTask(classId); for(int i=0;i<NAMES.length;i++){StudentExperimentReportModuleDTO m=new StudentExperimentReportModuleDTO();m.setModuleName(NAMES[i]);m.setWeight(weights[i]);m.setParticipating(i<NAMES.length-1||comprehensiveParticipating);modules.add(m);index.put(NAMES[i],m);} List<StudentExperimentReportTaskDTO> tasks=new ArrayList<>(); for(TaskAllocation a: schoolDefaultTaskService.resolveForTeachingClass(classId)){if(a==null||a.getDisabledStatus()!=null&&a.getDisabledStatus()==1||TaskAllocation.PUBLICATION_MARKER.equals(a.getModule())||("comprehensive-case-training".equals(a.getModule())&&!comprehensiveParticipating))continue; TrainingTask t=trainingTaskMapper.selectByTaskKey(a.getModule()); if(t==null||!index.containsKey(t.getProjectName()))continue; StudentTrainingAnswer ans=studentTrainingAnswerMapper.selectByStudentClassAndTask(user.getUserId(),classId,t.getId()); StudentExperimentReportModuleDTO m=index.get(t.getProjectName());m.setTotalTaskCount(m.getTotalTaskCount()+1);int score=ans==null||ans.getAiAssessmentScore()==null?0:ans.getAiAssessmentScore();m.setScore(m.getScore().add(BigDecimal.valueOf(score)));if(ans!=null&&(Boolean.TRUE.equals(ans.getSubmitted())||"COMPLETED".equals(ans.getProgressStatus())))m.setCompletedTaskCount(m.getCompletedTaskCount()+1);StudentExperimentReportTaskDTO row=new StudentExperimentReportTaskDTO();row.setTaskName(t.getTaskName());row.setModuleName(t.getProjectName());if(ans!=null){row.setProgressStatus(ans.getProgressStatus());row.setSubmitted(ans.getSubmitted());row.setAiAssessmentScore(ans.getAiAssessmentScore());}tasks.add(row);} BigDecimal total=BigDecimal.ZERO;BigDecimal participatingWeight=BigDecimal.ZERO;for(StudentExperimentReportModuleDTO m:modules){BigDecimal score=m.getTotalTaskCount()==0?BigDecimal.ZERO:m.getScore().divide(BigDecimal.valueOf(m.getTotalTaskCount()),2,BigDecimal.ROUND_HALF_UP);m.setScore(score);if(Boolean.TRUE.equals(m.getParticipating())){BigDecimal weight=m.getWeight()==null?BigDecimal.ZERO:m.getWeight();total=total.add(score.multiply(weight));participatingWeight=participatingWeight.add(weight);}}report.setTotalScore(participatingWeight.signum()==0?BigDecimal.ZERO:total.divide(participatingWeight,2,BigDecimal.ROUND_HALF_UP));report.setModules(modules);report.setTasks(tasks);return report; }
|
||||
private boolean hasPublishedComprehensiveTask(String classId){if(taskAllocationMapper==null)return false;TaskAllocationExample example=new TaskAllocationExample();example.createCriteria().andClassIdEqualTo(classId);List<TaskAllocation> allocations=taskAllocationMapper.selectByExample(example);boolean published=false;boolean enabled=false;for(TaskAllocation allocation:allocations==null?Collections.<TaskAllocation>emptyList():allocations){if(allocation==null)continue;if(TaskAllocation.PUBLICATION_MARKER.equals(allocation.getModule()))published=true;else if("comprehensive-case-training".equals(allocation.getModule())&&(allocation.getDisabledStatus()==null||allocation.getDisabledStatus()==0))enabled=true;}return published&&enabled;}
|
||||
private BigDecimal[] weights(String id){TeachingClassScoreWeight w=teachingClassScoreWeightMapper==null?null:teachingClassScoreWeightMapper.selectByTeachingClassId(id);if(w==null)return DEFAULTS;return new BigDecimal[]{w.getFoundationWeight(),w.getMarketInsightWeight(),w.getPlanningDesignWeight(),w.getDevelopmentValidationWeight(),w.getLaunchOperationWeight(),w.getComprehensiveTrainingWeight()};}
|
||||
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||
import com.sztzjy.linkCommerce.entity.StudentTrainingAnswer;
|
||||
import com.sztzjy.linkCommerce.entity.TaskAllocation;
|
||||
import com.sztzjy.linkCommerce.entity.TaskAllocationExample;
|
||||
import com.sztzjy.linkCommerce.entity.TeachingClassScoreWeight;
|
||||
import com.sztzjy.linkCommerce.entity.TrainingTask;
|
||||
import com.sztzjy.linkCommerce.entity.dto.StudentExperimentReportDTO;
|
||||
import com.sztzjy.linkCommerce.entity.dto.StudentExperimentReportModuleDTO;
|
||||
import com.sztzjy.linkCommerce.entity.dto.StudentExperimentReportTaskDTO;
|
||||
import com.sztzjy.linkCommerce.mapper.StudentTrainingAnswerMapper;
|
||||
import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper;
|
||||
import com.sztzjy.linkCommerce.mapper.TeachingClassScoreWeightMapper;
|
||||
import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper;
|
||||
import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService;
|
||||
import com.sztzjy.linkCommerce.service.StudentExperimentReportService;
|
||||
import com.sztzjy.linkCommerce.service.StudentTeachingClassResolver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class StudentExperimentReportServiceImpl implements StudentExperimentReportService {
|
||||
@Autowired
|
||||
public StudentTeachingClassResolver studentTeachingClassResolver;
|
||||
@Autowired
|
||||
public SchoolDefaultTaskService schoolDefaultTaskService;
|
||||
@Autowired
|
||||
public TrainingTaskMapper trainingTaskMapper;
|
||||
@Autowired
|
||||
public StudentTrainingAnswerMapper studentTrainingAnswerMapper;
|
||||
@Autowired(required = false)
|
||||
public TeachingClassScoreWeightMapper teachingClassScoreWeightMapper;
|
||||
@Autowired(required = false)
|
||||
public TaskAllocationMapper taskAllocationMapper;
|
||||
|
||||
private static final String[] NAMES = {
|
||||
"互联网产品开发基础认知", "市场洞察与需求分析", "产品规划与设计",
|
||||
"产品开发与测试验证", "产品上线与运营推广", "综合实训"
|
||||
};
|
||||
private static final BigDecimal[] DEFAULTS = {
|
||||
new BigDecimal("0.10"), new BigDecimal("0.30"), new BigDecimal("0.10"),
|
||||
new BigDecimal("0.10"), new BigDecimal("0.20"), new BigDecimal("0.20")
|
||||
};
|
||||
|
||||
@Override
|
||||
public StudentExperimentReportDTO getCurrentReport(JwtUser user) {
|
||||
String classId = studentTeachingClassResolver.resolveRequired(user);
|
||||
StudentExperimentReportDTO report = new StudentExperimentReportDTO();
|
||||
report.setTeachingClassId(classId);
|
||||
|
||||
List<StudentExperimentReportModuleDTO> modules = new ArrayList<>();
|
||||
Map<String, StudentExperimentReportModuleDTO> moduleByName = new HashMap<>();
|
||||
Map<String, Integer> moduleIndex = new HashMap<>();
|
||||
BigDecimal[] weights = weights(classId);
|
||||
boolean comprehensiveParticipating = hasPublishedComprehensiveTask(classId);
|
||||
for (int i = 0; i < NAMES.length; i++) {
|
||||
StudentExperimentReportModuleDTO module = new StudentExperimentReportModuleDTO();
|
||||
module.setModuleName(NAMES[i]);
|
||||
module.setWeight(weights[i]);
|
||||
module.setParticipating(i < NAMES.length - 1 || comprehensiveParticipating);
|
||||
modules.add(module);
|
||||
moduleByName.put(NAMES[i], module);
|
||||
moduleIndex.put(NAMES[i], i);
|
||||
}
|
||||
|
||||
BigDecimal[] aiSums = zeroScores();
|
||||
BigDecimal[] teacherSums = zeroScores();
|
||||
int[] taskCounts = new int[NAMES.length];
|
||||
boolean[] hasAiScore = new boolean[NAMES.length];
|
||||
boolean[] hasTeacherScore = new boolean[NAMES.length];
|
||||
List<StudentExperimentReportTaskDTO> tasks = new ArrayList<>();
|
||||
|
||||
List<TaskAllocation> allocations = schoolDefaultTaskService.resolveForTeachingClass(classId);
|
||||
for (TaskAllocation allocation : allocations == null
|
||||
? Collections.<TaskAllocation>emptyList() : allocations) {
|
||||
if (allocation == null || (allocation.getDisabledStatus() != null && allocation.getDisabledStatus() == 1)
|
||||
|| TaskAllocation.PUBLICATION_MARKER.equals(allocation.getModule())
|
||||
|| ("comprehensive-case-training".equals(allocation.getModule()) && !comprehensiveParticipating)) {
|
||||
continue;
|
||||
}
|
||||
TrainingTask task = trainingTaskMapper.selectByTaskKey(allocation.getModule());
|
||||
Integer index = task == null ? null : moduleIndex.get(task.getProjectName());
|
||||
if (index == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StudentTrainingAnswer answer = studentTrainingAnswerMapper
|
||||
.selectByStudentClassAndTask(user.getUserId(), classId, task.getId());
|
||||
StudentExperimentReportModuleDTO module = moduleByName.get(task.getProjectName());
|
||||
module.setTotalTaskCount(module.getTotalTaskCount() + 1);
|
||||
taskCounts[index]++;
|
||||
|
||||
StudentExperimentReportTaskDTO row = new StudentExperimentReportTaskDTO();
|
||||
row.setTaskName(task.getTaskName());
|
||||
row.setModuleName(task.getProjectName());
|
||||
if (answer != null) {
|
||||
row.setProgressStatus(answer.getProgressStatus());
|
||||
row.setSubmitted(answer.getSubmitted());
|
||||
row.setAiAssessmentScore(answer.getAiAssessmentScore());
|
||||
row.setTeacherScore(answer.getTeacherScore());
|
||||
if (answer.getAiAssessmentScore() != null) {
|
||||
aiSums[index] = aiSums[index].add(BigDecimal.valueOf(answer.getAiAssessmentScore()));
|
||||
hasAiScore[index] = true;
|
||||
}
|
||||
if (answer.getTeacherScore() != null) {
|
||||
teacherSums[index] = teacherSums[index].add(answer.getTeacherScore());
|
||||
hasTeacherScore[index] = true;
|
||||
}
|
||||
if (Boolean.TRUE.equals(answer.getSubmitted())
|
||||
|| "COMPLETED".equals(answer.getProgressStatus())) {
|
||||
module.setCompletedTaskCount(module.getCompletedTaskCount() + 1);
|
||||
}
|
||||
}
|
||||
tasks.add(row);
|
||||
}
|
||||
|
||||
boolean anyAiScore = false;
|
||||
boolean anyTeacherScore = false;
|
||||
for (int i = 0; i < NAMES.length; i++) {
|
||||
BigDecimal aiAverage = average(aiSums[i], taskCounts[i], hasAiScore[i]);
|
||||
BigDecimal teacherAverage = average(teacherSums[i], taskCounts[i], hasTeacherScore[i]);
|
||||
anyAiScore = anyAiScore || aiAverage != null;
|
||||
anyTeacherScore = anyTeacherScore || teacherAverage != null;
|
||||
// 教师评分优先;本模块没有教师评分时才使用 AI 评分。
|
||||
modules.get(i).setScore(teacherAverage != null ? teacherAverage : aiAverage);
|
||||
}
|
||||
|
||||
BigDecimal aiTotal = aggregate(modules, aiSums, taskCounts, hasAiScore);
|
||||
BigDecimal teacherTotal = aggregate(modules, teacherSums, taskCounts, hasTeacherScore);
|
||||
report.setAiAssessmentScore(anyAiScore ? aiTotal : null);
|
||||
report.setTeacherScore(anyTeacherScore ? teacherTotal : null);
|
||||
// 总成绩同样遵循教师评分优先,且没有任何成绩时返回 null。
|
||||
report.setTotalScore(anyTeacherScore ? teacherTotal : (anyAiScore ? aiTotal : null));
|
||||
report.setModules(modules);
|
||||
report.setTasks(tasks);
|
||||
return report;
|
||||
}
|
||||
|
||||
private BigDecimal[] zeroScores() {
|
||||
BigDecimal[] scores = new BigDecimal[NAMES.length];
|
||||
for (int i = 0; i < scores.length; i++) {
|
||||
scores[i] = BigDecimal.ZERO;
|
||||
}
|
||||
return scores;
|
||||
}
|
||||
|
||||
private BigDecimal average(BigDecimal sum, int count, boolean present) {
|
||||
if (!present || count <= 0) {
|
||||
return null;
|
||||
}
|
||||
return sum.divide(BigDecimal.valueOf(count), 2, BigDecimal.ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal aggregate(List<StudentExperimentReportModuleDTO> modules,
|
||||
BigDecimal[] sums, int[] taskCounts, boolean[] present) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
BigDecimal participatingWeight = BigDecimal.ZERO;
|
||||
for (int i = 0; i < modules.size(); i++) {
|
||||
BigDecimal average = average(sums[i], taskCounts[i], present[i]);
|
||||
if (average == null || !Boolean.TRUE.equals(modules.get(i).getParticipating())) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal weight = modules.get(i).getWeight() == null
|
||||
? BigDecimal.ZERO : modules.get(i).getWeight();
|
||||
total = total.add(average.multiply(weight));
|
||||
participatingWeight = participatingWeight.add(weight);
|
||||
}
|
||||
return participatingWeight.signum() == 0
|
||||
? BigDecimal.ZERO
|
||||
: total.divide(participatingWeight, 2, BigDecimal.ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
private boolean hasPublishedComprehensiveTask(String classId) {
|
||||
if (taskAllocationMapper == null) {
|
||||
return false;
|
||||
}
|
||||
TaskAllocationExample example = new TaskAllocationExample();
|
||||
example.createCriteria().andClassIdEqualTo(classId);
|
||||
List<TaskAllocation> allocations = taskAllocationMapper.selectByExample(example);
|
||||
boolean published = false;
|
||||
boolean enabled = false;
|
||||
for (TaskAllocation allocation : allocations == null
|
||||
? Collections.<TaskAllocation>emptyList() : allocations) {
|
||||
if (allocation == null) {
|
||||
continue;
|
||||
}
|
||||
if (TaskAllocation.PUBLICATION_MARKER.equals(allocation.getModule())) {
|
||||
published = true;
|
||||
} else if ("comprehensive-case-training".equals(allocation.getModule())
|
||||
&& (allocation.getDisabledStatus() == null || allocation.getDisabledStatus() == 0)) {
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
return published && enabled;
|
||||
}
|
||||
|
||||
private BigDecimal[] weights(String id) {
|
||||
TeachingClassScoreWeight weight = teachingClassScoreWeightMapper == null
|
||||
? null : teachingClassScoreWeightMapper.selectByTeachingClassId(id);
|
||||
if (weight == null) {
|
||||
return DEFAULTS;
|
||||
}
|
||||
return new BigDecimal[]{
|
||||
weight.getFoundationWeight(), weight.getMarketInsightWeight(),
|
||||
weight.getPlanningDesignWeight(), weight.getDevelopmentValidationWeight(),
|
||||
weight.getLaunchOperationWeight(), weight.getComprehensiveTrainingWeight()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue