package cn.teammodel.service.impl; import cn.hutool.core.lang.UUID; import cn.teammodel.common.ErrorCode; import cn.teammodel.common.PK; import cn.teammodel.config.exception.ServiceException; import cn.teammodel.dao.EvaluationRepository; import cn.teammodel.model.dto.DeleteNodeDto; import cn.teammodel.model.dto.GetEvaluateTreeDto; import cn.teammodel.model.dto.InsertNodeDto; import cn.teammodel.model.dto.UpdateNodeDto; import cn.teammodel.model.entity.Evaluation; import cn.teammodel.model.entity.EvaluationTreeNode; import cn.teammodel.service.EvaluationService; import com.azure.spring.data.cosmos.core.CosmosTemplate; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @author winter * @create 2023-11-20 17:47 */ @Service public class EvaluationServiceImpl implements EvaluationService { @Resource CosmosTemplate cosmosTemplate; @Resource private EvaluationRepository evaluationRepository; @Override public Evaluation getTree(GetEvaluateTreeDto getEvaluateTreeDto) { String schoolId = getEvaluateTreeDto.getSchoolId(); String periodId = getEvaluateTreeDto.getPeriodId(); periodId = StringUtils.isEmpty(periodId) ? "default" : periodId; Evaluation evaluation = evaluationRepository.findBySchoolIdAndPeriodId(schoolId, periodId, PK.PK_EVALUATION); return this.buildTree(evaluation); } @Override public Evaluation buildTree(Evaluation evaluation) { if (evaluation == null) { return null; } List nodes = evaluation.getNodes(); List parents = new ArrayList<>(); // pid 为 null 或者 "" 则为 parents for (EvaluationTreeNode node : nodes) { if (StringUtils.isBlank(node.getPid())) { parents.add(node); } } // 迭代构建孩子节点 for (EvaluationTreeNode parent : parents) { buildChildren(parent, nodes); } evaluation.setNodes(parents); return evaluation; } @Override public void flattenTree(List trees, List nodes) { // 递归结束条件 if (ObjectUtils.isEmpty(trees)) return; // 这里的每个 tree 都是组装好的树,区别于单个节点 for (EvaluationTreeNode tree : trees) { // 递归 flattenTree(tree.getChildren(), nodes); // 回溯时删除 children,添加进列表 tree.setChildren(null); nodes.add(tree); } } @Override public Evaluation insertNode(InsertNodeDto insertNodeDto) { Evaluation evaluation = findEvaluation(insertNodeDto.getSchoolId(), insertNodeDto.getPeriodId()); List originNodes = evaluation.getNodes(); // 拷贝数据到新节点 EvaluationTreeNode newNode = new EvaluationTreeNode(); 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(), "父节点不存在"); } } // todo: 为新节点赋值必须参数 (id, creator), 可不可以添加默认值 order ? newNode.setId(UUID.randomUUID().toString()); originNodes.add(newNode); evaluation.setNodes(originNodes); return buildTree(evaluationRepository.save(evaluation)); } @Override public Evaluation updateNode(UpdateNodeDto updateNodeDto) { String updateNodeId = updateNodeDto.getId(); Evaluation evaluation = findEvaluation(updateNodeDto.getSchoolId(), updateNodeDto.getPeriodId()); List originNodes = evaluation.getNodes(); // 每个节点都有 id, 直接校验是否合法 EvaluationTreeNode updateNode = originNodes.stream() .filter(item -> updateNodeId.equals(item.getId())) .findFirst() .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "更新节点不存在")); // 更新字段,考虑直接 copy properties updateNode.setName(updateNodeDto.getName()); updateNode.setLogo(updateNodeDto.getLogo()); updateNode.setOrder(updateNodeDto.getOrder()); updateNode.setPraise(updateNodeDto.isPraise()); // todo: 为新节点赋值必须参数 (id, creator), 可不可以添加默认值 order ? return buildTree(evaluationRepository.save(evaluation)); } @Override public Evaluation deleteNode(DeleteNodeDto deleteNodeDto) { // 删除指定节点,可能是(一级,二级,三级),设计一个通用的 Evaluation evaluation = findEvaluation(deleteNodeDto.getSchoolId(), deleteNodeDto.getPeriodId()); List nodes = evaluation.getNodes(); List nodesToDelete = new ArrayList<>(); // 迭代器安全删除 for (EvaluationTreeNode node : nodes) { if (node.getId().equals(deleteNodeDto.getId())) { // 删除当前节点 nodesToDelete.add(node); // 递归删除其孩子节点 this.collectNodesToDelete(node.getId(), nodes, nodesToDelete); } } nodes.removeAll(nodesToDelete); return buildTree(evaluationRepository.save(evaluation)); } /** * 递归收集 id 的节点及 id 节点的孩子节点 (迭代器删除居然也报错) */ private void collectNodesToDelete(String id, List nodes, List nodesToDelete) { for (EvaluationTreeNode node : nodes) { if (id.equals(node.getPid())) { // 收集 nodesToDelete.add(node); // 递归收集 collectNodesToDelete(node.getId(), nodes, nodesToDelete); } } } /** * 通用的获取 evaluation 的方法: 判断参数,判断数据是否为空 */ private Evaluation findEvaluation(String schoolId,String periodId) { periodId = StringUtils.isEmpty(periodId) ? "default" : periodId; // 拿到要新增节点的原始数据 Evaluation evaluation = evaluationRepository.findBySchoolIdAndPeriodId(schoolId, periodId, PK.PK_EVALUATION); if (evaluation == null) { throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "学校评价数据不存在"); } return evaluation; } /** * 递归的构建父亲节点的孩子,以及孩子的孩子 (理论支持无极树,但应该考虑是否增加递归深度) */ private void buildChildren(EvaluationTreeNode parent, List nodes) { List children = getChildren(parent, nodes); if (children.size() == 0) return; parent.setChildren(children); for (EvaluationTreeNode child : children) { // 如果子节点还有孩子的话,就继续递归构建 if (getChildren(child, nodes).size() > 0) { buildChildren(child, nodes); } } } /** * 获取节点的孩子节点列表 */ private List getChildren(EvaluationTreeNode parent, List nodes) { List children = new ArrayList<>(); for (EvaluationTreeNode node : nodes) { if (StringUtils.isNotBlank(node.getPid()) && node.getPid().equals(parent.getId())) { children.add(node); } } return children; } }