update 报告相关算法

develop
hhb@hotmail.com 1 month ago
parent 7530edc04a
commit 9fe2a914d1

@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@RestController
@ -36,4 +37,17 @@ public class LaborEducationController {
Map<String, Object> res = laborEducationService.getDetails(findDto,request);
return R.success(res);
}
@PostMapping("getStudentSemesterScores")
@ApiOperation("分学期比对分析")
public R<Map<String, Object>> getStudentSemesterScores(@Valid @RequestBody LaborDto laborDto, HttpServletRequest request) {
Map<String, Object> res = laborEducationService.getStudentSemesterScores(laborDto,request);
return R.success(res);
}
@PostMapping("getStudentMonthlyScores")
@ApiOperation("按月比对分析")
public R<List<Map<String, Object>>> getStudentMonthlyScores(@Valid @RequestBody LaborDto laborDto, HttpServletRequest request) {
List<Map<String, Object>> res = laborEducationService.getStudentMonthlyScores(laborDto);
return R.success(res);
}
}

@ -2,8 +2,6 @@ package cn.teammodel.controller.admin.controller;
import cn.teammodel.common.R;
import cn.teammodel.controller.admin.service.TeacherService;
import cn.teammodel.model.dto.admin.exam.ExamRecordDto;
import cn.teammodel.model.dto.admin.exam.OverViewDto;
import cn.teammodel.model.dto.admin.teacher.TeacherDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@ -6,9 +6,12 @@ import cn.teammodel.model.dto.admin.labor.LaborDto;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
public interface LaborEducationService {
Map<String,Object> getAnalysis(LaborDto laborDto, HttpServletRequest request);
Map<String,Object> getDetails(FindDto findDto, HttpServletRequest request);
Map<String,Object> getStudentSemesterScores(LaborDto laborDto, HttpServletRequest request);
List<Map<String, Object>> getStudentMonthlyScores(LaborDto laborDto);
}

