diff --git a/src/main/java/cn/teammodel/ai/listener/SparkGptStreamListener.java b/src/main/java/cn/teammodel/ai/listener/SparkGptStreamListener.java
index 771f058..a557e12 100644
--- a/src/main/java/cn/teammodel/ai/listener/SparkGptStreamListener.java
+++ b/src/main/java/cn/teammodel/ai/listener/SparkGptStreamListener.java
@@ -1,6 +1,5 @@
package cn.teammodel.ai.listener;
-import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.teammodel.ai.SseHelper;
import cn.teammodel.ai.domain.SparkChatResponse;
@@ -65,7 +64,7 @@ public class SparkGptStreamListener extends WebSocketListener {
String msgSegment = sparkChatResponse.getPayload().getChoices().getText().get(0).getContent();
// 处理消息格式(空格和换行符)
// msgSegment = StrUtil.replace(msgSegment, " ", " ").replaceAll("\n", "\n");
- msgSegment = StrUtil.replace(msgSegment, " ", " ").replaceAll("\n", "
");
+// msgSegment = StrUtil.replace(msgSegment, " ", " ").replaceAll("\n", "
");
answer += msgSegment;
SseHelper.send(sseEmitter, msgSegment);
diff --git a/src/main/java/cn/teammodel/common/PK.java b/src/main/java/cn/teammodel/common/PK.java
index 599750c..877d43b 100644
--- a/src/main/java/cn/teammodel/common/PK.java
+++ b/src/main/java/cn/teammodel/common/PK.java
@@ -24,6 +24,7 @@ public interface PK {
String STUDENT = "Base-%s";
String CLASS = "Class-%s";
String CHAT_SESSION = "ChatSession";
+ String WEEK_DUTY = "Duty";
/**
* 构建分区键
diff --git a/src/main/java/cn/teammodel/config/exception/ServiceException.java b/src/main/java/cn/teammodel/config/exception/ServiceException.java
index d41050d..4368e9a 100644
--- a/src/main/java/cn/teammodel/config/exception/ServiceException.java
+++ b/src/main/java/cn/teammodel/config/exception/ServiceException.java
@@ -33,6 +33,7 @@ public final class ServiceException extends RuntimeException
public ServiceException(String message)
{
+ this.code = ErrorCode.SYSTEM_ERROR.getCode();
this.message = message;
}
diff --git a/src/main/java/cn/teammodel/controller/frontend/AiController.java b/src/main/java/cn/teammodel/controller/frontend/AiController.java
index e246560..427a5ba 100644
--- a/src/main/java/cn/teammodel/controller/frontend/AiController.java
+++ b/src/main/java/cn/teammodel/controller/frontend/AiController.java
@@ -13,7 +13,9 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.Resource;
import javax.validation.Valid;
+import java.io.IOException;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
@RestController
@RequestMapping("/ai")
@@ -29,6 +31,40 @@ public class AiController {
return chatMessageService.chatCompletion(chatCompletionReqDto);
}
+ @PostMapping("chat/test/completion")
+ @ApiOperation("与 spark 的流式对话")
+ public SseEmitter testCompletion(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) throws IOException, InterruptedException {
+ SseEmitter sseEmitter = new SseEmitter();
+ CompletableFuture.runAsync(() -> {
+ try {
+ sseEmitter.send("曾经以为我们");
+ Thread.sleep(1000);
+ sseEmitter.send("开始了就会走到最后,可是当分手之后才明白我想的");
+ Thread.sleep(1000);
+ sseEmitter.send("你不一定能做到,当我开始去遗忘我们");
+ Thread.sleep(1500);
+ sseEmitter.send("的经历时,却不知道遗忘已变成另一种开始,");
+ Thread.sleep(800);
+ sseEmitter.send("回忆是淡了,可是痛却还在,还是最真实。放手的时候微笑着说无所谓");
+ Thread.sleep(800);
+ sseEmitter.send(",离开了你我还可以过的很好");
+ Thread.sleep(1000);
+ sseEmitter.send(",而你却不知道微笑只是用来掩盖疼痛的伤疤。");
+ Thread.sleep(1200);
+ sseEmitter.send("离开你之后的自己陷入一种无助的状态,空洞的");
+ Thread.sleep(1300);
+ sseEmitter.send("双眼回忆不起曾经的你,那时候以为与世隔绝或许才是维护自己的最好方式。");
+ Thread.sleep(1100);
+ sseEmitter.send("[DONE]");
+ sseEmitter.complete();
+
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return sseEmitter;
+ }
+
@GetMapping("session/my")
@ApiOperation("查询我的聊天会话")
public R> listMySession() {
diff --git a/src/main/java/cn/teammodel/controller/frontend/AppraiseController.java b/src/main/java/cn/teammodel/controller/frontend/AppraiseController.java
index 91ae70a..462f7a0 100644
--- a/src/main/java/cn/teammodel/controller/frontend/AppraiseController.java
+++ b/src/main/java/cn/teammodel/controller/frontend/AppraiseController.java
@@ -43,6 +43,7 @@ public class AppraiseController {
@PostMapping("updateNode")
@ApiOperation(value = "更新评价树的节点", notes = "传递更新后的节点,而不是局部更新的值")
public R updateTree(@RequestBody @Valid UpdateNodeDto updateNodeDto) {
+ // fixme: 更新一二级节点应该同时更新三级的 path
Appraise appraise = evaluationService.updateNode(updateNodeDto);
return R.success(appraise);
}
diff --git a/src/main/java/cn/teammodel/controller/frontend/DutyController.java b/src/main/java/cn/teammodel/controller/frontend/DutyController.java
new file mode 100644
index 0000000..716bcfe
--- /dev/null
+++ b/src/main/java/cn/teammodel/controller/frontend/DutyController.java
@@ -0,0 +1,65 @@
+package cn.teammodel.controller.frontend;
+
+import cn.teammodel.common.R;
+import cn.teammodel.model.dto.weekDuty.DeleteDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.DutyVoteDto;
+import cn.teammodel.model.dto.weekDuty.InsertDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.UpdateDutyNodeDto;
+import cn.teammodel.model.entity.weekDuty.WeekDuty;
+import cn.teammodel.service.DutyService;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:06
+ */
+@RestController
+@RequestMapping("/duty")
+public class DutyController {
+ @Resource
+ private DutyService dutyService;
+
+ @GetMapping("/getTree")
+ @ApiOperation("获取值周评价标准树(不存在则拷贝模板)")
+ public R getTree() {
+ WeekDuty weekDuty = dutyService.getTree();
+ return R.success(weekDuty);
+ }
+
+ @PostMapping("/insertNode")
+ @ApiOperation("插入值周评价标准树节点")
+ public R insertNode(@RequestBody @Valid InsertDutyNodeDto insertDutyNodeDto) {
+ WeekDuty weekDuty = dutyService.insertNode(insertDutyNodeDto);
+ return R.success(weekDuty);
+ }
+
+ @PostMapping("/deleteNode")
+ @ApiOperation("删除值周评价标准树节点")
+ public R deleteNode(@RequestBody @Valid DeleteDutyNodeDto deleteDutyNodeDto) {
+ WeekDuty weekDuty = dutyService.deleteNode(deleteDutyNodeDto);
+ return R.success(weekDuty);
+ }
+
+ @PostMapping("/updateNode")
+ @ApiOperation("更新值周评价标准树节点")
+ public R updateNode(@RequestBody @Valid UpdateDutyNodeDto updateDutyNodeDto) {
+ WeekDuty weekDuty = dutyService.updateNode(updateDutyNodeDto);
+ return R.success(weekDuty);
+ }
+
+ @PostMapping("/vote")
+ @ApiOperation("值周评价投票")
+ public R vote(@RequestBody @Valid DutyVoteDto dutyVoteDto) {
+ dutyService.vote(dutyVoteDto);
+ return R.success("评价成功");
+ }
+
+
+
+
+
+}
diff --git a/src/main/java/cn/teammodel/model/dto/Appraise/InsertNodeDto.java b/src/main/java/cn/teammodel/model/dto/Appraise/InsertNodeDto.java
index 912c6a4..258345b 100644
--- a/src/main/java/cn/teammodel/model/dto/Appraise/InsertNodeDto.java
+++ b/src/main/java/cn/teammodel/model/dto/Appraise/InsertNodeDto.java
@@ -24,5 +24,5 @@ public class InsertNodeDto {
String logo;
Integer order = 0;
Integer score = 0;
- boolean isPraise;
+ Boolean isPraise = true;
}
diff --git a/src/main/java/cn/teammodel/model/dto/weekDuty/DeleteDutyNodeDto.java b/src/main/java/cn/teammodel/model/dto/weekDuty/DeleteDutyNodeDto.java
new file mode 100644
index 0000000..190f97f
--- /dev/null
+++ b/src/main/java/cn/teammodel/model/dto/weekDuty/DeleteDutyNodeDto.java
@@ -0,0 +1,17 @@
+package cn.teammodel.model.dto.weekDuty;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:56
+ */
+@Data
+public class DeleteDutyNodeDto {
+ @ApiModelProperty("要删除的节点 id")
+ @NotNull
+ private String nodeId;
+}
diff --git a/src/main/java/cn/teammodel/model/dto/weekDuty/DutyVoteDto.java b/src/main/java/cn/teammodel/model/dto/weekDuty/DutyVoteDto.java
new file mode 100644
index 0000000..449f205
--- /dev/null
+++ b/src/main/java/cn/teammodel/model/dto/weekDuty/DutyVoteDto.java
@@ -0,0 +1,25 @@
+package cn.teammodel.model.dto.weekDuty;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author winter
+ * @create 2024-01-03 15:05
+ */
+@Data
+public class DutyVoteDto {
+ @NotNull
+ @ApiModelProperty(value = "班级id", required = true)
+ private String classId;
+ @NotNull
+ @ApiModelProperty(value = "评价地点 id", required = true)
+ private String spotId;
+ @NotNull
+ @ApiModelProperty(value = "评价节点id", required = true)
+ private String dutyNodeId;
+ private String note;
+ private String[] attachments;
+}
diff --git a/src/main/java/cn/teammodel/model/dto/weekDuty/InsertDutyNodeDto.java b/src/main/java/cn/teammodel/model/dto/weekDuty/InsertDutyNodeDto.java
new file mode 100644
index 0000000..19cfc1b
--- /dev/null
+++ b/src/main/java/cn/teammodel/model/dto/weekDuty/InsertDutyNodeDto.java
@@ -0,0 +1,23 @@
+package cn.teammodel.model.dto.weekDuty;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:45
+ */
+
+@Data
+public class InsertDutyNodeDto {
+ @ApiModelProperty(value = "父亲节点,不传则为根节点")
+ String pid;
+ @ApiModelProperty(value = "节点名称", required = true)
+ @NotBlank(message = "name 不能为空")
+ String name;
+ Integer order = 0;
+ Integer score = 0;
+// Boolean positive = true;
+}
diff --git a/src/main/java/cn/teammodel/model/dto/weekDuty/UpdateDutyNodeDto.java b/src/main/java/cn/teammodel/model/dto/weekDuty/UpdateDutyNodeDto.java
new file mode 100644
index 0000000..c1c4c6e
--- /dev/null
+++ b/src/main/java/cn/teammodel/model/dto/weekDuty/UpdateDutyNodeDto.java
@@ -0,0 +1,23 @@
+package cn.teammodel.model.dto.weekDuty;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:59
+ */
+@Data
+@ApiModel("覆盖原节点")
+public class UpdateDutyNodeDto {
+ @ApiModelProperty(value = "待修改的节点 id")
+ @NotNull
+ private String id;
+ private String name;
+ private Integer order = 0;
+ private Integer score = 0;
+// boolean positive;
+}
diff --git a/src/main/java/cn/teammodel/model/entity/TmdUserDetail.java b/src/main/java/cn/teammodel/model/entity/TmdUserDetail.java
index 9dc5584..a9a4362 100644
--- a/src/main/java/cn/teammodel/model/entity/TmdUserDetail.java
+++ b/src/main/java/cn/teammodel/model/entity/TmdUserDetail.java
@@ -6,6 +6,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
+
/**
* @author winter
* @create 2023-11-09 15:28
diff --git a/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDuty.java b/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDuty.java
index 8570d3f..fdafe5e 100644
--- a/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDuty.java
+++ b/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDuty.java
@@ -6,21 +6,53 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.util.List;
+
+
/**
- * 值周对象是班级,对应一个评价的指标
+ * 每个学校值周评分标准 (一个学校只有一套)
* @author winter
- * @create 2023-12-29 15:53
+ * @create 2024-01-02 17:05
*/
@EqualsAndHashCode(callSuper = true)
@Container(containerName = "School")
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class WeekDuty extends BaseItem {
- private String classId;
- private String className;
+ private String schoolId;
+ private List nodes;
/**
- * 备注
+ * 打卡地标
*/
- private String note;
- private Long createTime;
+ private List spots;
+
+ @Data
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class DutyTreeNode {
+ private String id;
+ private String pid;
+ private String name;
+ private Integer score;
+ private Integer order;
+ /**
+ * 加分还是减分
+ */
+ private boolean positive;
+ /**
+ * 只有三级有
+ */
+ private String[] path;
+ private List children;
+ private String creator;
+ private String creatorId;
+ private Long createTime;
+ }
+ @Data
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class DutySpot {
+ private String id;
+ private String name;
+ }
+
}
+
diff --git a/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDutyRecord.java b/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDutyRecord.java
new file mode 100644
index 0000000..7ca0e08
--- /dev/null
+++ b/src/main/java/cn/teammodel/model/entity/weekDuty/WeekDutyRecord.java
@@ -0,0 +1,48 @@
+package cn.teammodel.model.entity.weekDuty;
+
+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;
+
+/**
+ * 值周对象是班级,对应一个评价的指标
+ * @author winter
+ * @create 2023-12-29 15:53
+ */
+@EqualsAndHashCode(callSuper = true)
+@Container(containerName = "School")
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class WeekDutyRecord extends BaseItem {
+ private String classId;
+ private String className;
+ private String academicYearId;
+ /**
+ * 班级得分
+ */
+ private Integer score;
+ private List nodes;
+ private Long createTime;
+
+ @Data
+ public static class WeekDutyItem {
+ private String id;
+ private WeekDuty.DutyTreeNode dutyTreeNode;
+ private String spotId;
+ private String spotName;
+ /**
+ * 评分是否传播到学生个人
+ */
+ private boolean spread;
+ private List attachments;
+ /**
+ * 备注
+ */
+ private String note;
+ private Long createTime;
+ }
+}
diff --git a/src/main/java/cn/teammodel/repository/DutyRecordRepository.java b/src/main/java/cn/teammodel/repository/DutyRecordRepository.java
new file mode 100644
index 0000000..dab276c
--- /dev/null
+++ b/src/main/java/cn/teammodel/repository/DutyRecordRepository.java
@@ -0,0 +1,13 @@
+package cn.teammodel.repository;
+
+import cn.teammodel.model.entity.weekDuty.WeekDutyRecord;
+import com.azure.spring.data.cosmos.repository.CosmosRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author winter
+ * @create 2024-01-04 17:23
+ */
+@Repository
+public interface DutyRecordRepository extends CosmosRepository {
+}
diff --git a/src/main/java/cn/teammodel/repository/DutyRepository.java b/src/main/java/cn/teammodel/repository/DutyRepository.java
new file mode 100644
index 0000000..7fc183a
--- /dev/null
+++ b/src/main/java/cn/teammodel/repository/DutyRepository.java
@@ -0,0 +1,21 @@
+package cn.teammodel.repository;
+
+import cn.teammodel.model.entity.weekDuty.WeekDuty;
+import com.azure.spring.data.cosmos.repository.CosmosRepository;
+import com.azure.spring.data.cosmos.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author winter
+ * @create 2024-01-03 15:18
+ */
+@Repository
+public interface DutyRepository extends CosmosRepository {
+ WeekDuty findBySchoolIdAndCode(String schoolId, String code);
+
+ @Query("select value n from c join n in c.nodes where c.schoolId = @schoolId and c.code = 'Duty' and n.id = @nodeId")
+ List findDutyNodeById(String nodeId, String schoolId);
+
+}
diff --git a/src/main/java/cn/teammodel/service/DutyService.java b/src/main/java/cn/teammodel/service/DutyService.java
new file mode 100644
index 0000000..cf89937
--- /dev/null
+++ b/src/main/java/cn/teammodel/service/DutyService.java
@@ -0,0 +1,24 @@
+package cn.teammodel.service;
+
+import cn.teammodel.model.dto.weekDuty.DeleteDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.DutyVoteDto;
+import cn.teammodel.model.dto.weekDuty.InsertDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.UpdateDutyNodeDto;
+import cn.teammodel.model.entity.weekDuty.WeekDuty;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:07
+ */
+public interface DutyService {
+ WeekDuty insertNode(InsertDutyNodeDto insertDutyNodeDto);
+
+ WeekDuty getTree();
+
+ WeekDuty deleteNode(DeleteDutyNodeDto deleteDutyNodeDto);
+
+ WeekDuty updateNode(UpdateDutyNodeDto updateDutyNodeDto);
+
+ void vote(DutyVoteDto dutyVoteDto);
+
+}
diff --git a/src/main/java/cn/teammodel/service/impl/DutyServiceImpl.java b/src/main/java/cn/teammodel/service/impl/DutyServiceImpl.java
new file mode 100644
index 0000000..50233b9
--- /dev/null
+++ b/src/main/java/cn/teammodel/service/impl/DutyServiceImpl.java
@@ -0,0 +1,300 @@
+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.model.dto.weekDuty.DeleteDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.DutyVoteDto;
+import cn.teammodel.model.dto.weekDuty.InsertDutyNodeDto;
+import cn.teammodel.model.dto.weekDuty.UpdateDutyNodeDto;
+import cn.teammodel.model.entity.User;
+import cn.teammodel.model.entity.school.ClassInfo;
+import cn.teammodel.model.entity.school.School;
+import cn.teammodel.model.entity.weekDuty.WeekDuty;
+import cn.teammodel.repository.ClassRepository;
+import cn.teammodel.repository.DutyRecordRepository;
+import cn.teammodel.repository.DutyRepository;
+import cn.teammodel.repository.SchoolRepository;
+import cn.teammodel.security.utils.SecurityUtil;
+import cn.teammodel.service.DutyService;
+import cn.teammodel.utils.SchoolDateUtil;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author winter
+ * @create 2024-01-03 10:08
+ */
+@Service
+public class DutyServiceImpl implements DutyService {
+ @Resource
+ private ClassRepository classRepository;
+ @Resource
+ private SchoolRepository schoolRepository;
+ @Resource
+ private DutyRecordRepository dutyRecordRepository;
+ @Resource
+ private DutyRepository dutyRepository;
+
+ @Override
+ public WeekDuty getTree() {
+ User user = SecurityUtil.getLoginUser();
+ String schoolId = user.getSchoolId();
+
+ WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId , PK.WEEK_DUTY);
+ if (duty == null) {
+ // copy from template
+ duty = dutyRepository.findBySchoolIdAndCode("template", PK.WEEK_DUTY);
+ if (duty == null) {
+ throw new RuntimeException("值周模板不存在");
+ }
+ duty.setId(null);
+ duty.setSchoolId(schoolId);
+ // 刷新值周树和 spots 的 id
+ refreshDuty(duty);
+ dutyRepository.save(duty);
+ }
+
+ return this.buildTree(duty);
+ }
+
+ @Override
+ public WeekDuty insertNode(InsertDutyNodeDto insertDutyNodeDto) {
+ String pid = insertDutyNodeDto.getPid();
+ String name = insertDutyNodeDto.getName();
+ Integer order = insertDutyNodeDto.getOrder();
+ Integer score = insertDutyNodeDto.getScore();
+
+ User user = SecurityUtil.getLoginUser();
+ String schoolId = user.getSchoolId();
+ WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
+ if (duty == null) {
+ throw new ServiceException("值周树不存在");
+ }
+ WeekDuty.DutyTreeNode newNode = new WeekDuty.DutyTreeNode();
+
+ List nodes = duty.getNodes();
+ // 非根节点(需考虑填充 path)
+ if (StringUtils.isNotEmpty(pid)) {
+ WeekDuty.DutyTreeNode parent = nodes.stream().filter(x -> pid.equals(x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "pid 无效"));
+ String level2 = parent.getName();
+ // 新节点是三级节点(二级节点不用特殊处理
+ String ppid = parent.getId();
+ if (ppid != null) {
+ String[] path = new String[3];
+ WeekDuty.DutyTreeNode root = nodes.stream().filter(x -> ppid.equals(x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误"));
+ // 层数不为三级,抛出异常
+ if (root.getPid() != null) {
+ throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误");
+ }
+ String level1 = root.getName();
+ path[0] = level1;
+ path[1] = level2;
+ path[2] = name;
+ newNode.setPath(path);
+ }
+ }
+
+ newNode.setId(UUID.randomUUID().toString());
+ newNode.setPid(pid);
+ newNode.setName(name);
+ newNode.setScore(score);
+ newNode.setOrder(order);
+ newNode.setPositive(score >= 0);
+ newNode.setCreator(user.getName());
+ newNode.setCreatorId(user.getId());
+ newNode.setCreateTime(Instant.now().toEpochMilli());
+ nodes.add(newNode);
+ return buildTree(dutyRepository.save(duty));
+ }
+
+ @Override
+ public WeekDuty deleteNode(DeleteDutyNodeDto deleteDutyNodeDto) {
+ String schoolId = SecurityUtil.getLoginUser().getSchoolId();
+ String nodeToDeleteId = deleteDutyNodeDto.getNodeId();
+
+ WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
+ if (duty == null) {
+ throw new ServiceException("值周树不存在");
+ }
+ List nodes = duty.getNodes();
+ List nodesToDelete = new ArrayList<>();
+ WeekDuty.DutyTreeNode node = nodes.stream()
+ .filter(x -> nodeToDeleteId.equals(x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "删除的节点不存在"));
+
+ nodesToDelete.add(node);
+ this.collectNodesToDelete(nodeToDeleteId, nodes, nodesToDelete);
+ // 批量删除节点
+ nodes.removeAll(nodesToDelete);
+
+ return buildTree(dutyRepository.save(duty));
+ }
+
+ @Override
+ public WeekDuty updateNode(UpdateDutyNodeDto updateDutyNodeDto) {
+ String nodeId = updateDutyNodeDto.getId();
+ String newNodeName = updateDutyNodeDto.getName();
+ Integer newScore = updateDutyNodeDto.getScore();
+
+ String schoolId = SecurityUtil.getLoginUser().getSchoolId();
+ WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
+ if (duty == null) {
+ throw new ServiceException("值周树不存在");
+ }
+ List nodes = duty.getNodes();
+ WeekDuty.DutyTreeNode nodeToUpdate = nodes.stream().filter(x -> nodeId.equals(x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new ServiceException("待更新的节点不存在"));
+ String originNodeName = nodeToUpdate.getName();
+ // 更新当前节点
+ nodeToUpdate.setName(newNodeName);
+ nodeToUpdate.setScore(newScore);
+ nodeToUpdate.setOrder(updateDutyNodeDto.getOrder());
+ nodeToUpdate.setPositive(newScore >= 0);
+
+ // 如果是一级,二级需同步更新三级节点的 path
+ if (nodeToUpdate.getPath() != null) {
+ int index = nodeToUpdate.getPid() == null ? 0 : 1;
+ nodes.forEach(x -> {
+ // 修改三级节点的 path
+ if (x.getPath() == null) {
+ return;
+ }
+ String[] path = x.getPath();
+ if (originNodeName.equals(path[index])) {
+ path[index] = newNodeName;
+ x.setPath(path);
+ }
+ });
+ }
+
+ return buildTree(dutyRepository.save(duty));
+ }
+
+ @Override
+ public void vote(DutyVoteDto dutyVoteDto) {
+ String classId = dutyVoteDto.getClassId();
+ String spotId = dutyVoteDto.getSpotId();
+ String dutyNodeId = dutyVoteDto.getDutyNodeId();
+ String note = dutyVoteDto.getNote();
+ String[] attachments = dutyVoteDto.getAttachments();
+
+ String schoolId = SecurityUtil.getLoginUser().getSchoolId();
+ ClassInfo classInfo = classRepository.findClassByIdAndCode(classId, String.format(PK.CLASS, schoolId));
+ if (classInfo == null) {
+ throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "目标班级不存在");
+ }
+ String className = classInfo.getName();
+ String periodId = classInfo.getPeriodId();
+ List semesters = schoolRepository.findSemestersById(schoolId, periodId);
+ if (ObjectUtils.isEmpty(semesters)) {
+ throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "内部结构异常");
+ }
+ String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
+ WeekDuty duty = dutyRepository.findBySchoolIdAndCode(schoolId, PK.WEEK_DUTY);
+ WeekDuty.DutyTreeNode dutyNode = duty.getNodes().stream()
+ .filter(x -> dutyNodeId.equals(x.getId()))
+ .findFirst()
+ .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价节点不存在"));
+
+
+
+ }
+
+ /**
+ * 刷新值周树和 spots 的 id
+ */
+ private void refreshDuty(WeekDuty duty) {
+ List nodes = duty.getNodes();
+ List spots = duty.getSpots();
+ // refresh tree nodes
+ nodes.forEach(x -> {
+ String oldId = x.getId();
+ String newId = UUID.randomUUID().toString();
+ x.setId(newId);
+ // 将孩子节点的 pid 改为新的 id
+ nodes.stream().filter(y -> oldId.equals(y.getPid())).forEach(y -> y.setPid(newId));
+ });
+
+ // refresh spot id
+ spots.forEach(x -> x.setId(UUID.randomUUID().toString()));
+ }
+
+ /**
+ * 递归收集要删除的节点
+ */
+ private void collectNodesToDelete(String nodeToDeleteId, List nodes, List nodesToDelete) {
+ List children = nodes.stream()
+ .filter(x -> nodeToDeleteId.equals(x.getPid()))
+ .collect(Collectors.toList());
+ if (children.size() == 0) return;
+ nodesToDelete.addAll(children);
+ for (WeekDuty.DutyTreeNode child : children) {
+ this.collectNodesToDelete(child.getId(), nodes, nodesToDelete);
+ }
+ }
+
+ public WeekDuty buildTree(WeekDuty weekDuty) {
+ if (weekDuty == null) {
+ return null;
+ }
+
+ List nodes = weekDuty.getNodes();
+ List parents = new ArrayList<>();
+
+ // pid 为 null 或者 "" 则为 parents
+ for (WeekDuty.DutyTreeNode node : nodes) {
+ if (StringUtils.isBlank(node.getPid())) {
+ parents.add(node);
+ }
+ }
+ // 迭代构建孩子节点
+ for (WeekDuty.DutyTreeNode parent : parents) {
+ buildChildren(parent, nodes);
+ }
+ // 根据 order 排序
+ parents = parents.stream().sorted(Comparator.comparing(WeekDuty.DutyTreeNode::getOrder)).collect(Collectors.toList());
+ weekDuty.setNodes(parents);
+ return weekDuty;
+ }
+
+ private void buildChildren(WeekDuty.DutyTreeNode parent, List nodes) {
+ List children = getChildren(parent, nodes);
+ if (children.size() == 0) return;
+
+ parent.setChildren(children);
+ for (WeekDuty.DutyTreeNode child : children) {
+ // 如果子节点还有孩子的话,就继续递归构建
+ if (getChildren(child, nodes).size() > 0) {
+ buildChildren(child, nodes);
+ }
+ }
+
+ }
+
+ private List getChildren(WeekDuty.DutyTreeNode parent, List nodes) {
+ List children = new ArrayList<>();
+ for (WeekDuty.DutyTreeNode node : nodes) {
+ if (StringUtils.isNotBlank(node.getPid()) && node.getPid().equals(parent.getId())) {
+ children.add(node);
+ }
+ }
+ return children;
+ }
+}
diff --git a/src/main/java/cn/teammodel/service/impl/EvaluationServiceImpl.java b/src/main/java/cn/teammodel/service/impl/EvaluationServiceImpl.java
index 5e1694c..cabc2d6 100644
--- a/src/main/java/cn/teammodel/service/impl/EvaluationServiceImpl.java
+++ b/src/main/java/cn/teammodel/service/impl/EvaluationServiceImpl.java
@@ -84,7 +84,7 @@ public class EvaluationServiceImpl implements EvaluationService {
if (appraise == null) {
appraise = RepositoryUtil.findOne(appraiseRepository.findTemplateTree(), "获取模板评价树失败");
if (appraise == null) {
- throw new ServiceException();
+ throw new ServiceException("模板评价树不存在");
}
// refresh
refreshAppraiseTree(appraise);
@@ -173,6 +173,7 @@ public class EvaluationServiceImpl implements EvaluationService {
AppraiseTreeNode newNode = new AppraiseTreeNode();
BeanUtils.copyProperties(insertNodeDto, newNode);
+ // todo: build path
String newNodePid = newNode.getPid();
// 根节点直接添加
if (StringUtils.isNotEmpty(newNodePid)) {
@@ -201,13 +202,12 @@ public class EvaluationServiceImpl implements EvaluationService {
.filter(item -> updateNodeId.equals(item.getId()))
.findFirst()
.orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "更新节点不存在"));
- // 更新字段,考虑直接 copy properties
+ // todo: 如果是一级,二级需同步更新三级节点的 path
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));
}
@@ -219,13 +219,14 @@ public class EvaluationServiceImpl implements EvaluationService {
List nodes = appraise.getNodes();
List nodesToDelete = new ArrayList<>();
- // 迭代器安全删除
+ // 收集需要删除的节点
for (AppraiseTreeNode node : nodes) {
if (node.getId().equals(deleteNodeDto.getId())) {
// 删除当前节点
nodesToDelete.add(node);
// 递归删除其孩子节点
this.collectNodesToDelete(node.getId(), nodes, nodesToDelete);
+ break;
}
}
if (ObjectUtils.isEmpty(nodesToDelete)) {
diff --git a/src/test/java/cn/teammodel/Scripts.java b/src/test/java/cn/teammodel/Scripts.java
new file mode 100644
index 0000000..749525d
--- /dev/null
+++ b/src/test/java/cn/teammodel/Scripts.java
@@ -0,0 +1,27 @@
+package cn.teammodel;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author winter
+ * @create 2024-01-03 16:21
+ */
+@SpringBootTest
+public class Scripts {
+
+
+ @Test
+ public void genDocFromJson() throws IOException {
+ String path = "C://Users/Administrator/Desktop/data.json";
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode root = objectMapper.readTree(new File(path));
+ System.out.println(root.get("content"));
+
+ }
+}