新增对评价树的crud以及测试bug修复

11111
winter 1 year ago
parent 1be6ca7e02
commit 68f5303a4c

@ -43,7 +43,12 @@
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter</artifactId>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-data-cosmos</artifactId>
@ -61,6 +66,13 @@
<optional>true</optional>
</dependency>
<!-- 修改后的jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

@ -8,6 +8,6 @@ import com.azure.cosmos.models.PartitionKey;
* @create 2023-11-22 15:31
*/
public interface PK {
PartitionKey PK_SCHOOL = new PartitionKey("School");
PartitionKey PK_EVALUATION = new PartitionKey("Evaluation");
}

@ -1,5 +1,7 @@
package cn.teammodel.config.exception;
import cn.teammodel.common.ErrorCode;
/**
*
*
@ -27,20 +29,21 @@ public final class ServiceException extends RuntimeException
/**
*
*/
public ServiceException()
{
}
public ServiceException() {}
public ServiceException(String message)
{
this.message = message;
}
public ServiceException(String message, Integer code)
{
public ServiceException(Integer code, String message) {
this.message = message;
this.code = code;
}
public ServiceException(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
public String getDetailMessage()
{

@ -0,0 +1,35 @@
package cn.teammodel.config.knife;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Knife4j <br/>
* https://doc.xiaominfo.com/knife4j/documentation/get_start.html
*/
@Configuration
@EnableSwagger2
//@Profile({"dev", "test"})
public class Knife4jConfig {
@Bean
public Docket defaultApi2() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
.title("五育评价接口文档")
.description("五育评价接口描述")
.version("1.0.1")
.build())
.select()
// 指定 Controller 扫描包路径
.apis(RequestHandlerSelectors.basePackage("cn.teammodel.controller"))
.paths(PathSelectors.any())
.build();
}
}

@ -1,30 +1,54 @@
package cn.teammodel.controller;
import cn.teammodel.common.R;
import cn.teammodel.model.dto.DeleteNodeDto;
import cn.teammodel.model.dto.GetEvaluateTreeDto;
import cn.teammodel.model.entity.EvaluationTreeNode;
import cn.teammodel.model.dto.InsertNodeDto;
import cn.teammodel.model.dto.UpdateNodeDto;
import cn.teammodel.model.entity.Evaluation;
import cn.teammodel.service.EvaluationService;
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 java.util.List;
/**
* @author winter
* @create 2023-11-22 15:10
*/
@RestController
@RequestMapping("/public/evaluate")
@RequestMapping("/appraise")
public class EvaluationController {
@Resource
private EvaluationService evaluationService;
@PostMapping("getTrees")
public R<List<EvaluationTreeNode>> getEvaluateTree(@RequestBody GetEvaluateTreeDto getEvaluateTreeDto) {
List<EvaluationTreeNode> tree = evaluationService.getTree(getEvaluateTreeDto);
return R.success(tree);
@ApiOperation(value = "获取评价树", notes = "获取评价树")
public R<Evaluation> getEvaluateTree(@RequestBody GetEvaluateTreeDto getEvaluateTreeDto) {
Evaluation evaluation = evaluationService.getTree(getEvaluateTreeDto);
return R.success(evaluation);
}
@PostMapping("insertNode")
@ApiOperation(value = "新增评价树的节点")
public R<Evaluation> insertNode(@RequestBody InsertNodeDto insertNodeDto) {
Evaluation evaluation = evaluationService.insertNode(insertNodeDto);
return R.success(evaluation);
}
@PostMapping("updateNode")
@ApiOperation(value = "更新评价树的节点", notes = "传递更新后的节点,而不是局部更新的值")
public R<Evaluation> updateTree(@RequestBody UpdateNodeDto updateNodeDto) {
Evaluation evaluation = evaluationService.updateNode(updateNodeDto);
return R.success(evaluation);
}
@PostMapping("deleteNode")
@ApiOperation(value = "删除评价树的节点")
public R<Evaluation> deleteNode(@RequestBody DeleteNodeDto deleteNodeDto) {
// todo: 注意删除子节点
Evaluation evaluation = evaluationService.deleteNode(deleteNodeDto);
return R.success(evaluation);
}
}

@ -0,0 +1,16 @@
package cn.teammodel.model.dto;
import lombok.Data;
/**
* @author winter
* @create 2023-11-22 16:16
*/
@Data
public class DeleteNodeDto {
// 检索需要,但是有 bug
String schoolId;
String periodId;
// todo: 判断空
String id;
}

@ -0,0 +1,21 @@
package cn.teammodel.model.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author winter
* @create 2023-11-22 16:16
*/
@Data
public class InsertNodeDto {
// 检索需要,但是有 bug
String schoolId;
String periodId;
@ApiModelProperty(value = "父亲节点,不传则为根节点")
String pid;
String name;
String logo;
Integer order;
boolean isPraise;
}

@ -0,0 +1,19 @@
package cn.teammodel.model.dto;
import lombok.Data;
/**
* @author winter
* @create 2023-11-22 16:16
*/
@Data
public class UpdateNodeDto {
// 检索需要,但是有 bug
String schoolId;
String periodId;
String id;
String name;
String logo;
Integer order;
boolean isPraise;
}

@ -1,6 +1,6 @@
package cn.teammodel.model.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@ -15,7 +15,7 @@ public class EvaluationTreeNode {
/**
*
*/
String order;
Integer order;
/**
*
*/
@ -23,7 +23,8 @@ public class EvaluationTreeNode {
/**
* ? true
*/
boolean isPraise = true;
@JsonIgnore
@JsonProperty("isPraise")
boolean isPraise;
List<EvaluationTreeNode> children;
}

@ -34,7 +34,17 @@ public class SecurityConfiguration {
.and()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/public/**").permitAll()
.antMatchers("/public/**",
"/swagger-ui/",
"/swagger-resources/**",
"/**/v2/api-docs",
"/**/v3/api-docs",
"/**/*.html",
"/**/*.js",
"/**/*.css",
"/**/*.png",
"/**/*.map",
"/favicon.ico").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // 启用 OIDC jwt filter

@ -26,7 +26,7 @@ public class SecurityUtils
}
catch (Exception e)
{
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED.value());
throw new ServiceException( HttpStatus.UNAUTHORIZED.value(), "获取用户ID异常");
}
}
@ -42,7 +42,7 @@ public class SecurityUtils
}
catch (Exception e)
{
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED.value());
throw new ServiceException(HttpStatus.UNAUTHORIZED.value(), "获取用户账户异常");
}
}
@ -57,7 +57,7 @@ public class SecurityUtils
}
catch (Exception e)
{
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED.value());
throw new ServiceException(HttpStatus.UNAUTHORIZED.value(), "获取用户信息异常");
}
}

