You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
598 lines
27 KiB
598 lines
27 KiB
package cn.teammodel.service.impl;
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
import cn.hutool.core.lang.UUID;
|
|
import cn.teammodel.common.ErrorCode;
|
|
import cn.teammodel.common.FiveEducations;
|
|
import cn.teammodel.common.IdRequest;
|
|
import cn.teammodel.common.PK;
|
|
import cn.teammodel.config.exception.ServiceException;
|
|
import cn.teammodel.dao.*;
|
|
import cn.teammodel.model.dto.Appraise.*;
|
|
import cn.teammodel.model.entity.User;
|
|
import cn.teammodel.model.entity.appraise.*;
|
|
import cn.teammodel.model.entity.school.ClassInfo;
|
|
import cn.teammodel.model.entity.school.School;
|
|
import cn.teammodel.model.entity.school.Student;
|
|
import cn.teammodel.model.vo.appraise.AppraiseRecordVo;
|
|
import cn.teammodel.model.vo.appraise.StudentReportVo;
|
|
import cn.teammodel.security.utils.SecurityUtil;
|
|
import cn.teammodel.service.EvaluationService;
|
|
import cn.teammodel.utils.RepositoryUtil;
|
|
import cn.teammodel.utils.SchoolDateUtil;
|
|
import com.azure.cosmos.models.CosmosPatchOperations;
|
|
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
|
|
import org.apache.commons.lang3.ObjectUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.data.domain.Page;
|
|
import org.springframework.data.domain.Sort;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import javax.annotation.Resource;
|
|
import java.time.Instant;
|
|
import java.time.LocalDate;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* @author winter
|
|
* @create 2023-11-20 17:47
|
|
*/
|
|
@Service
|
|
public class EvaluationServiceImpl implements EvaluationService {
|
|
/**
|
|
* 评价记录对象的类型
|
|
*/
|
|
private final static String TARGET_STUDENT = "student";
|
|
private final static String TARGET_CLASS = "class";
|
|
@Resource
|
|
private ClassRepository classRepository;
|
|
@Resource
|
|
private StudentRepository studentRepository;
|
|
@Resource
|
|
private SchoolRepository schoolRepository;
|
|
@Resource
|
|
private AppraiseRecordRepository appraiseRecordRepository;
|
|
@Resource
|
|
private AppraiseRepository appraiseRepository;
|
|
|
|
/**
|
|
* 通用的获取用户学校和学段对应的 appraise 的方法: 判断参数,判断数据是否为空 <br/>
|
|
* 从 token 中获取 schoolId
|
|
*/
|
|
private Appraise findAppraise(String periodId) {
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
String schoolId = loginUser.getSchoolId();
|
|
|
|
// 拿到要新增节点的原始数据
|
|
Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(schoolId, periodId, PK.PK_APPRAISE);
|
|
if (appraise == null) {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "该学段下评价项数据不存在");
|
|
}
|
|
return appraise;
|
|
}
|
|
|
|
@Override
|
|
public Appraise getTree(GetEvaluateTreeDto getEvaluateTreeDto) {
|
|
String periodId = getEvaluateTreeDto.getPeriodId();
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
String schoolId = loginUser.getSchoolId();
|
|
Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(schoolId, periodId, PK.PK_APPRAISE);
|
|
|
|
// 如果传参 period 所在没有 tree 的话,则复制一份模板 tree 给他 (处理节点 id)
|
|
if (appraise == null) {
|
|
appraise = RepositoryUtil.findOne(appraiseRepository.findTemplateTree(), "获取模板评价树失败");
|
|
if (appraise == null) {
|
|
throw new ServiceException();
|
|
}
|
|
// refresh
|
|
refreshAppraiseTree(appraise);
|
|
appraise.setPeriodId(periodId);
|
|
appraise.setSchoolId(schoolId);
|
|
appraise.setId(null);
|
|
appraise = appraiseRepository.save(appraise);
|
|
}
|
|
|
|
return this.buildTree(appraise);
|
|
}
|
|
|
|
|
|
/**
|
|
* 将 appraise 进行替换(天才)
|
|
*/
|
|
private void refreshAppraiseTree(Appraise appraise) {
|
|
List<AppraiseTreeNode> nodes = appraise.getNodes();
|
|
List<AchievementRule> rules = appraise.getAchievementRules();
|
|
List<AppraiseTreeNode> children = nodes.stream().filter(item -> item.getPid() != null).collect(Collectors.toList());
|
|
// 将非 root 的 nodes 通过 pid 收集成 list, 再遍历 nodes, 将其每一个 id 对应 map 中的 list 的 pid修改成新的 id 即可
|
|
Map<String, List<AppraiseTreeNode>> pidNodeMap = children.stream().collect(Collectors.groupingBy(AppraiseTreeNode::getPid));
|
|
nodes.forEach(item -> {
|
|
String newId = UUID.randomUUID().toString();
|
|
String oldId = item.getId();
|
|
List<AppraiseTreeNode> appraiseTreeNodes = pidNodeMap.get(oldId);
|
|
if (appraiseTreeNodes != null) {
|
|
appraiseTreeNodes.forEach(node -> node.setPid(newId));
|
|
}
|
|
// 处理每个节点属性
|
|
item.setId(newId);
|
|
item.setCreator("template");
|
|
});
|
|
|
|
// refresh rules
|
|
rules.forEach(item -> item.setId(UUID.randomUUID().toString()));
|
|
}
|
|
|
|
@Override
|
|
public Appraise buildTree(Appraise appraise) {
|
|
if (appraise == null) {
|
|
return null;
|
|
}
|
|
|
|
List<AppraiseTreeNode> nodes = appraise.getNodes();
|
|
List<AppraiseTreeNode> parents = new ArrayList<>();
|
|
|
|
// pid 为 null 或者 "" 则为 parents
|
|
for (AppraiseTreeNode node : nodes) {
|
|
if (StringUtils.isBlank(node.getPid())) {
|
|
parents.add(node);
|
|
}
|
|
}
|
|
// 迭代构建孩子节点
|
|
for (AppraiseTreeNode parent : parents) {
|
|
buildChildren(parent, nodes);
|
|
}
|
|
// 排序
|
|
parents = parents.stream().sorted(Comparator.comparing(AppraiseTreeNode::getOrder)).collect(Collectors.toList());
|
|
appraise.setNodes(parents);
|
|
return appraise;
|
|
}
|
|
|
|
@Override
|
|
public void flattenTree(List<AppraiseTreeNode> trees, List<AppraiseTreeNode> nodes) {
|
|
// 递归结束条件
|
|
if (ObjectUtils.isEmpty(trees)) return;
|
|
|
|
// 这里的每个 tree 都是组装好的树,区别于单个节点
|
|
for (AppraiseTreeNode tree : trees) {
|
|
// 递归
|
|
flattenTree(tree.getChildren(), nodes);
|
|
// 回溯时删除 children,添加进列表
|
|
tree.setChildren(null);
|
|
nodes.add(tree);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Appraise insertNode(InsertNodeDto insertNodeDto) {
|
|
Appraise appraise = findAppraise(insertNodeDto.getPeriodId());
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
|
|
List<AppraiseTreeNode> originNodes = appraise.getNodes();
|
|
// 拷贝数据到新节点
|
|
AppraiseTreeNode newNode = new AppraiseTreeNode();
|
|
BeanUtils.copyProperties(insertNodeDto, newNode);
|
|
|
|
String newNodePid = newNode.getPid();
|
|
// 根节点直接添加
|
|
if (StringUtils.isNotEmpty(newNodePid)) {
|
|
boolean invalid = originNodes.stream().noneMatch(item -> newNodePid.equals(item.getId()));
|
|
if (invalid) {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "父节点不存在");
|
|
}
|
|
}
|
|
newNode.setId(UUID.randomUUID().toString());
|
|
newNode.setCreatorId(loginUser.getId());
|
|
newNode.setCreator(loginUser.getName());
|
|
newNode.setCreateTime(Instant.now().toEpochMilli());
|
|
|
|
originNodes.add(newNode);
|
|
return buildTree(appraiseRepository.save(appraise));
|
|
}
|
|
|
|
@Override
|
|
public Appraise updateNode(UpdateNodeDto updateNodeDto) {
|
|
String updateNodeId = updateNodeDto.getId();
|
|
|
|
Appraise appraise = findAppraise(updateNodeDto.getPeriodId());
|
|
List<AppraiseTreeNode> originNodes = appraise.getNodes();
|
|
// 每个节点都有 id, 直接校验是否合法
|
|
AppraiseTreeNode updateNode = originNodes.stream()
|
|
.filter(item -> updateNodeId.equals(item.getId()))
|
|
.findFirst()
|
|
.orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "更新节点不存在"));
|
|
// 更新字段,考虑直接 copy properties
|
|
updateNode.setName(updateNodeDto.getName());
|
|
updateNode.setPath(updateNodeDto.getPath());
|
|
updateNode.setLogo(updateNodeDto.getLogo());
|
|
updateNode.setOrder(updateNodeDto.getOrder());
|
|
updateNode.setPraise(updateNodeDto.isPraise());
|
|
// todo: 为新节点赋值必须参数 (id, creator), 可不可以添加默认值 order ?
|
|
|
|
return buildTree(appraiseRepository.save(appraise));
|
|
}
|
|
|
|
@Override
|
|
public Appraise deleteNode(DeleteNodeDto deleteNodeDto) {
|
|
// 删除指定节点,可能是(一级,二级,三级),设计一个通用的
|
|
Appraise appraise = findAppraise(deleteNodeDto.getPeriodId());
|
|
List<AppraiseTreeNode> nodes = appraise.getNodes();
|
|
List<AppraiseTreeNode> nodesToDelete = new ArrayList<>();
|
|
|
|
// 迭代器安全删除
|
|
for (AppraiseTreeNode node : nodes) {
|
|
if (node.getId().equals(deleteNodeDto.getId())) {
|
|
// 删除当前节点
|
|
nodesToDelete.add(node);
|
|
// 递归删除其孩子节点
|
|
this.collectNodesToDelete(node.getId(), nodes, nodesToDelete);
|
|
}
|
|
}
|
|
if (ObjectUtils.isEmpty(nodesToDelete)) {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "删除节点不存在");
|
|
}
|
|
nodes.removeAll(nodesToDelete);
|
|
|
|
return buildTree(appraiseRepository.save(appraise));
|
|
}
|
|
|
|
@Override
|
|
public void vote(AppraiseVoteDto appraiseVoteDto) {
|
|
String targetId = appraiseVoteDto.getTargetId();
|
|
boolean spread = appraiseVoteDto.isSpread();
|
|
boolean pushParent = appraiseVoteDto.isPushParent();
|
|
String targetType = appraiseVoteDto.getTargetType();
|
|
String appraiseId = appraiseVoteDto.getAppraiseId();
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
String schoolId = loginUser.getSchoolId();
|
|
String classId;
|
|
String className;
|
|
String periodId;
|
|
String name;
|
|
String avatar = null;
|
|
String gender = null;
|
|
// 分别对班级和学生的关键信息取值
|
|
if (targetType.equals(TARGET_STUDENT)) {
|
|
List<Student> list = studentRepository.findByIdAndCode(targetId, String.format(PK.STUDENT, schoolId));
|
|
Student student = RepositoryUtil.findOne(list, "该学生不存在");
|
|
|
|
classId = student.getClassId();
|
|
periodId = student.getPeriodId();
|
|
name = student.getName();
|
|
avatar = student.getPicture();
|
|
gender = student.getGender();
|
|
|
|
// 获取班级信息
|
|
ClassInfo classInfo = classRepository.findClassByIdAndCode(classId, String.format(PK.CLASS, schoolId));
|
|
className = classInfo.getName();
|
|
} else if (targetType.equals(TARGET_CLASS)){
|
|
ClassInfo classInfo = classRepository.findClassByIdAndCode(targetId, String.format(PK.CLASS, schoolId));
|
|
if (classInfo == null) {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "班级不存在");
|
|
}
|
|
classId = targetId;
|
|
periodId = classInfo.getPeriodId();
|
|
name = classInfo.getName();
|
|
className = classInfo.getName();
|
|
} else {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "不受支持的评价对象");
|
|
}
|
|
|
|
// 获取评价项节点
|
|
List<AppraiseTreeNode> nodes = appraiseRepository.findNodeById(PK.PK_APPRAISE, appraiseId);
|
|
AppraiseTreeNode appraiseTreeNode = RepositoryUtil.findOne(nodes, "该评价项不存在");
|
|
if (appraiseTreeNode.getPath() == null) {
|
|
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "仅能评价三级评价项");
|
|
}
|
|
|
|
// 通过 periodId 获取 semesters
|
|
List<School.Period> periodById = schoolRepository.findPeriodById(schoolId, periodId);
|
|
School.Period period = RepositoryUtil.findOne(periodById, "获取学段失败");
|
|
List<School.Semester> semesters = period.getSemesters();
|
|
|
|
// 获取当前学年学期组合 ID
|
|
String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
|
|
|
|
// 查询是否存在记录
|
|
AppraiseRecord record = appraiseRecordRepository.findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode(
|
|
targetId,
|
|
classId,
|
|
academicYearId,
|
|
String.format(PK.PK_APPRAISE_RECORD,schoolId)
|
|
);
|
|
|
|
// 初始化新的评价节点
|
|
AppraiseRecordItem item = new AppraiseRecordItem();
|
|
item.setId(UUID.randomUUID().toString());
|
|
item.setAppraiseNode(appraiseTreeNode);
|
|
item.setCreator(loginUser.getName());
|
|
item.setCreatorId(loginUser.getId());
|
|
item.setCreateTime(Instant.now().toEpochMilli());
|
|
// 处理学校与学生的差异
|
|
if (targetType.equals(TARGET_CLASS)){
|
|
item.setSpread(spread);
|
|
} else {
|
|
item.setPushParent(pushParent);
|
|
}
|
|
|
|
// 不存在或者学生的班级不一样则创建一条新的,存在则处理一下分值后再向其 nodes 中插入一条 item
|
|
if (record == null || !classId.equals(record.getClassId())) {
|
|
List<AppraiseRecordItem> items = Collections.singletonList(item);
|
|
|
|
record = new AppraiseRecord();
|
|
record.setTargetId(targetId);
|
|
record.setTargetType(targetType);
|
|
record.setClassId(classId);
|
|
record.setClassName(className);
|
|
record.setName(name);
|
|
record.setAvatar(avatar);
|
|
record.setGender(gender);
|
|
record.setAcademicYearId(academicYearId);
|
|
record.setPraiseCount(appraiseTreeNode.isPraise() ? 1 : -1);
|
|
record.setScore(ObjectUtils.isEmpty(appraiseTreeNode.getScore()) ? 0 : appraiseTreeNode.getScore());
|
|
record.setNodes(items);
|
|
record.setCode(String.format(PK.PK_APPRAISE_RECORD, schoolId));
|
|
appraiseRecordRepository.save(record);
|
|
} else {
|
|
CosmosPatchOperations operations = CosmosPatchOperations.create();
|
|
operations.add("/nodes/-", item);
|
|
// 表扬 (待改进不会减少表扬数)
|
|
if (appraiseTreeNode.isPraise()) {
|
|
operations.increment("/praiseCount", 1);
|
|
}
|
|
// 加分
|
|
int scoreToPlus = ObjectUtils.isEmpty(appraiseTreeNode.getScore()) ? 0 : appraiseTreeNode.getScore();
|
|
operations.increment("/score", scoreToPlus);
|
|
// patch doc
|
|
appraiseRecordRepository.save(record.getId(), PK.buildOf(PK.PK_APPRAISE_RECORD, schoolId), AppraiseRecord.class, operations);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<AppraiseRecordVo> findVoteRecord(FindVoteRecordDto findVoteRecordDto) {
|
|
String periodId = findVoteRecordDto.getPeriodId();
|
|
String targetId = StringUtils.isBlank(findVoteRecordDto.getTargetId()) ? null : findVoteRecordDto.getTargetId();
|
|
String targetType = StringUtils.isBlank(findVoteRecordDto.getTargetType()) ? null : findVoteRecordDto.getTargetType();
|
|
String classId = StringUtils.isBlank(findVoteRecordDto.getClassId()) ? null : findVoteRecordDto.getClassId();
|
|
Boolean isPraise = findVoteRecordDto.getIsPraise();
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
String teacherId = loginUser.getId();
|
|
String schoolId = loginUser.getSchoolId();
|
|
|
|
List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
|
|
String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
|
|
// 分页
|
|
Sort sort = Sort.by("createTime").descending();
|
|
final CosmosPageRequest pageRequest = new CosmosPageRequest(findVoteRecordDto.getCurrent(), findVoteRecordDto.getSize(), null, sort);
|
|
Page<AppraiseRecordVo> appraiseRecordItemPage = appraiseRecordRepository.searchNodesByCondition(
|
|
targetId,
|
|
targetType,
|
|
classId,
|
|
teacherId,
|
|
academicYearId,
|
|
isPraise,
|
|
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
|
pageRequest
|
|
);
|
|
|
|
List<AppraiseRecordVo> content = appraiseRecordItemPage.getContent();
|
|
|
|
return content;
|
|
}
|
|
|
|
@Override
|
|
public void recallVote(RecallVoteDto recallVoteDto) {
|
|
String recordId = recallVoteDto.getRecordId();
|
|
String nodeId = recallVoteDto.getNodeId();
|
|
User loginUser = SecurityUtil.getLoginUser();
|
|
String schoolId = loginUser.getSchoolId();
|
|
String userId = loginUser.getId();
|
|
|
|
Optional<AppraiseRecord> optional = appraiseRecordRepository.findById(recordId, PK.buildOf(PK.PK_APPRAISE_RECORD, schoolId));
|
|
AppraiseRecord appraiseRecord = optional.orElseThrow(() -> new ServiceException("该记录不存在"));
|
|
AppraiseRecordItem record = appraiseRecord.getNodes()
|
|
.stream()
|
|
.filter(item -> nodeId.equals(item.getId()))
|
|
.findFirst().orElseThrow(() -> new ServiceException("该记录节点不存在"));
|
|
// 鉴权(不是创建老师不能撤回)
|
|
if (!loginUser.getRoles().contains("admin") && !userId.equals(record.getCreatorId())) {
|
|
throw new ServiceException(ErrorCode.NO_AUTH_ERROR.getCode(), "您不是创建老师,不能撤回");
|
|
}
|
|
// 删除评价项并且恢复评分
|
|
appraiseRecord.getNodes().removeIf(item -> nodeId.equals(item.getId()));
|
|
boolean praise = record.getAppraiseNode().isPraise();
|
|
Integer newPraiseCount = appraiseRecord.getPraiseCount() + (praise ? -1 : 1);
|
|
appraiseRecord.setPraiseCount(newPraiseCount);
|
|
int score = record.getAppraiseNode().getScore() == null ? 0 : record.getAppraiseNode().getScore();
|
|
Integer newScore = appraiseRecord.getScore() - score;
|
|
appraiseRecord.setScore(newScore);
|
|
// 保存
|
|
appraiseRecordRepository.save(appraiseRecord);
|
|
}
|
|
|
|
@Override
|
|
public StudentReportVo studentReport(IdRequest idRequest) {
|
|
String studentId = idRequest.getId();
|
|
User user = SecurityUtil.getLoginUser();
|
|
String schoolId = user.getSchoolId();
|
|
// 查询学生信息,学段等
|
|
Student student = RepositoryUtil.findOne(studentRepository.findByIdAndCode(studentId, String.format(PK.STUDENT, schoolId)), "当前学生不存在");
|
|
String periodId = student.getPeriodId();
|
|
String classId = student.getClassId();
|
|
List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
|
|
// 生成学年 id
|
|
String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
|
|
// 查询评价文档
|
|
AppraiseRecord appraiseRecord = appraiseRecordRepository.findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode(
|
|
studentId,
|
|
classId,
|
|
academicYearId,
|
|
String.format(PK.PK_APPRAISE_RECORD, schoolId)
|
|
);
|
|
// 班级内学生总数
|
|
int stuInClassCount = studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, schoolId));
|
|
|
|
if (appraiseRecord == null || appraiseRecord.getNodes() == null) {
|
|
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前学生暂未产生评价数据,无法生成报告");
|
|
}
|
|
Integer praiseCount = appraiseRecord.getPraiseCount();
|
|
List<AppraiseRecordItem> records = appraiseRecord.getNodes();
|
|
records = records.stream().sorted(Comparator.comparing(AppraiseRecordItem::getCreateTime).reversed()).collect(Collectors.toList());
|
|
// 查询成就规则
|
|
Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "当前成就规则还未创建");
|
|
List<AchievementRule> rules = appraise.getAchievementRules();
|
|
if (CollectionUtil.isEmpty(rules)) {
|
|
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前成就规则为空");
|
|
}
|
|
// 计算雷达图
|
|
Map<String, Integer> praiseDistribution = StudentReportVo.ofFiveEducation();
|
|
Map<String, Integer> criticalDistribution = StudentReportVo.ofFiveEducation();
|
|
// 计算数据
|
|
Map<String, Integer> praiseTeacherMap = new HashMap<>();
|
|
Map<String, Integer> criticalTeacherMap = new HashMap<>();
|
|
Map<String, Integer> praiseNodeMap = new HashMap<>();
|
|
Map<String, Integer> criticalNodeMap = new HashMap<>();
|
|
// 计算当前学生排名在全班次位
|
|
int greaterCount = appraiseRecordRepository.findClassRecord(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, classId, praiseCount);
|
|
float beyondPercent = 0;
|
|
long currentRank = stuInClassCount - greaterCount;
|
|
beyondPercent = (float) currentRank / stuInClassCount;
|
|
|
|
// 根据全部数据计算结果
|
|
for (AppraiseRecordItem record : records) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
String teacherId = record.getCreatorId();
|
|
String nodeName = appraiseNode.getName();
|
|
// 根据评价是否为表扬计算数据
|
|
if (appraiseNode.isPraise()) {
|
|
praiseTeacherMap.put(teacherId, praiseTeacherMap.getOrDefault(teacherId, 0) + 1);
|
|
praiseNodeMap.put(nodeName, praiseNodeMap.getOrDefault(nodeName, 0) + 1);
|
|
} else {
|
|
criticalTeacherMap.put(teacherId, criticalTeacherMap.getOrDefault(teacherId, 0) + 1);
|
|
criticalNodeMap.put(nodeName, criticalNodeMap.getOrDefault(nodeName, 0) + 1);
|
|
}
|
|
}
|
|
// 计算成就项 (排序 120(20x3) 60(10x4) 25(5x5) 121
|
|
rules = rules.stream().sorted(Comparator.comparing(AchievementRule::getLevel).reversed()).collect(Collectors.toList());
|
|
int ruleSize = rules.size();
|
|
AchievementRule curAchievement = rules.get(ruleSize - 1);
|
|
|
|
int n = 1;
|
|
for (int i = 0; i <= ruleSize - 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 == ruleSize - 1) {
|
|
n = praiseCount / curRule.getLevelCount() + 1;
|
|
}
|
|
}
|
|
|
|
// 汇总数据
|
|
String topPraiseTeacherId = praiseTeacherMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
|
|
AppraiseRecordItem tmp1 = records.stream().filter(item -> item.getCreatorId().equals(topPraiseTeacherId)).findFirst().orElse(null);
|
|
String topPraiseTeacherName = tmp1 == null ? null : tmp1.getCreator();
|
|
|
|
String topCriticalTeacherId = criticalTeacherMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
|
|
AppraiseRecordItem tmp2 = records.stream().filter(item -> item.getCreatorId().equals(topCriticalTeacherId)).findFirst().orElse(null);
|
|
String topCriticalTeacherName = tmp2 == null ? null : tmp2.getCreator();
|
|
|
|
String topPraiseNode = praiseNodeMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
|
|
String topCriticalNode = criticalNodeMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
|
|
|
|
return StudentReportVo.builder()
|
|
.praiseCount(praiseCount)
|
|
.score(appraiseRecord.getScore())
|
|
.beyondPercent(beyondPercent)
|
|
.topPraiseTeacher(new StudentReportVo.Teacher(topPraiseTeacherId, topPraiseTeacherName))
|
|
.topCriticalTeacher(new StudentReportVo.Teacher(topCriticalTeacherId, topCriticalTeacherName))
|
|
.topPraiseNode(topPraiseNode)
|
|
.topCriticalNode(topCriticalNode)
|
|
.praiseDistribution(praiseDistribution)
|
|
.criticalDistribution(criticalDistribution)
|
|
.curAchievement(curAchievement)
|
|
.achievementN(n)
|
|
.records(records)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* 递归收集 id 的节点及 id 节点的孩子节点 (迭代器删除居然也报错)
|
|
*/
|
|
private void collectNodesToDelete(String id, List<AppraiseTreeNode> nodes, List<AppraiseTreeNode> nodesToDelete) {
|
|
for (AppraiseTreeNode node : nodes) {
|
|
if (id.equals(node.getPid())) {
|
|
// 收集
|
|
nodesToDelete.add(node);
|
|
// 递归收集
|
|
collectNodesToDelete(node.getId(), nodes, nodesToDelete);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 递归的构建父亲节点的孩子,以及孩子的孩子 (理论支持无极树,但应该考虑是否增加递归深度)
|
|
*/
|
|
private void buildChildren(AppraiseTreeNode parent, List<AppraiseTreeNode> nodes) {
|
|
List<AppraiseTreeNode> children = getChildren(parent, nodes);
|
|
if (children.size() == 0) return;
|
|
|
|
parent.setChildren(children);
|
|
for (AppraiseTreeNode child : children) {
|
|
// 如果子节点还有孩子的话,就继续递归构建
|
|
if (getChildren(child, nodes).size() > 0) {
|
|
buildChildren(child, nodes);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* 获取节点的孩子节点列表
|
|
*/
|
|
private List<AppraiseTreeNode> getChildren(AppraiseTreeNode parent, List<AppraiseTreeNode> nodes) {
|
|
List<AppraiseTreeNode> children = new ArrayList<>();
|
|
for (AppraiseTreeNode node : nodes) {
|
|
if (StringUtils.isNotBlank(node.getPid()) && node.getPid().equals(parent.getId())) {
|
|
children.add(node);
|
|
}
|
|
}
|
|
return children;
|
|
}
|
|
}
|