update 德育看板

develop
hhb@hotmail.com 2 months ago
parent 158ef4b621
commit 195e62ef50

@ -29,6 +29,8 @@ public interface PK {
String WEEK_DUTY_RECORD = "DutyRecord-%s"; String WEEK_DUTY_RECORD = "DutyRecord-%s";
String CHAT_APP = "ChatApp"; String CHAT_APP = "ChatApp";
String NEWS = "News-%s"; String NEWS = "News-%s";
String CLASS_RESULT = "ExamClassResult-%s";
String EXAM = "Exam-%s";
/** /**
* *

@ -0,0 +1,32 @@
package cn.teammodel.controller.admin.controller;
import cn.teammodel.common.R;
import cn.teammodel.controller.admin.service.ExamService;
import cn.teammodel.controller.admin.service.LaborEducationService;
import cn.teammodel.model.dto.admin.exam.OverViewDto;
import cn.teammodel.model.dto.admin.labor.LaborDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Map;
@RestController
@RequestMapping("admin/labor")
@Api(tags = "管理员端-德育分析")
public class LaborEducationController {
@Resource
private LaborEducationService laborEducationService;
@PostMapping("getLaborAnalysis")
@ApiOperation("获取德育看板详细内容")
public R<Map<String, Object>> getLaborAnalysis(@Valid @RequestBody LaborDto laborDto, HttpServletRequest request) {
Map<String, Object> res = laborEducationService.getAnalysis(laborDto,request);
return R.success(res);
}
}

@ -0,0 +1,12 @@
package cn.teammodel.controller.admin.service;
import cn.teammodel.model.dto.admin.exam.OverViewDto;
import cn.teammodel.model.dto.admin.labor.LaborDto;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public interface LaborEducationService {
Map<String,Object> getAnalysis(LaborDto laborDto, HttpServletRequest request);
}

@ -155,12 +155,17 @@ public class AdminAppraiseServiceImpl implements AdminAppraiseService {
startTime = mondayOfCurWeek.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); startTime = mondayOfCurWeek.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
endTime = Instant.now().toEpochMilli(); endTime = Instant.now().toEpochMilli();
} }
List<ClassInfo> classes = classRepository.findClassBySchoolIdAndPeriodId(timeRangeDto.getPeriodId(), String.format(PK.CLASS, schoolId));
if (classes.isEmpty()) {
return new ArrayList<>();
}
List<String> classIds = classes.stream().map(ClassInfo::getId).collect(Collectors.toList());
List<RecordVo> res = appraiseRecordRepository.latestRecords( List<RecordVo> res = appraiseRecordRepository.latestRecords(
String.format(PK.PK_APPRAISE_RECORD, schoolId), String.format(PK.PK_APPRAISE_RECORD, schoolId),
academicYearId, academicYearId,
startTime, startTime,
endTime endTime,
classIds
); );
if (res != null) { if (res != null) {

@ -0,0 +1,970 @@
package cn.teammodel.controller.admin.service.impl;
import cn.teammodel.common.ErrorCode;
import cn.teammodel.common.PK;
import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.controller.admin.service.LaborEducationService;
import cn.teammodel.model.dto.admin.common.GroupDto;
import cn.teammodel.model.dto.admin.common.RMember;
import cn.teammodel.model.dto.admin.labor.LaborDto;
import cn.teammodel.model.entity.appraise.Appraise;
import cn.teammodel.model.entity.appraise.AppraiseTreeNode;
import cn.teammodel.model.entity.common.Exam;
import cn.teammodel.model.entity.common.ExamClassResult;
import cn.teammodel.model.entity.school.ClassInfo;
import cn.teammodel.model.entity.school.LessonRecord;
import cn.teammodel.model.vo.appraise.RecordVo;
import cn.teammodel.repository.*;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.service.impl.EvaluationServiceImpl;
import cn.teammodel.test.LessonRecordQueryService;
import cn.teammodel.utils.GroupUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@Service
public class LaborEducationServiceImpl implements LaborEducationService {
@Resource
private LessonRecordRepository lessonRecordRepository;
@Resource
private ClassRepository classRepository;
@Resource
private AppraiseRecordRepository appraiseRecordRepository;
@Resource
private ExamRepository examRepository;
@Resource
private ExamClassResultRepository examClassResultRepository;
@Resource
private AppraiseRepository appraiseRepository;
@Resource
private EvaluationServiceImpl evaluationService;
private static Environment environment; // 静态字段
@Autowired
private Environment env; // 非静态字段
@PostConstruct
public void init() {
LaborEducationServiceImpl.environment = env; // 在初始化时将非静态字段赋值给静态字段
}
@Override
public Map<String, Object> getAnalysis(LaborDto laborDto, HttpServletRequest request) {
//根据具体参数查询相关课列内容
List<LessonRecord> records;
LessonRecordQueryService queryService = new LessonRecordQueryService(lessonRecordRepository);
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
String lessonRecordKey = String.format(PK.PK_LESSON_RECORD, laborDto.getCode());
Long startTime = laborDto.getStartTime();
Long endTime = laborDto.getEndTime();
String subjectId = laborDto.getSubjectId();
String tmdId = laborDto.getTmdId();
String grade = laborDto.getGrade();
String periodId = laborDto.getPeriodId();
String academicYearId = laborDto.getAcademicYearId();
try {
records = queryService.queryLessonsInParallel(
lessonRecordKey, startTime, endTime, subjectId, tmdId, grade, periodId
);
// 获取当前月份的起始时间和结束时间
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long startOfMonth = calendar.getTimeInMillis();
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.MILLISECOND, -1);
long endOfMonth = calendar.getTimeInMillis();
// 筛选出本月的数据量
int currentRecordsCount = records.stream()
.filter(record -> record.getStartTime() >= startOfMonth && record.getStartTime() <= endOfMonth)
.mapToInt(record -> 1)
.sum();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "数据查询异常");
} finally {
queryService.shutdown();
}
//总评价数
List<ClassInfo> classes = classRepository.findClassBySchoolIdAndPeriodId(laborDto.getPeriodId(), String.format(PK.CLASS, schoolId));
if (classes.isEmpty()) {
throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "暂无班级");
}
List<String> classIds = classes.stream().map(ClassInfo::getId).collect(Collectors.toList());
List<RecordVo> res = appraiseRecordRepository.latestRecords(
String.format(PK.PK_APPRAISE_RECORD, schoolId),
academicYearId,
startTime,
endTime,
classIds
);
//表扬的次数
int rightCount = (int) res.stream().filter(RecordVo::isPraise).count();
//批评的次数
int wrongCount = (int) res.stream().filter(record -> !record.isPraise()).count();
//已经评价的学生人数
long uniqueTargetIdCount = res.stream()
.map(RecordVo::getTargetId)
.filter(Objects::nonNull) // 过滤掉 null 值
.distinct()
.count();
List<String> recordIds = new ArrayList<>();
for (LessonRecord record : records) {
recordIds.add(record.getId());
}
//examIds.add("c26e5766-597f-45b3-91ad-19e76426323b");
//获取所有课程下的课中活动
List<Exam> exams = examRepository.findExamsByIds(String.format(PK.EXAM, laborDto.getTmdId()), recordIds);
Map<String, List<List<String>>> knowledgeMap = new HashMap<>();
Map<String, List<Double>> point = new HashMap<>();
for (Exam exam : exams) {
if (exam.getPapers() != null) {
if (exam.getPapers().get(0).getKnowledge() != null)
{
knowledgeMap.put(exam.getId(), exam.getPapers().get(0).getKnowledge());
point.put(exam.getId(), exam.getPapers().get(0).getPoint());
}
}
}
List<String> examIds = new ArrayList<>();
for (Exam exam : exams) {
examIds.add(exam.getId());
}
List<ExamClassResult> examResults = examClassResultRepository.findAll(String.format(PK.CLASS_RESULT, laborDto.getCode()),examIds);
Map<String, Object> resMap = new HashMap<>();
//处理德育知识块和知识点的关联关系
resMap.put("subjectiveCount", res.size()); //主观评价数
resMap.put("examCount", exams.size());//评测数量
resMap.put("lessonCount", records.size());//班会课数量
resMap.put("rightCount", rightCount);//表扬评价数
resMap.put("wrongCount", wrongCount);//待改进评价数
resMap.put("uniqueTargetIdCount", uniqueTargetIdCount);//评价学生数
Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(schoolId, periodId, PK.PK_APPRAISE);
if (appraise != null) {
appraise = evaluationService.buildTree(appraise);
Map<String, List<String>> knowledgeBlockToPointsMap = getKnowledgeBlockToPointsMap(appraise);
Map<String, Object> classScoreRate = new HashMap<>();
Map<String, Object> gradeScoreRate = new HashMap<>();
if (laborDto.getClassId() != null) {
classScoreRate = calculateKnowledgeScoreRateForClass(laborDto.getClassId(), examResults, knowledgeMap, appraise, point,res,request);
}
if (laborDto.getGrade() != null) {
gradeScoreRate = calculateKnowledgeScoreForGrade(laborDto.getGrade(), examResults, knowledgeMap,appraise, point,res);
}
Map<String,Object> schoolScoreRate = calculateKnowledgeScoreForSchool(examResults, knowledgeMap,appraise, point,res);
resMap.put("gradeScoreRate", gradeScoreRate);
resMap.put("classScoreRate", classScoreRate);
resMap.put("schoolScoreRate", schoolScoreRate);
List<Map<String, Object>> scores = calculateScoresWithDetails(res, appraise);
List<Map<String, Object>> students = combineScoresWithExamResults(scores,examResults,knowledgeMap,point,knowledgeBlockToPointsMap);
resMap.put("scores", students);
}
//处理主观评价内容
return resMap;
}
public List<Map<String, Object>> combineScoresWithExamResults(
List<Map<String, Object>> scores,
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Map<String, List<Double>> points,
Map<String, List<String>> knowledgeBlockToPointsMap) {
// 1. 计算每个学生的知识点得分
Map<String, Map<String, Double>> studentKnowledgePointScores = new HashMap<>();
for (Map<String, Object> studentResult : scores) {
String studentId = (String) studentResult.get("studentId");
// 初始化知识点总分
Map<String, Double> knowledgeTotalScore = new HashMap<>();
// 计算当前学生的知识点得分
calculateStudentScoreRates(studentId, examResults, knowledgeMap, points, knowledgeTotalScore);
// 将知识点得分存储到 studentKnowledgePointScores 中
studentKnowledgePointScores.put(studentId, knowledgeTotalScore);
}
// 2. 将知识点得分汇总到知识块级别
Map<String, Map<String, Double>> studentKnowledgeBlockScores = new HashMap<>();
for (Map.Entry<String, Map<String, Double>> entry : studentKnowledgePointScores.entrySet()) {
String studentId = entry.getKey();
Map<String, Double> pointScores = entry.getValue();
Map<String, Double> blockScores = studentKnowledgeBlockScores.computeIfAbsent(studentId, k -> new HashMap<>());
// 遍历知识块和知识点的映射关系
for (Map.Entry<String, List<String>> blockEntry : knowledgeBlockToPointsMap.entrySet()) {
String knowledgeBlock = blockEntry.getKey();
List<String> knowledgePoints = blockEntry.getValue();
// 计算该知识块的总得分
double totalScore = 0.0;
for (String knowledgePoint : knowledgePoints) {
if (pointScores.containsKey(knowledgePoint)) {
totalScore += pointScores.get(knowledgePoint);
}
}
blockScores.put(knowledgeBlock, totalScore);
}
}
// 3. 更新现有的 scores 返回数据
for (Map<String, Object> studentResult : scores) {
String studentId = (String) studentResult.get("studentId");
Map<String, Integer> subjectiveScores = (Map<String, Integer>) studentResult.get("scores");
// 查找该学生的知识块客观成绩
Map<String, Double> knowledgeBlockScores = studentKnowledgeBlockScores.get(studentId);
// 初始化主观总分和客观总分
double subjectiveTotal = 0.0;
double objectiveTotal = 0.0;
int knowledgeBlockCount = knowledgeBlockScores.size();
// 更新主观成绩,计算主观总分和客观总分
Map<String, Object> combinedScores = new HashMap<>();
for (Map.Entry<String, Integer> entry : subjectiveScores.entrySet()) {
String knowledgeBlock = entry.getKey();
int count = entry.getValue(); // 次数
// 确保 count 不小于0
if (count < 0) {
count = 0; //次数永远不能为负数
}
double maxCount = 50.0; // 最大次数(可根据实际情况调整)
// 将次数转换为 0-100 的分数
double convertedScore = (count / maxCount) * 100;
subjectiveTotal += convertedScore;
// 获取对应知识块的客观成绩
double objectiveScore = knowledgeBlockScores.get(knowledgeBlock);
objectiveTotal += objectiveScore;
/*
/
String combinedScore = count + "/" + objectiveScore;
*/
int finalCount = count;
combinedScores.put(knowledgeBlock, new HashMap<String, Object>() {{
put("count", finalCount);
put("objectiveScore", objectiveScore);
}});
//combinedScores.put(knowledgeBlock, objectiveScore);
}
// 计算客观平均分
double objectiveAverage = (knowledgeBlockCount > 0) ? objectiveTotal : 0.0;
// 计算综合得分(主观占 60%,客观占 40%
double compositeScore = (subjectiveTotal * 0.6) + (objectiveAverage * 0.4);
compositeScore = Double.parseDouble(String.format("%.2f", compositeScore));
// 更新返回数据
studentResult.put("scores", combinedScores);
studentResult.put("subjectiveTotal", subjectiveTotal);
studentResult.put("objectiveTotal", objectiveAverage);
studentResult.put("compositeScore", compositeScore);
}
return scores;
}
public static List<Map<String, Object>> calculateScoresWithDetails(List<RecordVo> res, Appraise appraise) {
// 1. 构建知识点到知识块的映射 (一个知识块对应多个知识点)
Map<String, List<String>> knowledgeBlockToPointsMap = getKnowledgeBlockToPointsMap(appraise);
// 2. 构建所有二级知识块名称列表
Set<String> knowledgeBlocks = new HashSet<>();
for (AppraiseTreeNode node : appraise.getNodes()) {
if ("德育".equals(node.getName())) {
for (AppraiseTreeNode secondLevelNode : node.getChildren()) {
knowledgeBlocks.add(secondLevelNode.getName());
}
}
}
// 3. 构建知识点到知识块的映射 (一个知识点对应一个知识块)
Map<String, String> knowledgePointToBlockMap = new HashMap<>();
for (Map.Entry<String, List<String>> entry : knowledgeBlockToPointsMap.entrySet()) {
String knowledgeBlock = entry.getKey();
for (String knowledgePoint : entry.getValue()) {
knowledgePointToBlockMap.put(knowledgePoint, knowledgeBlock);
}
}
// 4. 遍历评价记录并统计得分
Map<String, Map<String, Integer>> studentScores = new HashMap<>();
for (RecordVo record : res) {
String studentId = record.getTargetId(); // 学生 ID
String studentName = record.getTargetName(); // 学生名称
String className = record.getClassName(); // 班级名称
String appraiseName = record.getAppraiseName(); // 评价内容
boolean isPraise = record.isPraise(); // 是否为优点
// 获取评价内容对应的第二级节点(知识块)
String knowledgeBlock = knowledgePointToBlockMap.get(appraiseName);
if (knowledgeBlock == null) {
continue; // 如果不在德育知识块中,跳过
}
// 初始化学生得分记录
String studentKey = studentId + "|" + studentName + "|" + className; // 组合学生唯一标识
studentScores.putIfAbsent(studentKey, new HashMap<>());
Map<String, Integer> studentScoreMap = studentScores.get(studentKey);
// 计分
// 计分
int score = isPraise ? 1 : -1;
int currentScore = studentScoreMap.getOrDefault(knowledgeBlock, 0) + score;
studentScoreMap.put(knowledgeBlock, Math.max(currentScore, 0));
}
// 5. 初始化缺失的二级知识块分数
for (Map.Entry<String, Map<String, Integer>> entry : studentScores.entrySet()) {
Map<String, Integer> studentScoreMap = entry.getValue();
for (String block : knowledgeBlocks) {
studentScoreMap.putIfAbsent(block, 0);
}
}
// 6. 整理并返回结果
return getMaps(studentScores);
}
// 整理并返回结果
private static List<Map<String, Object>> getMaps(Map<String, Map<String, Integer>> studentScores) {
List<Map<String, Object>> result = new ArrayList<>();
for (Map.Entry<String, Map<String, Integer>> entry : studentScores.entrySet()) {
String studentKey = entry.getKey();
Map<String, Integer> scoreMap = entry.getValue();
// 解析学生唯一标识
String[] studentInfo = studentKey.split("\\|");
String studentId = studentInfo[0];
String studentName = studentInfo[1];
String className = studentInfo[2];
// 构建返回结果
Map<String, Object> studentResult = new HashMap<>();
studentResult.put("studentId", studentId);
studentResult.put("studentName", studentName);
studentResult.put("className", className);
studentResult.put("scores", scoreMap);
result.add(studentResult);
}
return result;
}
// 构建知识块到知识点的映射 (一个知识块对应多个知识点)
private static Map<String, List<String>> getKnowledgeBlockToPointsMap(Appraise appraise) {
Map<String, List<String>> knowledgeBlockToPointsMap = new HashMap<>();
for (AppraiseTreeNode node : appraise.getNodes()) {
if ("德育".equals(node.getName())) {
for (AppraiseTreeNode secondLevelNode : node.getChildren()) {
String knowledgeBlock = secondLevelNode.getName();
List<String> knowledgePoints = new ArrayList<>();
// 假设知识点的信息存储在 secondLevelNode.getChildren() 中
for (AppraiseTreeNode pointNode : secondLevelNode.getChildren()) {
knowledgePoints.add(pointNode.getName());
}
knowledgeBlockToPointsMap.put(knowledgeBlock, knowledgePoints);
}
}
}
return knowledgeBlockToPointsMap;
}
// 知识点得分率结果类
@Data
@AllArgsConstructor
public static class KnowledgeScoreRate {
public String knowledge; // 知识点
public double scoreRate; // 得分率
}
/**
*
*
* @param studentId ID
* @param examResults
* @param knowledgeMap ID ->
* @param appraise
* @return
*/
public static Map<String, Double> calculateKnowledgeScoreRateForStudent(
String studentId, List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap, Appraise appraise, Map<String, List<Double>> points) {
Map<String, Double> knowledgeTotalScore = new HashMap<>();
// 计算学生的知识点得分率
List<KnowledgeScoreRate> studentScoreRates = calculateStudentScoreRates(studentId, examResults, knowledgeMap, points, knowledgeTotalScore);
// 查找知识点对应的节点及其父节点
Map<String, Double> parentNodeScoreRates = new HashMap<>();
for (KnowledgeScoreRate scoreRate : studentScoreRates) {
AppraiseTreeNode node = findKnowledgeNode(appraise.getNodes(), scoreRate.getKnowledge());
if (node != null) {
AppraiseTreeNode parentNode = findParentNode(appraise.getNodes(), node.getId());
if (parentNode != null) {
double parentNodeScoreRate = calculateNodeScoreRate(parentNode, knowledgeTotalScore);
parentNodeScoreRates.put(parentNode.getName(), parentNodeScoreRate);
}else {
// 如果没有找到父节点初始化一个默认的得分率为0的父节点得分率
parentNodeScoreRates.put(node.getName(), 0.0);
}
}
}
// 初始化所有同层的父节点
for (AppraiseTreeNode node : appraise.getNodes()) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
if (!parentNodeScoreRates.containsKey(child.getName())) {
parentNodeScoreRates.put(child.getName(), 0.0);
}
}
}
}
return parentNodeScoreRates;
}
/**
*
*
* @param classId ID
* @param examResults
* @param knowledgeMap ID ->
* @param appraise
* @return ID ->
*/
public static Map<String, Object> calculateKnowledgeScoreRateForClass(
String classId,
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Appraise appraise,
Map<String, List<Double>> points,
List<RecordVo> res
, HttpServletRequest request) { // 新增主观评价记录
// 将 classId 转换为 List<String>
List<String> classIds = Collections.singletonList(classId);
GroupDto groupDto = new GroupDto();
groupDto.setIds(classIds);
groupDto.setSchoolId(appraise.getSchoolId());
String url = environment.getProperty("ies.server-url-group");
Map<String, Object> groupId = GroupUtil.getGroupId(groupDto,new GroupUtil(environment), request,url);
List<RMember> rMembers = new ArrayList<>();
for (Map.Entry<String, Object> entry : groupId.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (key.equals("members")) {
String jsonGroups = JSON.toJSONString(value);
rMembers = JSON.parseObject(jsonGroups, new TypeReference<List<RMember>>() {});
}
}
// 1. 获取班级所有学生的客观分数
Map<String, Map<String, Double>> studentScoreRates = new HashMap<>();
Map<String, Double> classScoreRates = new HashMap<>();
// 遍历班级中的每个学生
for (ExamClassResult examResult : examResults) {
if (!examResult.getInfo().getId().equals(classId)) continue; // 过滤出该班级的考试
List<Integer> statuses = examResult.getStatus();
for (int i = 0; i < examResult.getStudentIds().size(); i++) {
String studentId = examResult.getStudentIds().get(i);
String name = rMembers.stream()
.filter(member -> member.getId().equals(studentId))
.findFirst()
.map(RMember::getName)
.orElse("未知");
int status = statuses.get(i);
if (status == 1) continue; // 跳过 status 为 1 的记录
// 计算父节点得分率(客观分数)
Map<String, Double> scoreRates = calculateKnowledgeScoreRateForStudent(
studentId, examResults, knowledgeMap, appraise, points
);
studentScoreRates.put(name, scoreRates);
// 累加班级整体得分率
for (Map.Entry<String, Double> entry : scoreRates.entrySet()) {
classScoreRates.put(entry.getKey(), classScoreRates.getOrDefault(entry.getKey(), 0.0) + entry.getValue());
}
}
}
// 2. 获取班级所有学生的主观分数(次数)
List<Map<String, Object>> subjectiveScoresList = calculateScoresWithDetails(res, appraise);
// 3. 将主观次数转换为0-100分数
Map<String, Map<String, Double>> subjectiveScores = new HashMap<>();
for (Map<String, Object> studentScore : subjectiveScoresList) {
String studentId = (String) studentScore.get("studentId");
// 修改这里:假设 scores 是 Map<String, Integer>
@SuppressWarnings("unchecked")
Map<String, Integer> scores = (Map<String, Integer>) studentScore.get("scores");
Map<String, Double> convertedScores = new HashMap<>();
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String block = entry.getKey();
int count = entry.getValue();
double maxCount = 50.0;
double convertedScore = (count / maxCount) * 100;
convertedScores.put(block, convertedScore);
}
subjectiveScores.put(studentId, convertedScores);
}
// 4. 融合主观和客观分数
Map<String, Map<String, Double>> compositeStudentScores = new HashMap<>();
for (Map.Entry<String, Map<String, Double>> entry : studentScoreRates.entrySet()) {
String studentId = entry.getKey();
Map<String, Double> objective = entry.getValue();
Map<String, Double> subjective = subjectiveScores.getOrDefault(studentId, new HashMap<>());
// 计算综合得分
Map<String, Double> compositeScores = new HashMap<>();
for (Map.Entry<String, Double> objectiveEntry : objective.entrySet()) {
String block = objectiveEntry.getKey();
double objectiveScore = objectiveEntry.getValue();
double subjectiveScore = subjective.getOrDefault(block, 0.0);
// 按6:4比例计算综合得分
double compositeScore = (subjectiveScore * 0.6) + (objectiveScore * 0.4);
compositeScore = Double.parseDouble(String.format("%.2f", compositeScore));
compositeScores.put(block, compositeScore);
}
compositeStudentScores.put(studentId, compositeScores);
}
// 5. 计算班级整体得分率
int studentCount = compositeStudentScores.size();
Map<String, Double> compositeClassScoreRates = new HashMap<>();
for (Map<String, Double> studentScores : compositeStudentScores.values()) {
for (Map.Entry<String, Double> entry : studentScores.entrySet()) {
String block = entry.getKey();
double score = entry.getValue();
compositeClassScoreRates.put(block, compositeClassScoreRates.getOrDefault(block, 0.0) + score);
}
}
compositeClassScoreRates.replaceAll((k, v) -> Double.parseDouble(String.format("%.2f", v / studentCount))); // 保留小数点后两位
// 6. 初始化所有同层的父节点
for (AppraiseTreeNode node : appraise.getNodes()) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
if (!compositeClassScoreRates.containsKey(child.getName())) {
compositeClassScoreRates.put(child.getName(), 0.0);
}
}
}
}
// 7. 返回结果
Map<String, Object> result = new HashMap<>();
result.put("classScoreRates", compositeClassScoreRates); // 班级整体得分率
result.put("studentScoreRates", compositeStudentScores); // 每个学生的综合得分率
return result;
}
private static List<KnowledgeScoreRate> calculateStudentScoreRates(
String studentId,
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Map<String, List<Double>> points,
Map<String, Double> knowledgeTotalScore) {
// 记录每个知识点的考试次数
Map<String, Integer> knowledgeExamCount = new HashMap<>();
// 过滤出当前学生的考试结果
for (ExamClassResult examResult : examResults) {
int studentIndex = examResult.getStudentIds().indexOf(studentId);
if (studentIndex == -1) continue;
int status = examResult.getStatus().get(studentIndex);
if (status == 1) continue;
// 获取该考试的知识点
List<List<String>> knowledgeList = knowledgeMap.get(examResult.getExamId());
List<Double> point = points.get(examResult.getExamId());
if (knowledgeList == null) continue;
// 检查 studentScores 是否为空或越界
List<List<Double>> studentScores = examResult.getStudentScores();
if (studentIndex >= studentScores.size() || studentScores.get(studentIndex) == null) {
continue;
}
// 获取当前学生的成绩列表
List<Double> studentScoreList = studentScores.get(studentIndex);
// 确保知识列表、分数列表和学生成绩列表的大小一致
if (knowledgeList.size() != point.size() || knowledgeList.size() != studentScoreList.size()) {
continue;
}
// 遍历每道题的得分和知识点
for (int i = 0; i < studentScoreList.size(); i++) {
String knowledge = knowledgeList.get(i).get(0); // 每题的第一个知识点
double studentScore = studentScoreList.get(i); // 学生的得分
// 累加知识点总分
knowledgeTotalScore.put(knowledge, knowledgeTotalScore.getOrDefault(knowledge, 0.0) + studentScore);
// 记录该知识点的考试次数
knowledgeExamCount.put(knowledge, knowledgeExamCount.getOrDefault(knowledge, 0) + 1);
}
}
// 计算每个知识点的平均分
Map<String, Double> knowledgeAverageScore = new HashMap<>();
for (Map.Entry<String, Double> entry : knowledgeTotalScore.entrySet()) {
String knowledge = entry.getKey();
double totalScore = entry.getValue();
int examCount = knowledgeExamCount.getOrDefault(knowledge, 1); // 避免除零
double averageScore = totalScore / examCount;
knowledgeAverageScore.put(knowledge, averageScore);
}
// 返回知识点平均分
return knowledgeAverageScore.entrySet().stream()
.map(entry -> new KnowledgeScoreRate(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
public static double calculateNodeScoreRate(AppraiseTreeNode node, Map<String, Double> knowledgeTotalScore) {
// 获取当前节点的得分
double totalScore = knowledgeTotalScore.getOrDefault(node.getName(), 0.0);
// 递归计算子节点的得分并累加
for (AppraiseTreeNode child : node.getChildren()) {
totalScore += calculateNodeScoreRate(child, knowledgeTotalScore);
}
return totalScore;
}
/**
*
*
* @param gradeId ID
* @param examResults
* @param knowledgeMap ID ->
* @param appraise
* @param points
* @return
*/
public static Map<String, Object> calculateKnowledgeScoreForGrade(
String gradeId,
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Appraise appraise,
Map<String, List<Double>> points,
List<RecordVo> res) { // 新增主观评价记录
// 1. 获取年级所有班级的客观分数
Map<String, Map<String, Double>> classScores = new HashMap<>();
Map<String, Double> gradeScores = new HashMap<>();
// 过滤出该年级的考试结果
List<ExamClassResult> gradeExamResults = examResults.stream()
.filter(examResult -> examResult.getGradeId().equals(gradeId))
.collect(Collectors.toList());
// 2. 获取年级所有学生的主观分数(次数)
List<Map<String, Object>> subjectiveScoresList = calculateScoresWithDetails(res, appraise);
// 3. 将主观次数转换为0-100分数
Map<String, Map<String, Double>> subjectiveScores = new HashMap<>();
for (Map<String, Object> studentScore : subjectiveScoresList) {
String studentId = (String) studentScore.get("studentId");
// 修改这里:假设 scores 是 Map<String, Integer>
@SuppressWarnings("unchecked")
Map<String, Integer> scores = (Map<String, Integer>) studentScore.get("scores");
Map<String, Double> convertedScores = new HashMap<>();
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String block = entry.getKey();
int count = entry.getValue();
double maxCount = 50.0;
double convertedScore = (count / maxCount) * 100;
convertedScores.put(block, convertedScore);
}
subjectiveScores.put(studentId, convertedScores);
}
// 4. 遍历年级下的所有班级
for (ExamClassResult examResult : gradeExamResults) {
String classId = examResult.getInfo().getId();
String className = examResult.getInfo().getName();
Map<String, Double> classScoreSum = new HashMap<>();
Map<String, Integer> classScoreCount = new HashMap<>();
Map<String, Double> classScoresInner = new HashMap<>();
// 获取学生状态列表
List<Integer> statuses = examResult.getStatus();
// 遍历班级中的每个学生
for (int i = 0; i < examResult.getStudentIds().size(); i++) {
String studentId = examResult.getStudentIds().get(i);
if (statuses.get(i) == 1) {
continue;
}
// 计算学生的知识点得分(客观分数)
Map<String, Double> studentObjectiveScores = calculateKnowledgeScoreRateForStudent(
studentId, examResults, knowledgeMap, appraise, points
);
// 获取学生的主观分数
Map<String, Double> studentSubjectiveScores = subjectiveScores.getOrDefault(studentId, new HashMap<>());
// 计算综合得分主观60%客观40%
Map<String, Double> studentCompositeScores = new HashMap<>();
for (Map.Entry<String, Double> entry : studentObjectiveScores.entrySet()) {
String block = entry.getKey();
double objectiveScore = entry.getValue();
double subjectiveScore = studentSubjectiveScores.getOrDefault(block, 0.0);
double compositeScore = (subjectiveScore * 0.6) + (objectiveScore * 0.4);
studentCompositeScores.put(block, compositeScore);
}
// 累加班级整体得分
for (Map.Entry<String, Double> entry : studentCompositeScores.entrySet()) {
String nodeName = entry.getKey();
double score = entry.getValue();
classScoreSum.put(nodeName, classScoreSum.getOrDefault(nodeName, 0.0) + score);
classScoreCount.put(nodeName, classScoreCount.getOrDefault(nodeName, 0) + 1);
}
}
// 计算班级平均得分
for (Map.Entry<String, Double> entry : classScoreSum.entrySet()) {
String nodeName = entry.getKey();
double totalScore = entry.getValue();
int count = classScoreCount.get(nodeName);
classScoresInner.put(nodeName, Double.parseDouble(String.format("%.2f", totalScore / count))); // 保留小数点后两位
}
// 初始化所有同层的父节点
for (AppraiseTreeNode node : appraise.getNodes()) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
if (!classScoresInner.containsKey(child.getName())) {
classScoresInner.put(child.getName(), 0.0);
}
}
}
}
// 存储班级的平均得分
classScores.put(className, classScoresInner);
}
// 5. 计算年级的平均得分
Map<String, Double> gradeScoreSum = new HashMap<>();
Map<String, Integer> gradeScoreCount = new HashMap<>();
for (Map<String, Double> classScore : classScores.values()) {
for (Map.Entry<String, Double> entry : classScore.entrySet()) {
String nodeName = entry.getKey();
double score = entry.getValue();
gradeScoreSum.put(nodeName, gradeScoreSum.getOrDefault(nodeName, 0.0) + score);
gradeScoreCount.put(nodeName, gradeScoreCount.getOrDefault(nodeName, 0) + 1);
}
}
for (Map.Entry<String, Double> entry : gradeScoreSum.entrySet()) {
String nodeName = entry.getKey();
double totalScore = entry.getValue();
int count = gradeScoreCount.get(nodeName);
gradeScores.put(nodeName, Double.parseDouble(String.format("%.2f", totalScore / count))); // 保留小数点后两位
}
// 初始化所有同层的父节点
for (AppraiseTreeNode node : appraise.getNodes()) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
if (!gradeScores.containsKey(child.getName())) {
gradeScores.put(child.getName(), 0.0);
}
}
}
}
// 6. 返回结果
Map<String, Object> result = new HashMap<>();
result.put("gradeScores", gradeScores); // 年级平均得分
result.put("classScores", classScores); // 每个班级的平均得分
return result;
}
public static Map<String, Object> calculateKnowledgeScoreForSchool(
List<ExamClassResult> examResults,
Map<String, List<List<String>>> knowledgeMap,
Appraise appraise,
Map<String, List<Double>> points,
List<RecordVo> res) { // 新增主观评价记录
// 1. 获取全校所有年级的客观分数
Map<String, Map<String, Double>> gradeScores = new HashMap<>();
Map<String, Double> schoolScores = new HashMap<>();
// 获取所有年级的 ID
Set<String> gradeIds = examResults.stream()
.map(ExamClassResult::getGradeId)
.collect(Collectors.toSet());
// 2. 获取全校所有学生的主观分数(次数)
List<Map<String, Object>> subjectiveScoresList = calculateScoresWithDetails(res, appraise);
// 3. 将主观次数转换为0-100分数
Map<String, Map<String, Double>> subjectiveScores = new HashMap<>();
for (Map<String, Object> studentScore : subjectiveScoresList) {
String studentId = (String) studentScore.get("studentId");
// 修改这里:假设 scores 是 Map<String, Integer>
@SuppressWarnings("unchecked")
Map<String, Integer> scores = (Map<String, Integer>) studentScore.get("scores");
Map<String, Double> convertedScores = new HashMap<>();
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
String block = entry.getKey();
int count = entry.getValue();
double maxCount = 50.0;
double convertedScore = (count / maxCount) * 100;
convertedScores.put(block, convertedScore);
}
subjectiveScores.put(studentId, convertedScores);
}
// 4. 遍历所有年级
for (String gradeId : gradeIds) {
// 计算该年级的平均得分(包含主观和客观分数)
Map<String, Object> gradeResult = calculateKnowledgeScoreForGrade(
gradeId, examResults, knowledgeMap, appraise, points, res
);
// 获取该年级的平均得分
Map<String, Double> gradeScoresInner = (Map<String, Double>) gradeResult.get("gradeScores");
gradeScores.put(gradeId, gradeScoresInner);
// 累加全校的得分总和
Map<String, Map<String, Double>> classScores = (Map<String, Map<String, Double>>) gradeResult.get("classScores");
for (Map<String, Double> classScoresInner : classScores.values()) {
for (Map.Entry<String, Double> entry : classScoresInner.entrySet()) {
String nodeName = entry.getKey();
double score = entry.getValue();
schoolScores.put(nodeName, schoolScores.getOrDefault(nodeName, 0.0) + score);
}
}
}
// 5. 计算全校的平均得分
if (gradeIds.size() == 1) {
schoolScores.putAll(gradeScores.get(gradeIds.iterator().next()));
} else {
// 计算全校的平均得分
for (Map.Entry<String, Double> entry : schoolScores.entrySet()) {
String nodeName = entry.getKey();
double totalScore = entry.getValue();
int totalCount = gradeIds.size(); // 每个年级视为一个单位
schoolScores.put(nodeName, Double.parseDouble(String.format("%.2f", totalScore / totalCount))); // 保留小数点后两位
}
}
// 6. 初始化所有同层的父节点
for (AppraiseTreeNode node : appraise.getNodes()) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
if (!schoolScores.containsKey(child.getName())) {
schoolScores.put(child.getName(), 0.0);
}
}
}
}
// 7. 返回结果
Map<String, Object> result = new HashMap<>();
result.put("schoolScores", schoolScores); // 全校平均得分
result.put("gradeScores", gradeScores); // 每个年级的平均得分
return result;
}
/**
*
*
* @param nodes
* @param knowledge
* @return
*/
private static AppraiseTreeNode findKnowledgeNode(List<AppraiseTreeNode> nodes, String knowledge) {
for (AppraiseTreeNode node : nodes) {
if (node.getName().equals("德育")) {
for (AppraiseTreeNode child : node.getChildren()) {
for (AppraiseTreeNode grandchild : child.getChildren()) {
if (grandchild.getName().equals(knowledge)) {
return grandchild; // 返回知识点对应的节点
}
}
}
}
}
return null; // 未找到匹配的节点
}
private static AppraiseTreeNode findParentNode(List<AppraiseTreeNode> nodes, String nodeId) {
for (AppraiseTreeNode node : nodes) {
for (AppraiseTreeNode child : node.getChildren()) {
for (AppraiseTreeNode grandchild : child.getChildren()) {
if (grandchild.getId().equals(nodeId)) {
return child; // 返回父节点
}
}
}
}
return null; // 未找到父节点
}
}

