|
|
@ -36,6 +36,7 @@ import org.springframework.core.io.ClassPathResource;
|
|
|
|
import org.springframework.data.domain.Page;
|
|
|
|
import org.springframework.data.domain.Page;
|
|
|
|
import org.springframework.data.domain.Sort;
|
|
|
|
import org.springframework.data.domain.Sort;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import javax.servlet.ServletOutputStream;
|
|
|
|
import javax.servlet.ServletOutputStream;
|
|
|
@ -46,6 +47,9 @@ import java.io.InputStream;
|
|
|
|
import java.time.Instant;
|
|
|
|
import java.time.Instant;
|
|
|
|
import java.time.LocalDate;
|
|
|
|
import java.time.LocalDate;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -476,6 +480,190 @@ public class EvaluationServiceImpl implements EvaluationService {
|
|
|
|
appraiseRecordRepository.save(appraiseRecord);
|
|
|
|
appraiseRecordRepository.save(appraiseRecord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List<StudentReportVo> Reports(ReportDto reportDto) {
|
|
|
|
|
|
|
|
// 获取当前登录用户和学校信息
|
|
|
|
|
|
|
|
//User user = SecurityUtil.getLoginUser();
|
|
|
|
|
|
|
|
String schoolId = "hbcn";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取学生 ID 列表
|
|
|
|
|
|
|
|
List<String> studentIds = reportDto.getIds();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(studentIds)) {
|
|
|
|
|
|
|
|
throw new ServiceException(ErrorCode.PARAM_ERROR.getCode(), "学生 ID 列表不能为空");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Set<String> studentIdSet = new HashSet<>(studentIds);
|
|
|
|
|
|
|
|
// 批量查询学生信息
|
|
|
|
|
|
|
|
List<Student> students = studentRepository.findAllByCodeAndIdIn(String.format(PK.STUDENT, schoolId),studentIdSet);
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(students)) {
|
|
|
|
|
|
|
|
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "未找到对应的学生信息");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取学段 ID 和班级 ID
|
|
|
|
|
|
|
|
String periodId = students.get(0).getPeriodId(); // 假设所有学生属于同一个学段
|
|
|
|
|
|
|
|
List<String> classIds = students.stream().map(Student::getClassId).collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查询学期信息
|
|
|
|
|
|
|
|
List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
|
|
|
|
|
|
|
|
String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量查询评价记录
|
|
|
|
|
|
|
|
List<AppraiseRecord> appraiseRecords = appraiseRecordRepository.findAllRecord(
|
|
|
|
|
|
|
|
studentIds, classIds.get(0), academicYearId, String.format(PK.PK_APPRAISE_RECORD, schoolId)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(appraiseRecords)) {
|
|
|
|
|
|
|
|
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "未找到对应的评价记录");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查询班级内学生总数
|
|
|
|
|
|
|
|
Map<String, Integer> classStudentCountMap = new HashMap<>();
|
|
|
|
|
|
|
|
for (String classId : classIds) {
|
|
|
|
|
|
|
|
int count = studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, schoolId));
|
|
|
|
|
|
|
|
classStudentCountMap.put(classId, count);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查询成就规则
|
|
|
|
|
|
|
|
Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "当前成就规则还未创建");
|
|
|
|
|
|
|
|
List<AchievementRule> rules = appraise.getAchievementRules();
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(rules)) {
|
|
|
|
|
|
|
|
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前成就规则为空");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建线程池
|
|
|
|
|
|
|
|
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
|
|
|
|
|
|
|
List<CompletableFuture<StudentReportVo>> futures = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 并行处理每个学生的报告生成任务
|
|
|
|
|
|
|
|
for (Student student : students) {
|
|
|
|
|
|
|
|
CompletableFuture<StudentReportVo> future = CompletableFuture.supplyAsync(() -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return generateStudentReport(student, appraiseRecords, classStudentCountMap, rules, academicYearId, schoolId);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
System.out.println("生成报告失败:" + e.getMessage());
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, executor);
|
|
|
|
|
|
|
|
futures.add(future);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 等待所有任务完成并收集结果
|
|
|
|
|
|
|
|
List<StudentReportVo> reportVos = futures.stream()
|
|
|
|
|
|
|
|
.map(CompletableFuture::join)
|
|
|
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭线程池
|
|
|
|
|
|
|
|
executor.shutdown();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reportVos;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private StudentReportVo generateStudentReport(Student student, List<AppraiseRecord> appraiseRecords,
|
|
|
|
|
|
|
|
Map<String, Integer> classStudentCountMap, List<AchievementRule> rules,
|
|
|
|
|
|
|
|
String academicYearId, String schoolId) {
|
|
|
|
|
|
|
|
String studentId = student.getId();
|
|
|
|
|
|
|
|
String classId = student.getClassId();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查找当前学生的评价记录
|
|
|
|
|
|
|
|
Optional<AppraiseRecord> appraiseRecordOpt = appraiseRecords.stream()
|
|
|
|
|
|
|
|
.filter(record -> record.getTargetId().equals(studentId))
|
|
|
|
|
|
|
|
.findFirst();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化默认值
|
|
|
|
|
|
|
|
Map<String, Integer> praiseDistribution = StudentReportVo.ofFiveEducation();
|
|
|
|
|
|
|
|
Map<String, Integer> criticalDistribution = StudentReportVo.ofFiveEducation();
|
|
|
|
|
|
|
|
List<AppraiseRecordItem> records = Collections.emptyList();
|
|
|
|
|
|
|
|
int praiseCount = 0;
|
|
|
|
|
|
|
|
float beyondPercent = 0.0f;
|
|
|
|
|
|
|
|
AchievementRule curAchievement = rules.get(rules.size() - 1); // 默认使用最低成就规则
|
|
|
|
|
|
|
|
int n = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有评价记录,则填充数据
|
|
|
|
|
|
|
|
if (appraiseRecordOpt.isPresent()) {
|
|
|
|
|
|
|
|
AppraiseRecord appraiseRecord = appraiseRecordOpt.get();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理评价记录
|
|
|
|
|
|
|
|
if (appraiseRecord.getNodes() != null) {
|
|
|
|
|
|
|
|
// 计算雷达图数据
|
|
|
|
|
|
|
|
for (AppraiseRecordItem record : appraiseRecord.getNodes()) {
|
|
|
|
|
|
|
|
AppraiseTreeNode appraiseNode = record.getAppraiseNode();
|
|
|
|
|
|
|
|
String[] path = appraiseNode.getPath();
|
|
|
|
|
|
|
|
if (path == null || path.length == 0) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String root = path[0];
|
|
|
|
|
|
|
|
String key = FiveEducations.getCodeByName(root);
|
|
|
|
|
|
|
|
if (key != null) {
|
|
|
|
|
|
|
|
if (appraiseNode.isPraise()) {
|
|
|
|
|
|
|
|
praiseDistribution.put(key, praiseDistribution.getOrDefault(key, 0) + 1);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
criticalDistribution.put(key, criticalDistribution.getOrDefault(key, 0) + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算学生排名
|
|
|
|
|
|
|
|
praiseCount = appraiseRecord.getPraiseCount();
|
|
|
|
|
|
|
|
int greaterCount = appraiseRecordRepository.findClassRecord(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, classId, praiseCount);
|
|
|
|
|
|
|
|
int stuInClassCount = classStudentCountMap.get(classId);
|
|
|
|
|
|
|
|
beyondPercent = (float) (stuInClassCount - greaterCount) / stuInClassCount;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算成就项 (排序 120(20x3) 60(10x4) 25(5x5) 121
|
|
|
|
|
|
|
|
rules = rules.stream().sorted(Comparator.comparing(AchievementRule::getLevel).reversed()).collect(Collectors.toList());
|
|
|
|
|
|
|
|
int ruleSize = rules.size();
|
|
|
|
|
|
|
|
curAchievement = rules.get(rules.size() - 1);
|
|
|
|
|
|
|
|
for (int i = 0; i <= rules.size() - 1; i++) {
|
|
|
|
|
|
|
|
AchievementRule curRule = rules.get(i);
|
|
|
|
|
|
|
|
int flag = praiseCount / curRule.getPromotionCount();
|
|
|
|
|
|
|
|
if (ruleSize == 1) {
|
|
|
|
|
|
|
|
n = praiseCount / curRule.getLevelCount() + 1;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag >= 1) {
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
|
|
|
curAchievement = curRule;
|
|
|
|
|
|
|
|
AchievementRule lastRule = rules.get(i + 1);
|
|
|
|
|
|
|
|
n = (praiseCount - lastRule.getPromotionCount()) / curRule.getLevelCount() + 1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
AchievementRule nextRule = rules.get(i - 1);
|
|
|
|
|
|
|
|
curAchievement = nextRule;
|
|
|
|
|
|
|
|
n = (praiseCount - curRule.getPromotionCount()) / nextRule.getLevelCount() + 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == rules.size() - 1) {
|
|
|
|
|
|
|
|
n = praiseCount / curRule.getLevelCount() + 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取记录
|
|
|
|
|
|
|
|
records = appraiseRecord.getNodes().stream()
|
|
|
|
|
|
|
|
.sorted(Comparator.comparing(AppraiseRecordItem::getCreateTime).reversed())
|
|
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 构建报告
|
|
|
|
|
|
|
|
return StudentReportVo.builder()
|
|
|
|
|
|
|
|
.name(student.getName())
|
|
|
|
|
|
|
|
.studentId(studentId)
|
|
|
|
|
|
|
|
.className(student.getClassId()) // 假设班级名可以从学生信息中获取
|
|
|
|
|
|
|
|
.praiseCount(praiseCount)
|
|
|
|
|
|
|
|
.score(appraiseRecordOpt.isPresent() ? appraiseRecordOpt.get().getScore() : 0) // 默认分数为 0
|
|
|
|
|
|
|
|
.beyondPercent(beyondPercent)
|
|
|
|
|
|
|
|
.topPraiseTeacher(new StudentReportVo.Teacher(null, null)) // 默认值为 null
|
|
|
|
|
|
|
|
.topCriticalTeacher(new StudentReportVo.Teacher(null, null)) // 默认值为 null
|
|
|
|
|
|
|
|
.topPraiseNode(null) // 默认值为 null
|
|
|
|
|
|
|
|
.topCriticalNode(null) // 默认值为 null
|
|
|
|
|
|
|
|
.praiseDistribution(praiseDistribution)
|
|
|
|
|
|
|
|
.criticalDistribution(criticalDistribution)
|
|
|
|
|
|
|
|
.curAchievement(curAchievement)
|
|
|
|
|
|
|
|
.achievementN(n)
|
|
|
|
|
|
|
|
.records(records)
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public StudentReportVo studentReport(IdRequest idRequest) {
|
|
|
|
public StudentReportVo studentReport(IdRequest idRequest) {
|
|
|
|
String studentId = idRequest.getId();
|
|
|
|
String studentId = idRequest.getId();
|
|
|
|