@ -1,6 +1,10 @@
package cn.teammodel.service;
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 java.util.List;
@ -10,12 +14,12 @@ import java.util.List;
* @create 2023-11-20 17:46
*/
public interface EvaluationService {
List<EvaluationTreeNode> getTree(GetEvaluateTreeDto getEvaluateTreeDto);
Evaluation getTree(GetEvaluateTreeDto getEvaluateTreeDto);
/**
*
*/
List<EvaluationTreeNode> buildTree(List<EvaluationTreeNode> nodes);
Evaluation buildTree(Evaluation evaluation);
/**
*
@ -27,4 +31,19 @@ public interface EvaluationService {
* @description:
*/
void flattenTree(List<EvaluationTreeNode> trees, List<EvaluationTreeNode> nodes);
/**
*
*/
Evaluation insertNode(InsertNodeDto insertNodeDto);
/**
*
*/
Evaluation updateNode(UpdateNodeDto updateNodeDto);
/**
* ,
*/
Evaluation deleteNode(DeleteNodeDto deleteNodeDto);
}

@ -1,13 +1,21 @@
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;
@ -20,25 +28,30 @@ import java.util.List;
*/
@Service
public class EvaluationServiceImpl implements EvaluationService {
@Resource
CosmosTemplate cosmosTemplate;
@Resource
private EvaluationRepository evaluationRepository;
@Override
public List<EvaluationTreeNode> getTree(GetEvaluateTreeDto getEvaluateTreeDto) {
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_SCHOOL);
List<EvaluationTreeNode> nodes = evaluation.getNodes();
List<EvaluationTreeNode> trees = this.buildTree(nodes);
evaluation.setNodes(trees);
return trees;
Evaluation evaluation = evaluationRepository.findBySchoolIdAndPeriodId(schoolId, periodId, PK.PK_EVALUATION);
return this.buildTree(evaluation);
}
@Override
public List<EvaluationTreeNode> buildTree(List<EvaluationTreeNode> nodes) {
public Evaluation buildTree(Evaluation evaluation) {
if (evaluation == null) {
return null;
}
List<EvaluationTreeNode> nodes = evaluation.getNodes();
List<EvaluationTreeNode> parents = new ArrayList<>();
// pid 为 null 或者 "" 则为 parents
for (EvaluationTreeNode node : nodes) {
if (StringUtils.isBlank(node.getPid())) {
@ -49,7 +62,8 @@ public class EvaluationServiceImpl implements EvaluationService {
for (EvaluationTreeNode parent : parents) {
buildChildren(parent, nodes);
}
return parents;
evaluation.setNodes(parents);
return evaluation;
}
@Override
@ -67,6 +81,101 @@ public class EvaluationServiceImpl implements EvaluationService {
}
}
@Override
public Evaluation insertNode(InsertNodeDto insertNodeDto) {
Evaluation evaluation = findEvaluation(insertNodeDto.getSchoolId(), insertNodeDto.getPeriodId());
List<EvaluationTreeNode> 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<EvaluationTreeNode> 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<EvaluationTreeNode> nodes = evaluation.getNodes();
List<EvaluationTreeNode> 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<EvaluationTreeNode> nodes, List<EvaluationTreeNode> 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;
}
/**
* , (,)
*/
@ -84,6 +193,10 @@ public class EvaluationServiceImpl implements EvaluationService {
}
/**
*
*/
private List<EvaluationTreeNode> getChildren(EvaluationTreeNode parent, List<EvaluationTreeNode> nodes) {
List<EvaluationTreeNode> children = new ArrayList<>();
for (EvaluationTreeNode node : nodes) {

@ -1,4 +1,14 @@
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# 文件上传
servlet:
multipart:
# 大小限制
max-file-size: 10MB
cloud:
azure:
cosmos:

@ -1,6 +1,5 @@
package cn.teammodel;
import cn.hutool.json.JSONUtil;
import cn.teammodel.dao.EvaluationRepository;
import cn.teammodel.dao.StudentRepository;
import cn.teammodel.manager.DingAlertNotifier;
@ -9,16 +8,20 @@ import cn.teammodel.model.entity.EvaluationTreeNode;
import cn.teammodel.service.EvaluationService;
import cn.teammodel.service.impl.EvaluationServiceImpl;
import com.azure.cosmos.models.PartitionKey;
import com.azure.spring.data.cosmos.core.CosmosTemplate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@SpringBootTest
class TeamModelExtensionApplicationTests {
@Resource
CosmosTemplate cosmosTemplate;
@Autowired
private DingAlertNotifier notifier;
@ -65,7 +68,7 @@ class TeamModelExtensionApplicationTests {
nodes.add(node2);
evaluation.setNodes(nodes);
Evaluation saved = evaluationRepository.save(evaluation);
Evaluation saved = cosmosTemplate.insert(evaluation, null);
System.out.println(saved);
}
@ -73,14 +76,14 @@ class TeamModelExtensionApplicationTests {
public void testSelect() {
Evaluation saved = evaluationRepository.findBySchoolId("hbcn", new PartitionKey("evaluation"));
EvaluationService service = new EvaluationServiceImpl();
System.out.println(JSONUtil.parse(service.buildTree(saved.getNodes())).toStringPretty());
//System.out.println(JSONUtil.parse(service.buildTree(saved.getNodes())).toStringPretty());
}
@Test
public void testUpdate() {
//EvaluationTree saved = evaluationTreeRepository.findBySchoolId("hbcn", new PartitionKey("evaluation"));
Evaluation saved = evaluationRepository.findBySchoolId("hbcn", new PartitionKey("evaluation"));
EvaluationService service = new EvaluationServiceImpl();
System.out.println(JSONUtil.parse(service.buildTree(saved.getNodes())).toStringPretty());
//System.out.println(JSONUtil.parse(service.buildTree(saved.getNodes())).toStringPretty());
evaluationRepository.save(saved);
}

@ -1,6 +1,5 @@
package cn.teammodel;
import cn.hutool.json.JSONUtil;
import cn.teammodel.model.entity.Evaluation;
import cn.teammodel.model.entity.EvaluationTreeNode;
import cn.teammodel.service.impl.EvaluationServiceImpl;
@ -127,11 +126,11 @@ public class TestWithoutSpring {
nodes.add(node2);
evaluation.setNodes(nodes);
List<EvaluationTreeNode> tree = service.buildTree(nodes);
System.out.println(JSONUtil.parse(service.buildTree(tree)).toStringPretty());
List<EvaluationTreeNode> nodeList = new ArrayList<>();
service.flattenTree(tree, nodeList);
System.out.println(JSONUtil.parse(nodeList).toStringPretty());
//List<EvaluationTreeNode> tree = service.buildTree(nodes);
//System.out.println(JSONUtil.parse(service.buildTree(tree)).toStringPretty());
//
//List<EvaluationTreeNode> nodeList = new ArrayList<>();
//service.flattenTree(tree, nodeList);
//System.out.println(JSONUtil.parse(nodeList).toStringPretty());
}
}

Loading…
Cancel
Save