@ -38,6 +38,10 @@ import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@ -529,24 +533,45 @@ public class LaborEducationServiceImpl implements LaborEducationService {
Map<String, Integer> parentNodeScoreCount = new HashMap<>(); // 记录每个父节点的知识点数量
for (LaborEducationServiceImpl.KnowledgeScoreRate scoreRate : studentScoreRates) {
AppraiseTreeNode node = findKnowledgeNode(appraise.getNodes(), scoreRate.getKnowledge());
String knowledge = scoreRate.getKnowledge();
AppraiseTreeNode node = findKnowledgeNode(appraise.getNodes(), knowledge);
// 1. 查找知识点所属的知识块假设通过knowledgeMap映射
String block = findKnowledgeBlock(knowledgeMap, knowledge); // 自定义方法从knowledgeMap中获取知识点所属的知识块
// 2. 查找知识块对应的父节点
AppraiseTreeNode parentNode = null;
if (block != null) {
parentNode = findParentNodeByBlockName(appraise.getNodes(), block); // 自定义方法:按知识块名称查找父节点
}
// 3. 处理匹配到节点的情况
if (node != null) {
AppraiseTreeNode parentNode = findParentNode(appraise.getNodes(), node.getId());
if (parentNode != null) {
// 累加父节点的得分
double currentScore = parentNodeScoreRates.getOrDefault(parentNode.getName(), 0.0);
parentNodeScoreRates.put(parentNode.getName(), currentScore + scoreRate.getScoreRate());
parentNode = findParentNode(appraise.getNodes(), node.getId());
} else {
// 未匹配知识点默认60分归入其所属知识块的父节点
double defaultScore = 60.0;
if (parentNode == null) {
// 若知识块无对应父节点,归入全局默认块
parentNodeScoreRates.merge("默认块", defaultScore, Double::sum);
parentNodeScoreCount.merge("默认块", 1, Integer::sum);
} else {
// 归入知识块对应的父节点
parentNodeScoreRates.merge(parentNode.getName(), defaultScore, Double::sum);
parentNodeScoreCount.merge(parentNode.getName(), 1, Integer::sum);
}
continue; // 跳过后续匹配逻辑
}
// 累加父节点的知识点数量
int currentCount = parentNodeScoreCount.getOrDefault(parentNode.getName(), 0);
parentNodeScoreCount.put(parentNode.getName(), currentCount + 1);
// 4. 累加得分到父节点(原有逻辑)
if (parentNode != null) {
parentNodeScoreRates.merge(parentNode.getName(), scoreRate.getScoreRate(), Double::sum);
parentNodeScoreCount.merge(parentNode.getName(), 1, Integer::sum);
} else {
// 如果没有找到父节点,直接使用当前知识点的得分
parentNodeScoreRates.put(node.getName(), scoreRate.getScoreRate());
parentNodeScoreCount.put(node.getName(), 1);
}
}
}
// 计算父节点的平均得分原始正确率转换为基于60分的加分制
parentNodeScoreRates.replaceAll((parentNodeName, totalScore) -> {
@ -576,6 +601,30 @@ public class LaborEducationServiceImpl implements LaborEducationService {
return parentNodeScoreRates;
}
// 从knowledgeMap中查找知识点所属的知识块
private static String findKnowledgeBlock(Map<String, List<List<String>>> knowledgeMap, String knowledge) {
for (Map.Entry<String, List<List<String>>> entry : knowledgeMap.entrySet()) {
for (List<String> subList : entry.getValue()) {
if (subList.contains(knowledge)) {
return entry.getKey();
}
}
}
return null;
}
// 按知识块名称查找父节点
private static AppraiseTreeNode findParentNodeByBlockName(List<AppraiseTreeNode> nodes, String blockName) {
for (AppraiseTreeNode node : nodes) {
if (node.getName().equals(blockName)) {
return node;
}
AppraiseTreeNode found = findParentNodeByBlockName(node.getChildren(), blockName);
if (found != null) return found;
}
return null;
}
/**
*
@ -826,7 +875,6 @@ public class LaborEducationServiceImpl implements LaborEducationService {
Map<String, List<List<String>>> knowledgeMap,
Map<String, List<Double>> points,
Map<String, Double> knowledgeTotalScore) {
Map<String, Double> knowledgeTotalAverage = new HashMap<>();
Map<String, Integer> knowledgeExamCount = new HashMap<>();
@ -1321,4 +1369,673 @@ public class LaborEducationServiceImpl implements LaborEducationService {
return null; // 未找到父节点
}
//获取每月各个知识库综合得分以及整体得分内容
public List<Map<String, Object>> getStudentMonthlyScores(LaborDto laborDto) {
// 1. 获取基础信息
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
String studentId = laborDto.getStudentId();
// 2. 获取相关开课记录
List<LessonRecord> records = getLessonRecords(laborDto, schoolId);
//List<String> classIds = Collections.singletonList(laborDto.getClassId());
List<RecordVo> res = appraiseRecordRepository.getStudentRecords(
String.format(PK.PK_APPRAISE_RECORD, schoolId),
laborDto.getStudentId(),
laborDto.getStartTime(),
laborDto.getEndTime(),
"德育"
);
// 3. 获取考试及知识点映射
Map<String, List<List<String>>> examKnowledgeMap = new HashMap<>();
Map<String, List<Double>> points = new HashMap<>();
List<ExamVo> exams = getExamsWithKnowledge(laborDto, records, examKnowledgeMap, points);
// 4. 获取考试结果并按月份分组
Map<Integer, List<ExamResultWrapper>> monthlyResults = getMonthlyExamResults(
schoolId, laborDto.getClassId(), exams, records, examKnowledgeMap, points, res
);
// 5. 获取知识块配置
Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(
schoolId, laborDto.getPeriodId(), PK.PK_APPRAISE
);
appraise = evaluationService.buildTree(appraise);
Map<String, List<String>> knowledgeBlockMap = getKnowledgeBlockToPointsMap(appraise);
// 在获取知识块配置后添加
Map<String, String> pointToBlockMap = new HashMap<>();
knowledgeBlockMap.forEach((block, ps) -> {
ps.forEach(point -> pointToBlockMap.put(point, block));
});
// 处理主观评价数据,统计每个月的次数
Map<String, Map<Integer, Integer>> blockMonthlyCounts = new HashMap<>();
Map<String, Map<Integer, Integer>> finalBlockMonthlyCounts = blockMonthlyCounts;
res.stream()
.filter(vo -> studentId.equals(vo.getTargetId()))
.forEach(vo -> {
// 获取知识点对应的知识块
String originalPoint = vo.getAppraiseName();
String block = pointToBlockMap.getOrDefault(originalPoint, "其他");
// 计算月份
int month = getMonthFromTimestamp(vo.getCreateTime());
// 更新计数器
Map<Integer, Integer> monthCounts = finalBlockMonthlyCounts.computeIfAbsent(block, k -> new HashMap<>());
int current = monthCounts.getOrDefault(month, 0);
monthCounts.put(month, current + (vo.isPraise() ? 1 : -1));
});
// 处理负数和非分类数据
blockMonthlyCounts = blockMonthlyCounts.entrySet().stream()
.filter(entry -> !"未分类".equals(entry.getKey())) // 过滤掉未映射的评价
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> Math.max(e.getValue(), 0)
)
)));
// 6. 处理每月数据,传入主观评价统计结果
List<Map<String, Object>> monthlyData = processMonthlyData(studentId, monthlyResults, knowledgeBlockMap,
getMonthsBetween(laborDto.getStartTime(), laborDto.getEndTime()), blockMonthlyCounts);
// 7. 计算综合得分并添加到结果
return addOverallScore(monthlyData, knowledgeBlockMap.keySet());
}
// 新增方法:添加综合得分
private List<Map<String, Object>> addOverallScore(
List<Map<String, Object>> monthlyData,
Set<String> knowledgeBlocks
) {
// 创建综合得分容器
Map<String, Double> totalScores = new HashMap<>();
Map<String, Integer> scoreCounts = new HashMap<>();
// 累加所有月份得分
for (Map<String, Object> monthData : monthlyData) {
for (String block : knowledgeBlocks) {
Double score = (Double) monthData.get(block);
if (score != null) {
totalScores.put(block, totalScores.getOrDefault(block, 0.0) + score);
scoreCounts.put(block, scoreCounts.getOrDefault(block, 0) + 1);
}
}
}
// 计算平均分
Map<String, Object> overall = new LinkedHashMap<>();
overall.put("type", "overall");
for (String block : knowledgeBlocks) {
int count = scoreCounts.getOrDefault(block, 1);
double avg = totalScores.getOrDefault(block, 0.0) / count;
overall.put(block, Double.parseDouble(String.format("%.2f", avg)));
}
// 创建新的结果列表(保持原数据不变)
List<Map<String, Object>> result = new ArrayList<>(monthlyData);
result.add(overall);
return result;
}
private List<LessonRecord> getLessonRecords(LaborDto laborDto, String schoolId) {
LessonRecordQueryService queryService = new LessonRecordQueryService(lessonRecordRepository);
try {
return queryService.queryLessonsInParallel(
String.format(PK.PK_LESSON_RECORD, schoolId),
laborDto.getStartTime(),
laborDto.getEndTime(),
laborDto.getSubjectId(),
null,
null,
laborDto.getPeriodId()
);
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "课程查询异常");
} finally {
queryService.shutdown();
}
}
private List<ExamVo> getExamsWithKnowledge(LaborDto laborDto, List<LessonRecord> records,
Map<String, List<List<String>>> examKnowledgeMap,
Map<String, List<Double>> points) {
List<String> lessonRecordIds = records.stream().map(LessonRecord::getId).collect(Collectors.toList());
List<ExamVo> exams = examRepository.findExamsByIds(laborDto.getSource(), lessonRecordIds);
exams.forEach(exam -> {
if (!exam.getPapers().isEmpty() && exam.getPapers().get(0).getKnowledge() != null) {
examKnowledgeMap.put(exam.getId(), exam.getPapers().get(0).getKnowledge());
points.put(exam.getId(), exam.getPapers().get(0).getPoint());
}
});
return exams.stream()
.filter(exam -> !exam.getClasses().isEmpty())
.collect(Collectors.toList());
}
private Map<Integer, List<ExamResultWrapper>> getMonthlyExamResults(String schoolId, String classId,
List<ExamVo> exams, List<LessonRecord> records,
Map<String, List<List<String>>> examKnowledgeMap,
Map<String, List<Double> > points,
List<RecordVo> res) {
Map<Integer, List<ExamResultWrapper>> monthlyResults = new HashMap<>();
exams.forEach(exam -> {
List<ExamClassResult> results = examClassResultRepository.findById(
String.format(PK.CLASS_RESULT, schoolId), classId, exam.getId()
);
results.forEach(result -> {
records.stream()
.filter(r -> r.getId().equals(exam.getLessonRecordId()))
.findFirst()
.ifPresent(record -> {
int month = getMonthFromTimestamp(record.getStartTime());
List<List<String>> knowledge = examKnowledgeMap.get(exam.getId());
List<Double> point = points.get(exam.getId());
monthlyResults.computeIfAbsent(month, k -> new ArrayList<>())
.add(new ExamResultWrapper(exam.getId(),result, knowledge,point));
});
});
});
return monthlyResults;
}
private List<Map<String, Object>> processMonthlyData(String studentId,
Map<Integer, List<ExamResultWrapper>> monthlyResults,
Map<String, List<String>> knowledgeBlockMap,
Set<Integer> months,
Map<String, Map<Integer, Integer>> blockMonthlyCounts) {
return months.stream().map(month -> {
Map<String, Object> monthData = new LinkedHashMap<>();
monthData.put("month", month);
List<ExamResultWrapper> wrappers = monthlyResults.getOrDefault(month, Collections.emptyList());
List<ExamClassResult> examResults = wrappers.stream()
.map(ExamResultWrapper::getResult)
.collect(Collectors.toList());
Map<String, List<List<String>>> monthlyKnowledgeMap = new HashMap<>();
Map<String, List<Double>> monthlyPoints = new HashMap<>();
wrappers.forEach(wrapper -> {
String examId = wrapper.getExamId();
monthlyKnowledgeMap.put(examId, wrapper.getKnowledge());
monthlyPoints.put(examId, wrapper.getPoints());
});
Map<String, Double> knowledgeScores = calculateMonthlyKnowledgeScores(
studentId, examResults, monthlyKnowledgeMap, monthlyPoints
);
Set<String> monthlyTestedKnowledges = knowledgeScores.keySet();
knowledgeBlockMap.forEach((block, knowledges) -> {
List<String> testedKnowledges = knowledges.stream()
.filter(monthlyTestedKnowledges::contains)
.collect(Collectors.toList());
double finalScore;
if (testedKnowledges.isEmpty()) {
finalScore = 60.0;
} else {
double averageCorrectRate = testedKnowledges.stream()
.mapToDouble(k -> knowledgeScores.get(k))
.average()
.orElse(60.0);
finalScore = 60.0 + (averageCorrectRate * 0.4);
finalScore = Math.min(finalScore, 100.0);
}
finalScore = Double.parseDouble(String.format("%.2f", finalScore));
// 处理主观评分(根据知识块获取对应次数)
Map<Integer, Integer> monthlyCounts = blockMonthlyCounts.getOrDefault(block, new HashMap<>());
int praiseCount = monthlyCounts.getOrDefault(month, 0);
// 步骤1计算次数转换分数0-40分
final double K = 50.0; // 调整系数控制分数增长速度
double convertedScore = (praiseCount * 40.0) / (praiseCount + K);
convertedScore = Math.min(convertedScore, 40.0);
convertedScore = Double.parseDouble(String.format("%.2f", convertedScore));
// 步骤2计算主观总分60分基础 + 转换分)
double subjectiveTotal = 60.0 + convertedScore;
subjectiveTotal = Math.min(subjectiveTotal, 100.0);
subjectiveTotal = Double.parseDouble(String.format("%.2f", subjectiveTotal));
// 步骤3主客观合并主观60% + 客观40%
double combinedScore = (subjectiveTotal * 0.6) + (finalScore * 0.4);
combinedScore = Double.parseDouble(String.format("%.2f", combinedScore));
monthData.put(block, combinedScore);
});
return monthData;
})
.sorted(Comparator.comparingInt(m -> (Integer) m.get("month")))
.collect(Collectors.toList());
}
private Map<String, Double> calculateMonthlyKnowledgeScores(String studentId,
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Map<String, List<Double>> points) {
Map<String, Double> knowledgeTotalScore = new HashMap<>();
Map<String, Double> knowledgeTotalAverage = new HashMap<>();
Map<String, Integer> knowledgeExamCount = new HashMap<>();
// 处理无考试数据的情况初始化为60分
if (examResults.isEmpty()) {
knowledgeMap.values().stream()
.flatMap(List::stream)
.flatMap(List::stream)
.distinct()
.forEach(knowledge -> knowledgeTotalScore.put(knowledge, 60.0));
return knowledgeTotalScore;
}
// 遍历每个考试结果
for (ExamClassResult examResult : examResults) {
int studentIndex = examResult.getStudentIds().indexOf(studentId);
if (studentIndex == -1) continue;
if (examResult.getStatus().get(studentIndex) == 1) continue; // 跳过无效状态
List<List<String>> knowledgeList = knowledgeMap.get(examResult.getExamId());
List<Double> pointList = points.get(examResult.getExamId());
if (knowledgeList == null || pointList == null) continue;
List<Double> studentScores = examResult.getStudentScores().get(studentIndex);
if (studentScores == null || knowledgeList.size() != studentScores.size()) continue;
// 计算当前考试的知识点得分
Map<String, Double> currentExamScores = new HashMap<>();
Map<String, Integer> currentExamCounts = new HashMap<>();
for (int i = 0; i < studentScores.size(); i++) {
List<String> knowledges = knowledgeList.get(i);
double score = studentScores.get(i);
if (knowledges.isEmpty() || score < 0) continue;
double perKnowledgeScore = score / knowledges.size();
knowledges.forEach(knowledge -> {
currentExamScores.merge(knowledge, perKnowledgeScore, Double::sum);
currentExamCounts.merge(knowledge, 1, Integer::sum);
});
}
// 更新累计数据
currentExamScores.forEach((knowledge, total) -> {
double average = total / currentExamCounts.get(knowledge);
knowledgeTotalAverage.merge(knowledge, average, Double::sum);
knowledgeExamCount.merge(knowledge, 1, Integer::sum);
});
}
// 计算最终得分基础60 + 实际得分,限制范围)
knowledgeTotalAverage.forEach((knowledge, total) -> {
int count = knowledgeExamCount.get(knowledge);
double finalScore = 60.0 + Math.min(total / count, 40.0);
knowledgeTotalScore.put(knowledge, Math.min(finalScore, 100.0));
});
// 确保所有知识点都有值(处理部分知识点未出现在考试中的情况)
knowledgeMap.values().stream()
.flatMap(List::stream)
.flatMap(List::stream)
.distinct()
.forEach(knowledge -> knowledgeTotalScore.putIfAbsent(knowledge, 60.0));
// 保留两位小数
knowledgeTotalScore.replaceAll((k, v) -> Double.parseDouble(String.format("%.2f", v)));
return knowledgeTotalScore;
}
// 辅助类封装考试结果和知识点
@Data
@AllArgsConstructor
private static class ExamResultWrapper {
private String examId;
private ExamClassResult result;
private List<List<String>> knowledge; // 每个题目对应的知识点列表
private List<Double> points;
}
// 时间处理工具方法
private int getMonthFromTimestamp(Long timestamp) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault())
.getMonthValue();
}
private Set<Integer> getMonthsBetween(Long start, Long end) {
Set<Integer> months = new TreeSet<>();
LocalDateTime startDate = Instant.ofEpochMilli(start).atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime endDate = Instant.ofEpochMilli(end).atZone(ZoneId.systemDefault()).toLocalDateTime();
while (!startDate.isAfter(endDate)) {
months.add(startDate.getMonthValue());
startDate = startDate.plusMonths(1);
}
return months;
}
// 学期配置实体对应JSON结构
@Data
@AllArgsConstructor
public static class SemesterConfig {
private String name;
private int start; // 1表示学年开始学期
private int month; // 月份1-12
private int day; // 日期1-31
private String id;
}
// 学期时间范围实体
@Data
@AllArgsConstructor
public static class SemesterPeriod {
private String name;
private LocalDate startDate;
private LocalDate endDate;
// 转换为时间戳(用于查询)
public Long getStartTimestamp() {
return startDate.atStartOfDay(ZoneId.systemDefault())
.toInstant().toEpochMilli();
}
public Long getEndTimestamp() {
return endDate.atTime(23, 59, 59)
.atZone(ZoneId.systemDefault())
.toInstant().toEpochMilli();
}
}
// 分学期比对,分析 学期数据
public Map<String, Object> getStudentSemesterScores(LaborDto laborDto,HttpServletRequest request) {
// 1. 获取学期配置
List<School.Semester> semesters = schoolRepository.findSemestersById(laborDto.getCode(), laborDto.getPeriodId());
List<SemesterConfig> configs = new ArrayList<>();
for (School.Semester semester : semesters) {
configs.add(new SemesterConfig(semester.getName(), semester.getStart(), semester.getMonth(), semester.getDay(), semester.getId()));
}
// 2. 计算学期时间范围
Map<String, SemesterPeriod> periods = calculateSemesters(configs);
SemesterPeriod current = periods.get("current");
SemesterPeriod last = periods.get("last");
// 3. 查询并处理数据
Map<String, Object> currentData = current != null ?
processSemesterData(laborDto, current, request,true) :
Collections.emptyMap();
Map<String, Object> lastData = last != null ?
processSemesterData(laborDto, last, request,true) :
Collections.emptyMap();
// 4. 组合最终结果
return combineResults(laborDto, currentData, lastData);
}
// 核心学期计算逻辑
private Map<String, SemesterPeriod> calculateSemesters(List<SemesterConfig> semesters) {
LocalDate now = LocalDate.now();
int currentYear = now.getYear();
// 1. 确定学年起始学期
SemesterConfig startSemester = semesters.stream()
.filter(s -> s.start == 1)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未配置起始学期"));
// 2. 计算当前所属学年
int academicYear = calculateAcademicYear(startSemester, LocalDate.now());
// 3. 生成当前学年学期
List<SemesterPeriod> currentYearSemesters = generateYearSemesters(
semesters, academicYear, startSemester);
// 4. 确定当前学期
SemesterPeriod current = currentYearSemesters.stream()
.filter(sp -> !now.isBefore(sp.startDate) && !now.isAfter(sp.endDate))
.findFirst()
.orElse(null);
// 5. 确定上学期
SemesterPeriod last = findLastSemester(current, semesters, academicYear, startSemester);
return new HashMap<String, SemesterPeriod>() {{
put("current", current);
put("last", last);
}};
}
// 增强学术年度计算方法
private int calculateAcademicYear(SemesterConfig startSemester, LocalDate currentDate) {
int currentYear = currentDate.getYear();
int currentMonth = currentDate.getMonthValue();
return (currentMonth < startSemester.month) ? currentYear -1 : currentYear;
}
// 生成学年学期时间范围
private List<SemesterPeriod> generateYearSemesters(List<SemesterConfig> semesters,
int academicYear,
SemesterConfig startSemester) {
List<SemesterPeriod> periods = new ArrayList<>();
// 1. 生成各学期开始日期
for (SemesterConfig sc : semesters) {
int year = academicYear;
if (sc.month < startSemester.month) {
year += 1; // 跨年学期(如 2024 学年下学期是 2025 年 2 月)
}
LocalDate startDate = LocalDate.of(year, sc.month, sc.day);
periods.add(new SemesterPeriod(sc.name, startDate, null));
}
periods.sort(Comparator.comparing(SemesterPeriod::getStartDate));
// 2. 计算结束日期
for (int i = 0; i < periods.size(); i++) {
SemesterPeriod curr = periods.get(i);
if (i < periods.size() - 1) {
// 非最后学期:下一学期开始前 1 天
curr.setEndDate(periods.get(i + 1).getStartDate().minusDays(1));
} else {
// 最后学期:学年结束年份的 7 月 31 日2025-07-31
int endYear = academicYear + 1; // 关键修改:统一用学年结束年份
curr.setEndDate(LocalDate.of(endYear, 7, 31));
}
// 3. 校验时间顺序
if (curr.getStartDate().isAfter(curr.getEndDate())) {
throw new IllegalStateException("学期日期无效: " + curr.getName()
+ " 开始日期(" + curr.getStartDate()
+ ") 不能晚于结束日期(" + curr.getEndDate() + ")");
}
}
return periods;
}
// 查找上学期(含跨学年处理)
private SemesterPeriod findLastSemester(SemesterPeriod current,
List<SemesterConfig> semesters,
int academicYear,
SemesterConfig startSemester) {
if (current == null) return null;
List<SemesterPeriod> currentYearSemesters = generateYearSemesters(
semesters, academicYear, startSemester);
int index = currentYearSemesters.indexOf(current);
if (index > 0) {
return currentYearSemesters.get(index-1);
} else {
// 获取去年最后一个学期
List<SemesterPeriod> lastYear = generateYearSemesters(
semesters, academicYear-1, startSemester);
return lastYear.isEmpty() ? null : lastYear.get(lastYear.size()-1);
}
}
// 处理学期数据
private Map<String, Object> processSemesterData(LaborDto laborDto,
SemesterPeriod semester,
HttpServletRequest request,
boolean isCurrent) {
Map<String, Object> result = new HashMap<>();
// 1. 设置时间范围查询条件
laborDto.setStartTime(convertToTimestamp(semester.startDate));
laborDto.setEndTime(convertToTimestamp(semester.endDate));
// 2. 获取原始分析数据
Map<String, Object> analysisData = getAnalysis(laborDto, request);
// 3. 提取知识点次数(仅当前学期)
if (isCurrent) {
List<Map<String, Object>> scores = (List<Map<String, Object>>) analysisData.get("scores");
Map<String, Integer> knowledgeCounts = scores.stream()
.filter(s -> laborDto.getStudentId().equals(s.get("studentId")))
.findFirst()
.map(s -> (List<Map<String, Object>>) s.get("appraises"))
.map(appraises -> appraises.stream()
.collect(Collectors.toMap(
a -> (String) a.get("appraiseName"),
a -> 1,
Integer::sum
)))
.orElse(new HashMap<>());
result.put("knowledgeCounts", knowledgeCounts);
}
// 4. 提取综合得分
Map<String, Double> blockScores = extractBlockScores(analysisData, laborDto.getStudentId());
result.put("blockScores", blockScores);
// 5. 提取班级均分
Map<String, List<Map<String, Object>>> classScoreRate = (Map<String, List<Map<String, Object>>>) analysisData.get("classScoreRate");
List<Map<String, Object>> classScoreList = classScoreRate.get("classScoreRates");
Map<String, Double> classAverages = classScoreList.stream()
.findFirst()
.map(c -> (List<Map<String, Object>>) c.get("blocks"))
.map(blocks -> blocks.stream()
.collect(Collectors.toMap(
b -> (String) b.get("name"),
b -> (Double) b.get("score")
)))
.orElse(new HashMap<>());
result.put("classAverage", classAverages);
return result;
}
private Map<String, Double> extractBlockScores(Map<String, Object> data, String studentId) {
return ((List<Map<String, Object>>) data.get("scores")).stream()
.filter(s -> studentId.equals(s.get("studentId")))
.findFirst()
.map(scoreMap -> (Map<String, Map<String, Object>>) scoreMap.get("scores")) // 获取知识块得分对象
.map(scoresMap -> scoresMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey, // 知识块名称(如"道德修养"
entry -> (Double) entry.getValue().get("compositeScore") // 提取复合分数
))
)
.orElse(new HashMap<>());
}
// 辅助方法转换LocalDate为时间戳
private long convertToTimestamp(LocalDate date) {
return date.atStartOfDay(ZoneId.systemDefault())
.toInstant()
.toEpochMilli();
}
// 结果合并方法
private Map<String, Object> combineResults(LaborDto laborDto,
Map<String, Object> currentData,
Map<String, Object> lastData) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("studentId", laborDto.getStudentId());
Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(laborDto.getCode(), laborDto.getPeriodId(), PK.PK_APPRAISE);
// 获取知识块配置
appraise = evaluationService.buildTree(appraise);
Set<String> knowledgeBlocks = loadKnowledgeBlocks(appraise);
List<Map<String, Object>> scores = new ArrayList<>();
for (String block : knowledgeBlocks) {
Map<String, Object> blockData = new LinkedHashMap<>();
blockData.put("name", block);
Map<String, Double> currentBlockScores = (Map<String, Double>) currentData.get("blockScores");
double moralScore = currentBlockScores.getOrDefault(block, 0.0); // 精确获取目标知识块
blockData.put("TScore", moralScore);
Map<String, Double> lastBlockScores = (Map<String, Double>) lastData.get("blockScores");
double lastScore = lastBlockScores.getOrDefault(block, 0.0);
blockData.put("LScore", lastScore);
// 班级均分
Map<String, Double> classAvg = (Map<String, Double>) currentData.get("classAverage");
blockData.put("classScore", classAvg.getOrDefault(block, 0.0));
// 知识点次数
if (currentData.containsKey("knowledgeCounts")) {
Map<String, Integer> counts = (Map<String, Integer>) currentData.get("knowledgeCounts");
Appraise finalAppraise = appraise;
counts.entrySet().stream()
.filter(e -> isBelongToBlock(e.getKey(), block, finalAppraise))
.forEach(e -> blockData.put(e.getKey(), e.getValue()));
}
scores.add(blockData);
}
result.put("scores", scores);
return result;
}
private Set<String> loadKnowledgeBlocks(Appraise appraise) {
// 从appraise配置获取所有德育知识块
Set<String> knowledgePoints = new HashSet<>();
for (AppraiseTreeNode node : appraise.getNodes()) {
if ("德育".equals(node.getName())) {
for (AppraiseTreeNode secondLevelNode : node.getChildren()) {
knowledgePoints.add(secondLevelNode.getName());
}
}
}
return knowledgePoints;
}
private boolean isBelongToBlock(String knowledge, String block,Appraise appraise) {
// 实现知识点归属判断逻辑
Map<String, List<String>> knowledgeBlockToPointsMap = getKnowledgeBlockToPointsMap(appraise);
return knowledgeBlockToPointsMap.getOrDefault(block, Collections.emptyList())
.contains(knowledge);
}
}

@ -25,4 +25,5 @@ public class LaborDto {
private String academicYearId;
private String classId;
private String source = "1";
private String studentId;
}

@ -86,6 +86,16 @@ public interface AppraiseRecordRepository extends CosmosRepository<AppraiseRecor
/**
*
*/
@Query("select c.id as recordId, c.name as targetName, c.avatar, c.classId,c.className, n.id as recordNodeId, c.targetId, c.targetType, n.creator, n.createTime, n.appraiseNode.name as appraiseName, n.appraiseNode.isPraise, n.device,n.appraiseNode.path from Student as c join n in c.nodes where " +
"c.code = @code and " +
"c.targetId = @studentId and " +
"(IS_NULL(@startTime) or n.createTime >= @startTime) and " +
"(IS_NULL(@endTime) or n.createTime <= @endTime) and " +
"(IS_NULL(@typeName) or array_contains(n.appraiseNode.path ,@typeName)) "
)
List<RecordVo> getStudentRecords(String code, String studentId, Long startTime, Long endTime,String typeName);
@Query("select c.id as recordId, c.name as targetName, c.avatar, c.classId,c.className, n.id as recordNodeId, c.targetId, c.targetType, n.creator, n.createTime, n.appraiseNode.name as appraiseName, n.appraiseNode.isPraise, n.device,n.appraiseNode.path from Student as c join n in c.nodes where " +
"c.code = @code and " +
"c.academicYearId = @academicYearId and " +

Loading…
Cancel
Save