@ -0,0 +1,26 @@
package cn.teammodel.model.dto.admin.labor;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class LaborDto {
@ApiModelProperty("学校编码")
public String code;
@ApiModelProperty("TmdId")
public String tmdId;
public Long startTime;
public Long endTime;
@ApiModelProperty("学段Id")
public String periodId;
public Integer year;
@ApiModelProperty("年级")
public String grade;
public String gradeName;
@ApiModelProperty("科目信息")
public String subjectId;
public String semesterId;
private String academicYearId;
private String classId;
}

@ -31,6 +31,7 @@ public class AppraiseTreeNode {
*/ */
@JsonProperty("isPraise") @JsonProperty("isPraise")
boolean isPraise; boolean isPraise;
double scoreRate;
List<AppraiseTreeNode> children = new ArrayList<>(); List<AppraiseTreeNode> children = new ArrayList<>();
} }

@ -19,6 +19,7 @@ public class Exam extends BaseItem {
public String school; public String school;
public String creatorId; public String creatorId;
public int stuCount; public int stuCount;
public String lessonRecordId;
/* //实际考试人数 /* //实际考试人数
public int realCount; public int realCount;
//平均分 //平均分

@ -0,0 +1,48 @@
package cn.teammodel.model.entity.common;
import cn.teammodel.model.entity.BaseItem;
import com.azure.spring.data.cosmos.core.mapping.Container;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Container(containerName = "Common")
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ExamClassResult extends BaseItem {
public String school;
public String examId ;
public String subjectId ;
public String gradeId ;
public int year ;
public ClassInfo info ;
public boolean progress;
public List<String> studentIds ;
public List<List<String>> studentAnswers ;
//记录学生客观题选项内容,便于学情分析
public List<List<List<String>>> ans ;
public List<List<Double>> studentScores ;
//记录学生作答状态
//0 已作答 1缺考 2补考 3补考完成
public List<Integer> status ;
public String scope ;
public List<Double> sum ;
public double average ;
//单科单班得分率
public double srate ;
//单科单班标准差
public double standard ;
@Data
public static class ClassInfo {
public String id;
public String name;
public String periodId ;
public int year ;
public String no;
}
}

@ -13,6 +13,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -71,13 +72,14 @@ public interface AppraiseRecordRepository extends CosmosRepository<AppraiseRecor
/** /**
* , * ,
*/ */
@Query("select c.id as recordId, c.name as targetName, c.avatar, c.className, n.id as recordNodeId, c.targetId, c.targetType, n.creator, n.createTime, n.appraiseNode.name as appraiseName, n.appraiseNode.isPraise, n.device from Student as c join n in c.nodes where " + @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 from Student as c join n in c.nodes where " +
"c.code = @code and " + "c.code = @code and " +
"c.academicYearId = @academicYearId and " + "c.academicYearId = @academicYearId and " +
"(IS_NULL(@startTime) or n.createTime >= @startTime) and " + "(IS_NULL(@startTime) or n.createTime >= @startTime) and " +
"(IS_NULL(@endTime) or n.createTime <= @endTime)" "(IS_NULL(@endTime) or n.createTime <= @endTime) and " +
"c.classId in (@ids)"
) )
List<RecordVo> latestRecords(String code, String academicYearId, Long startTime, Long endTime); List<RecordVo> latestRecords(String code, String academicYearId, Long startTime, Long endTime, Collection<String> ids);
@Query("select c.classId as id, count(1) as count from Student as c join n in c.nodes where( " + @Query("select c.classId as id, count(1) as count from Student as c join n in c.nodes where( " +

@ -0,0 +1,16 @@
package cn.teammodel.repository;
import cn.teammodel.model.entity.common.ExamClassResult;
import com.azure.spring.data.cosmos.repository.CosmosRepository;
import com.azure.spring.data.cosmos.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface ExamClassResultRepository extends CosmosRepository<ExamClassResult, String> {
@Query("select * from ExamClassResult as s where s.code = @code and s.examId in (@ids) ")
List<ExamClassResult> findAll(String code, Collection<String> ids);
@Query("select * from ExamClassResult as s where s.code = @code and s.info.id = @id and s.examId = @examId")
List<ExamClassResult> findById(String code, String id,String examId);
}

@ -6,6 +6,7 @@ import com.azure.spring.data.cosmos.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List; import java.util.List;
@Repository @Repository
@ -15,4 +16,6 @@ public interface ExamRepository extends CosmosRepository<Exam, String> {
List<Exam> findExamByClassId(@Param("code")String code, @Param("classId")String classId, @Param("periodId")String periodId); List<Exam> findExamByClassId(@Param("code")String code, @Param("classId")String classId, @Param("periodId")String periodId);
@Query("select * from Exam as s where s.code = @code and s.id = @id") @Query("select * from Exam as s where s.code = @code and s.id = @id")
List<Exam> findExamById(@Param("code")String code, @Param("id")String id); List<Exam> findExamById(@Param("code")String code, @Param("id")String id);
@Query("select s.id,s.papers from Exam as s where s.code = @code and s.lessonRecordId in (@ids)")
List<Exam> findExamsByIds(String code, Collection<String> ids);
} }

Loading…
Cancel
Save