Browse Source

feat: "第二次部署尝试"

Leonezhurui 2 years ago
parent
commit
0e6268f5b7
33 changed files with 1204 additions and 20 deletions
  1. 22 13
      .drone.yml
  2. 2 2
      Dockerfile
  3. 16 3
      deploy.sh
  4. 6 0
      pom.xml
  5. 78 0
      src/main/java/com/njuzr/eaibackend/controller/AIController.java
  6. 78 0
      src/main/java/com/njuzr/eaibackend/controller/AssignmentController.java
  7. 125 0
      src/main/java/com/njuzr/eaibackend/controller/CourseController.java
  8. 18 0
      src/main/java/com/njuzr/eaibackend/dto/AIDTO.java
  9. 30 0
      src/main/java/com/njuzr/eaibackend/dto/AssignmentDTO.java
  10. 41 0
      src/main/java/com/njuzr/eaibackend/dto/CourseDTO.java
  11. 15 0
      src/main/java/com/njuzr/eaibackend/dto/EnrollDTO.java
  12. 37 0
      src/main/java/com/njuzr/eaibackend/enums/AssignmentStatus.java
  13. 21 0
      src/main/java/com/njuzr/eaibackend/mapper/AIDialogueMapper.java
  14. 15 0
      src/main/java/com/njuzr/eaibackend/mapper/AssignmentMapper.java
  15. 15 0
      src/main/java/com/njuzr/eaibackend/mapper/CourseMapper.java
  16. 39 0
      src/main/java/com/njuzr/eaibackend/po/AIDialogue.java
  17. 19 0
      src/main/java/com/njuzr/eaibackend/po/AIEntry.java
  18. 42 0
      src/main/java/com/njuzr/eaibackend/po/Assignment.java
  19. 42 0
      src/main/java/com/njuzr/eaibackend/po/Course.java
  20. 25 0
      src/main/java/com/njuzr/eaibackend/po/Enrollment.java
  21. 23 0
      src/main/java/com/njuzr/eaibackend/po/StudentAssignment.java
  22. 166 0
      src/main/java/com/njuzr/eaibackend/service/AIDialogueService.java
  23. 90 0
      src/main/java/com/njuzr/eaibackend/service/AIRequestService.java
  24. 14 0
      src/main/java/com/njuzr/eaibackend/service/CourseService.java
  25. 73 0
      src/main/java/com/njuzr/eaibackend/service/impl/CourseServiceImpl.java
  26. 28 0
      src/main/java/com/njuzr/eaibackend/utils/ConvertUtil.java
  27. 16 0
      src/main/java/com/njuzr/eaibackend/utils/ModelMapperUtil.java
  28. 15 0
      src/main/java/com/njuzr/eaibackend/utils/WebClientUtil.java
  29. 17 0
      src/main/java/com/njuzr/eaibackend/vo/AIDialogueVO.java
  30. 39 0
      src/main/java/com/njuzr/eaibackend/vo/CourseVO.java
  31. 10 0
      src/main/resources/application.yaml
  32. 2 2
      src/main/resources/mapper/UserMapper.xml
  33. 25 0
      src/test/java/com/njuzr/eaibackend/service/AIRequestServiceTest.java

+ 22 - 13
.drone.yml

@@ -23,19 +23,28 @@ steps:
 
   # 步骤 3: 调用脚本进行部署
   - name: deploy-with-script
-    image: alpine/git  # 使用包含 git 的镜像来确保可以执行脚本
-    commands:
-      - chmod +x ./deploy.sh  # 确保脚本具有执行权限
-      - ./deploy.sh  # 执行脚本
+    image: docker
     environment:
-      IMAGE_TAG: registry.cn-hangzhou.aliyuncs.com/english-ai/eai-backend:latest
-    volumes:
-      - name: docker_socket
-        path: /var/run/docker.sock
+      CONTAINER_NAME: eai-backend
+      DOCKER_USERNAME: leonezhurui
+      DOCKER_REGISTRY: registry.cn-hangzhou.aliyuncs.com
+      IMAGE_NAME: english-ai/eai-backend
+      # 假设这个标签是你想要部署的标签,可以根据实际情况修改
+      IMAGE_TAG: latest
+    commands:
+      - docker login --username=$DOCKER_USERNAME $DOCKER_REGISTRY
+      - tag=$DOCKER_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
+      - docker pull $tag
+      - |
+        if [ $(docker ps -aq -f name=^/${CONTAINER_NAME}$) ]; then
+            docker rm -f ${CONTAINER_NAME}
+        fi
+      - docker run --name ${CONTAINER_NAME} -d $tag -p 8081:80
+    when:
+      event:
+        - push
 
 
-# 为了让 Drone 能够执行 Docker 命令,需要挂载 Docker 守护进程的 socket
-volumes:
-  - name: docker_socket
-    host:
-      path: /var/run/docker.sock
+trigger:
+  branch:
+    - main

+ 2 - 2
Dockerfile

@@ -1,6 +1,6 @@
 FROM openjdk:11-jre-slim
 COPY target/*.jar /app/app.jar
-COPY src/main/resources/application.yml /app/application.yml
+COPY src/main/resources/application.yaml /app/application.yaml
 WORKDIR /app
-CMD ["java", "-jar", "/app/app.jar", "-Duser.timezone=Asia/Shanghai", "--spring.config.location=/app/application.yml"]
+CMD ["java", "-jar", "/app/app.jar", "-Duser.timezone=Asia/Shanghai", "--spring.config.location=/app/application.yaml"]
 EXPOSE 8080

+ 16 - 3
deploy.sh

@@ -1,7 +1,20 @@
 set -xeu
 cd `dirname $0`
-docker run --rm -it -v $PWD:/app -v ~/.m2:/root/.m2 -w /app maven:3.8.6-openjdk-11-slim mvn package -DskipTests
+
+container_name="eai-backend"
+
+# 检查是否存在同名容器,如果存在,则删除
+if [ $(docker ps -aq -f name=^/${container_name}$) ]; then
+    echo "Removing existing container named ${container_name}"
+    docker rm -f ${container_name}
+fi
+
+# 拉取最新的镜像
+echo "Pulling latest image for ${tag}"
 docker login --username=leonezhurui registry.cn-hangzhou.aliyuncs.com
 tag=registry.cn-hangzhou.aliyuncs.com/english-ai/eai-backend:`git symbolic-ref --short -q HEAD`
-docker build -t $tag .
-docker push $tag
+docker pull ${tag}
+
+# 运行新的容器
+echo "Running new container from ${tag}"
+docker run --name ${container_name} -d ${tag} -p 8081:80

+ 6 - 0
pom.xml

@@ -138,6 +138,12 @@
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
 
+        <!-- MongoDB依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-mongodb</artifactId>
+        </dependency>
+
 
 
 

+ 78 - 0
src/main/java/com/njuzr/eaibackend/controller/AIController.java

@@ -0,0 +1,78 @@
+package com.njuzr.eaibackend.controller;
+
+import com.njuzr.eaibackend.dto.AIDTO;
+import com.njuzr.eaibackend.po.AIDialogue;
+import com.njuzr.eaibackend.po.AIEntry;
+import com.njuzr.eaibackend.service.AIDialogueService;
+import com.njuzr.eaibackend.vo.AIDialogueVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.method.P;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/1 - 23:36
+ * @Package: EAI-Backend
+ */
+
+@Slf4j
+@RestController
+@RequestMapping("/api/ai")
+public class AIController {
+    private final AIDialogueService aiDialogueService;
+
+    @Autowired
+    public AIController(AIDialogueService aiDialogueService) {
+        this.aiDialogueService = aiDialogueService;
+    }
+
+    @PreAuthorize("hasRole('ROLE_STUDENT') or hasRole('ROLE_TEACHER')")
+    @GetMapping
+    public MyResponse getDialogues(
+            @RequestParam Long assignmentId,
+            @RequestParam Long userId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "20") int size
+
+    ) {
+        AIDialogueVO dialogues = aiDialogueService.getAIDialogue(assignmentId, userId, page, size);
+        return MyResponse.success(dialogues);
+    }
+
+    /**
+     * 学生进入写作界面触发,创建AI会话
+     * @param assignmentId
+     * @param userId
+     * @return
+     */
+    @PreAuthorize("hasRole('ROLE_STUDENT')")
+    @PostMapping
+    public MyResponse createDialogue(
+            @RequestParam Long assignmentId,
+            @AuthenticationPrincipal(expression = "id") Long userId
+    ) {
+        aiDialogueService.createAIDialogue(assignmentId, userId);
+        return MyResponse.success("创建AI会话成功");
+    }
+
+    /**
+     * 学生请求AI:1WebClient请求,获得结果;2存储+返回
+     * @param dialogueId
+     * @param aidto
+     * @return
+     */
+    @PreAuthorize("hasRole('ROLE_STUDENT')")
+    @PutMapping
+    public MyResponse requestAI(
+            @RequestParam String dialogueId,
+            @RequestBody AIDTO aidto
+            ) {
+        log.info("解析参数为{}, AI会话ID为{}", aidto.toString(), dialogueId);
+        AIEntry entry = aiDialogueService.requestAI(dialogueId, aidto);
+        return MyResponse.success(entry);
+    }
+}

+ 78 - 0
src/main/java/com/njuzr/eaibackend/controller/AssignmentController.java

@@ -0,0 +1,78 @@
+package com.njuzr.eaibackend.controller;
+
+import com.njuzr.eaibackend.dto.AssignmentDTO;
+import com.njuzr.eaibackend.enums.AssignmentStatus;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 20:01
+ * @Package: EAI-Backend
+ */
+
+@RestController
+@RequestMapping("/api/assignment")
+public class AssignmentController {
+
+    /**
+     * 创建作业,在某一个课程下创建作业
+     * @param courseId
+     * @param assignmentDTO
+     * @return
+     */
+    @PostMapping
+    public MyResponse createAssignment(
+            @RequestParam Long courseId,
+            @RequestBody AssignmentDTO assignmentDTO
+    ) {
+        return MyResponse.success("");
+
+    }
+
+    @GetMapping("/byCourse/{courseId}")
+    public MyResponse getAssignmentsByCourseId(
+            @PathVariable Long courseId
+    ) {
+        return MyResponse.success("");
+    }
+
+    @GetMapping("/{assignmentId}")
+    public MyResponse getAssignmentById(@PathVariable Long assignmentId) {
+        return MyResponse.success("");
+    }
+
+    @PutMapping
+    public MyResponse updateAssignment(@RequestBody AssignmentDTO assignmentDTO) {
+        return MyResponse.success("");
+    }
+
+    @DeleteMapping("/{assignmentId}")
+    public MyResponse deleteAssignment(@PathVariable Long assignmentId) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 获取考生作业结果,包括成绩和老师评语
+     * @param studentId
+     * @param assignmentId
+     * @return
+     */
+    @GetMapping("/result")
+    public MyResponse getResult(@RequestParam Long studentId, @RequestParam Long assignmentId) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 更新状态(需要多种情况讨论)
+     * @param assignmentId
+     * @param status
+     * @return
+     */
+    @PutMapping("/updateStatus")
+    public MyResponse updateStatus(
+            @RequestParam Long assignmentId,
+            @RequestParam AssignmentStatus status) {
+        return MyResponse.success("");
+    }
+
+}

+ 125 - 0
src/main/java/com/njuzr/eaibackend/controller/CourseController.java

@@ -0,0 +1,125 @@
+package com.njuzr.eaibackend.controller;
+
+import com.njuzr.eaibackend.dto.CourseDTO;
+import com.njuzr.eaibackend.dto.EnrollDTO;
+import com.njuzr.eaibackend.service.CourseService;
+import com.njuzr.eaibackend.vo.CourseVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 16:34
+ * @Package: EAI-Backend
+ */
+
+@RestController
+@RequestMapping("/api/course")
+public class CourseController {
+    private final CourseService courseService;
+
+    @Autowired
+    public CourseController(CourseService courseService) {
+        this.courseService = courseService;
+    }
+
+
+    /**
+     * 分页返回所有的课程信息
+     * @return
+     */
+    @GetMapping("/findAll")
+    public MyResponse findAllCourses(
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size
+    ) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 根据学生Id获取其所选的所有课程的信息
+     * @param studentId
+     * @return
+     */
+    @GetMapping("/byStudent/{studentId}")
+    public MyResponse findByStudentId(
+            @PathVariable Long studentId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size
+    ) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 根据老师Id获取的其所创建的所有课程信息
+     * @param teacherId
+     * @return
+     */
+    @PreAuthorize("hasRole('TEACHER')")
+    @GetMapping("/byTeacher/{teacherId}")
+    public MyResponse findByTeacherId(
+            @PathVariable Long teacherId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size
+    ) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 根据课程Id,获取某个课程的详细信息
+     * @param courseId
+     * @param page
+     * @param size
+     * @return
+     */
+    @GetMapping("/{courseId}")
+    public MyResponse findByCourseId(
+            @PathVariable Long courseId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size
+    ) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 创建课程
+     * @param courseDTO
+     * @return
+     */
+    @PreAuthorize("hasRole('TEACHER') or hasRole('ADMIN')")
+    @PostMapping
+    public MyResponse createCourse(@Validated @RequestBody CourseDTO courseDTO) {
+        CourseVO courseVO = courseService.createCourse(courseDTO);
+        return MyResponse.success(courseVO);
+    }
+
+    @PutMapping
+    public MyResponse updateCourse(@RequestBody CourseDTO courseDTO) {
+        return MyResponse.success("");
+    }
+
+    @PutMapping("/enrollCode")
+    public MyResponse updateEnrollCode(@RequestParam String enrollCode) {
+        return MyResponse.success("");
+    }
+
+    @DeleteMapping
+    public MyResponse deleteCourse(@RequestParam Long courseId) {
+        return MyResponse.success("");
+    }
+
+    /**
+     * 选课,通过studentId和courseId,完成选课
+     * @param enrollDTO
+     * @return
+     */
+    @PostMapping("/enroll")
+    public MyResponse enroll(@RequestBody EnrollDTO enrollDTO) {
+        return MyResponse.success("");
+    }
+
+
+
+}

+ 18 - 0
src/main/java/com/njuzr/eaibackend/dto/AIDTO.java

@@ -0,0 +1,18 @@
+package com.njuzr.eaibackend.dto;
+
+import com.njuzr.eaibackend.po.AIEntry;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/2 - 00:28
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class AIDTO {
+    private String model; // 设置AI类别,目前支持ChatGLM3、Qwen
+    private List<AIEntry> messages;
+}

+ 30 - 0
src/main/java/com/njuzr/eaibackend/dto/AssignmentDTO.java

@@ -0,0 +1,30 @@
+package com.njuzr.eaibackend.dto;
+
+import com.njuzr.eaibackend.enums.AssignmentStatus;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 20:03
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class AssignmentDTO {
+    private String assignmentName;
+
+    private String description;
+
+    private String descriptionFile; // 作业要求,Word/Pdf文件
+
+    private List<String> attachments; // 作业附件
+
+    private Long publisher; // 发布作业的老师的Id
+
+    private Date startTime;
+
+    private Date endTime;
+}

+ 41 - 0
src/main/java/com/njuzr/eaibackend/dto/CourseDTO.java

@@ -0,0 +1,41 @@
+package com.njuzr.eaibackend.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 16:57
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class CourseDTO {
+    @NotNull(message = "课程名不能缺失")
+    private String courseName;
+
+    @NotNull(message = "老师Id不能缺失")
+    private List<Long> teacherIds;
+
+    private List<String> courseImages;
+
+    private String targetGrade;
+
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    @NotNull(message = "起始时间不能缺失")
+    private Date startTime;
+
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    @NotNull(message = "截止时间不能缺失")
+    private Date endTime;
+
+    @NotNull(message = "课程描述不能缺失")
+    private String description;
+
+    @NotNull(message = "选课码不能缺失")
+    private String enrollCode; // 选课码
+}

+ 15 - 0
src/main/java/com/njuzr/eaibackend/dto/EnrollDTO.java

@@ -0,0 +1,15 @@
+package com.njuzr.eaibackend.dto;
+
+import lombok.Data;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 19:58
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class EnrollDTO {
+    private Long studentId;
+    private Long courseId;
+}

+ 37 - 0
src/main/java/com/njuzr/eaibackend/enums/AssignmentStatus.java

@@ -0,0 +1,37 @@
+package com.njuzr.eaibackend.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/4 - 22:55
+ * @Package: EAI-Backend
+ */
+
+public enum AssignmentStatus {
+    NOT_EDITED(0, "NOT_EDITED"), // 待完成,还未进行写作页面进行编辑
+    NOT_SUBMITTED(1, "NOT_SUBMITTED"), // 未提交,正在完成作业中
+    SUBMITTED(2, "SUBMITTED"), // 已提交,完成编辑并提交内容
+    NOT_DONE(3, "NOT_DONE"), // 未完成,到了DDL,仍然没有提交的
+    PUBLISHED(4, "PUBLISHED"), // 老师已批改并发布
+    NOT_CORRECTED(5, "NOT_CORRECTED"), //未批改(状态仅老师可见)
+    CORRECTED(6, "CORRECTED") //已批改、但还未发布(状态仅老师可见)
+    ;
+
+    @EnumValue
+    private final int code;
+    private final String status;
+
+    AssignmentStatus(int code, String status) {
+        this.code = code;
+        this.status = status;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+}

+ 21 - 0
src/main/java/com/njuzr/eaibackend/mapper/AIDialogueMapper.java

@@ -0,0 +1,21 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.njuzr.eaibackend.po.AIDialogue;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.data.mongodb.repository.Query;
+
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/3 - 11:06
+ * @Package: EAI-Backend
+ */
+
+public interface AIDialogueMapper extends MongoRepository<AIDialogue, Long> {
+    Page<AIDialogue> findByAssignmentIdAndUserId(Long assignmentId, Long userId, Pageable pageable);
+
+}

+ 15 - 0
src/main/java/com/njuzr/eaibackend/mapper/AssignmentMapper.java

@@ -0,0 +1,15 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.Assignment;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 16:32
+ * @Package: EAI-Backend
+ */
+
+@Mapper
+public interface AssignmentMapper extends BaseMapper<Assignment> {
+}

+ 15 - 0
src/main/java/com/njuzr/eaibackend/mapper/CourseMapper.java

@@ -0,0 +1,15 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.Course;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 16:31
+ * @Package: EAI-Backend
+ */
+
+@Mapper
+public interface CourseMapper extends BaseMapper<Course> {
+}

+ 39 - 0
src/main/java/com/njuzr/eaibackend/po/AIDialogue.java

@@ -0,0 +1,39 @@
+package com.njuzr.eaibackend.po;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.redis.core.index.Indexed;
+
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/3 - 10:33
+ * @Package: EAI-Backend
+ */
+
+@Data
+@Document(collection = "aiDialogues")
+public class AIDialogue {
+    @Id
+    private String id;
+
+    @Indexed
+    private Long assignmentId; // 外键
+
+    @Indexed
+    private Long userId; // 外键,需要建立(assignmentId, userId)外键索引
+
+    private List<DialogueEntry> dialogues;
+
+
+    @Data
+    @AllArgsConstructor
+    public static class DialogueEntry {
+        private String role;
+        private String content;
+        private Long timestamp;
+    }
+}

+ 19 - 0
src/main/java/com/njuzr/eaibackend/po/AIEntry.java

@@ -0,0 +1,19 @@
+package com.njuzr.eaibackend.po;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/3 - 22:30
+ * @Package: EAI-Backend
+ */
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AIEntry {
+    private String role;
+    private String content;
+}

+ 42 - 0
src/main/java/com/njuzr/eaibackend/po/Assignment.java

@@ -0,0 +1,42 @@
+package com.njuzr.eaibackend.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njuzr.eaibackend.enums.AssignmentStatus;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/4 - 22:21
+ * @Package: EAI-Backend
+ */
+
+
+@Data
+@TableName("assignment")
+public class Assignment {
+    @TableId(type = IdType.AUTO)
+    private Long assignmentId;
+
+    private String assignmentName;
+
+    private String description;
+
+    private String descriptionFile; // 作业要求,Word/Pdf文件
+
+    private List<String> attachments; // 作业附件
+
+    private Long publisher; // 发布作业的老师的Id
+
+    private Date startTime;
+
+    private Date endTime;
+
+    private Date createTime;
+
+    private AssignmentStatus status;
+}

+ 42 - 0
src/main/java/com/njuzr/eaibackend/po/Course.java

@@ -0,0 +1,42 @@
+package com.njuzr.eaibackend.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/4 - 22:21
+ * @Package: EAI-Backend
+ */
+
+@Data
+@TableName("course")
+public class Course {
+    @TableId(type = IdType.AUTO)
+    private Long courseId;
+
+    private String courseName;
+
+    @TableField("teacher_ids")
+    private String teacherIds; // 序列化好的字符串
+
+    @TableField("course_imgs")
+    private String courseImages; // 序列化好的字符串
+
+    private String targetGrade;
+
+    private Date startTime;
+
+    private Date endTime;
+
+    private Date createTime;
+
+    private String description;
+
+}

+ 25 - 0
src/main/java/com/njuzr/eaibackend/po/Enrollment.java

@@ -0,0 +1,25 @@
+package com.njuzr.eaibackend.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/4 - 23:04
+ * @Package: EAI-Backend
+ */
+
+
+@Data
+@TableName("course_student")
+public class Enrollment {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long courseId;
+
+    private Long userId;
+}

+ 23 - 0
src/main/java/com/njuzr/eaibackend/po/StudentAssignment.java

@@ -0,0 +1,23 @@
+package com.njuzr.eaibackend.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/4 - 23:21
+ * @Package: EAI-Backend
+ */
+
+@Data
+@TableName("student_assignment")
+public class StudentAssignment {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long assignmentId;
+    private Long userId;
+    private Double score;
+    private String remark;
+}

+ 166 - 0
src/main/java/com/njuzr/eaibackend/service/AIDialogueService.java

@@ -0,0 +1,166 @@
+package com.njuzr.eaibackend.service;
+
+import com.njuzr.eaibackend.dto.AIDTO;
+import com.njuzr.eaibackend.exception.MyException;
+import com.njuzr.eaibackend.mapper.AIDialogueMapper;
+import com.njuzr.eaibackend.po.AIDialogue;
+import com.njuzr.eaibackend.po.AIEntry;
+import com.njuzr.eaibackend.utils.ModelMapperUtil;
+import com.njuzr.eaibackend.vo.AIDialogueVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.*;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.*;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/3 - 11:29
+ * @Package: EAI-Backend
+ */
+
+@Slf4j
+@Service
+public class AIDialogueService {
+    private final MongoTemplate mongoTemplate;
+
+    private final AIDialogueMapper aiDialogueMapper;
+
+    private final AIRequestService aiRequestService;
+
+    @Autowired
+    public AIDialogueService(MongoTemplate mongoTemplate, AIDialogueMapper aiDialogueMapper, AIRequestService aiRequestService) {
+        this.mongoTemplate = mongoTemplate;
+        this.aiDialogueMapper = aiDialogueMapper;
+        this.aiRequestService = aiRequestService;
+    }
+
+    /**
+     * 创建新的AI会话,在学生点击进行写作界面时会自动触发
+     * @param assignmentId
+     * @param userId
+     */
+    public void createAIDialogue(Long assignmentId, Long userId) {
+        if (aiDialogueMapper.findByAssignmentIdAndUserId(assignmentId, userId, PageRequest.of(0,1)).hasContent()) {
+            throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"AI会话已存在");
+        }
+        AIDialogue aiDialogue = new AIDialogue();
+        aiDialogue.setAssignmentId(assignmentId);
+        aiDialogue.setUserId(userId);
+        aiDialogue.setDialogues(new ArrayList<>());
+        aiDialogueMapper.save(aiDialogue);
+    }
+
+    /**
+     * 分页返回查找内容
+     * @param assignmentId
+     * @param userId
+     * @param page
+     * @param size
+     * @return
+     */
+    public AIDialogueVO getAIDialogue(Long assignmentId, Long userId, int page, int size) {
+
+        AIDialogue targetDialogue = mongoTemplate.findOne(
+                Query.query(Criteria.where("assignmentId").is(assignmentId).and("userId").is(userId)), AIDialogue.class
+        );
+
+        if (targetDialogue == null) {
+            throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"找不到AI会话");
+        }
+
+        Pageable pageable = PageRequest.of(page, size);
+
+        List<AIDialogue.DialogueEntry> sortedDialogues = targetDialogue.getDialogues();
+        sortedDialogues.sort((d1, d2) -> d2.getTimestamp().compareTo(d1.getTimestamp()));
+
+        // 手动处理dialogues数组的分页逻辑
+        int start = (int) pageable.getOffset();
+        int end = Math.min((start + pageable.getPageSize()), sortedDialogues.size());
+
+        if (start > sortedDialogues.size()) {
+            start = end = sortedDialogues.size();
+        }
+
+        List<AIDialogue.DialogueEntry> pagedDialogues = sortedDialogues.subList(start, end);
+
+        List<AIEntry> entries = pagedDialogues.stream().map(entry -> ModelMapperUtil.map(entry, AIEntry.class)).collect(Collectors.toList());
+
+        AIDialogueVO aiDialogueVO = new AIDialogueVO();
+        aiDialogueVO.setDialogueId(targetDialogue.getId());
+
+        aiDialogueVO.setMessages(new PageImpl<>(entries, pageable, targetDialogue.getDialogues().size()));
+
+        // 返回包含分页dialogues和总条目数的PageImpl
+        return aiDialogueVO;
+    }
+
+    public AIEntry requestAI(String dialogueId, AIDTO aidto) {
+        // 1 使用WebClient请求数据
+        String modelType = aidto.getModel();
+        List<AIEntry> entries = aidto.getMessages();
+        if (entries.size() > 11) {
+            // TODO 限制上下文数量
+        }
+
+        AIRequestService.AIResponse res;
+        if (Objects.equals(modelType, "ChatGLM3")) {
+            res = aiRequestService.requestChatGLM(aidto.getMessages());
+        } else if (modelType.equals("QWen14B")) {
+            res = aiRequestService.requestQWen(aidto.getMessages());
+        } else {
+            throw new MyException(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase()+":"+"模型不支持");
+        }
+
+        // 2 更新数据库
+        AIEntry lastEntry = aidto.getMessages().get(aidto.getMessages().size()-1);
+        AIDialogue.DialogueEntry userEntry = new AIDialogue.DialogueEntry(lastEntry.getRole(), lastEntry.getContent(), System.currentTimeMillis() / 1000);
+
+        String retContent;
+        String role;
+        Long timestamp;
+        try {
+            retContent = res.getChoices().get(0).getMessage().getContent();
+            role = res.getChoices().get(0).getMessage().getRole();
+            timestamp = res.getCreated();
+        } catch (Exception e) {
+            log.error("AI请求返回的数据内容有误,数据为{}", res);
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"请求失败");
+        }
+
+        AIDialogue.DialogueEntry aiEntry = new AIDialogue.DialogueEntry(role, retContent, timestamp);
+
+        addDialogueEntry(dialogueId, userEntry, aiEntry);
+
+        return new AIEntry(role, retContent);
+    }
+
+
+    /**
+     * 更新MongoDB数据;通过$push的方式,减少更新的开销;请求成功则会一次性更新两条数据,如果不成功,则两条都不插入。
+     * @param dialogueId
+     * @param userEntry
+     * @param aiEntry
+     */
+    private void addDialogueEntry(String dialogueId, AIDialogue.DialogueEntry userEntry, AIDialogue.DialogueEntry aiEntry) {
+        try {
+            Query query =  new Query(Criteria.where("id").is(dialogueId));
+            Update update = new Update().push("dialogues").each(userEntry, aiEntry);
+            mongoTemplate.updateFirst(query, update, AIDialogue.class);
+        } catch (Exception e) {
+            log.error("MongoDB数据库更新出错");
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"数据库更新失败");
+        }
+
+    }
+}

+ 90 - 0
src/main/java/com/njuzr/eaibackend/service/AIRequestService.java

@@ -0,0 +1,90 @@
+package com.njuzr.eaibackend.service;
+
+import com.njuzr.eaibackend.exception.MyException;
+import com.njuzr.eaibackend.po.AIEntry;
+import com.njuzr.eaibackend.utils.WebClientUtil;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/2 - 00:00
+ * @Package: EAI-Backend
+ */
+
+@Slf4j
+@Service
+public class AIRequestService {
+    private final WebClientUtil chatglmClient = new WebClientUtil("http://10.58.0.2:6678");
+    private final WebClientUtil qwenClient = new WebClientUtil("http://10.58.0.2:6679");
+
+    private final String prefix = "/v1/chat/completions";
+
+    public AIResponse requestChatGLM(List<AIEntry> messages) {
+        MyRequestObject requestObject = new MyRequestObject("ChatGLM3", messages);
+
+        try {
+            return chatglmClient.post(prefix, requestObject, AIResponse.class);
+        } catch (Exception e) {
+            log.error("WebClient请求失败,AI请求失败~");
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"服务器请求AI出错");
+        }
+    }
+
+    public AIResponse requestQWen(List<AIEntry> messages) {
+        MyRequestObject requestObject = new MyRequestObject("QWen14B", messages);
+
+        try {
+            return qwenClient.post(prefix, requestObject, AIResponse.class);
+        } catch (Exception e) {
+            log.error("WebClient请求失败,AI请求失败~");
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"服务器请求AI出错");
+        }
+    }
+
+    @Data
+    @AllArgsConstructor
+    static class MyRequestObject {
+        private String model;
+        private List<AIEntry> messages;
+    }
+
+    @Data
+    public static class AIResponse {
+        private String id;
+        private List<Choice> choices;
+        private Long created;
+        private String model;
+        private String object;
+        private String system_fingerprint;
+        private Usage usage;
+    }
+
+    @Data
+    static class Choice {
+        private String finish_reason;
+        private int index;
+        private String logprobs;
+        private ResponseMessage message;
+    }
+
+    @Data
+    static class ResponseMessage {
+        private String content; // AI生成的回复
+        private String role;
+        private String function_call;
+        private String tool_calls;
+    }
+
+    @Data
+    static class Usage {
+        private int completion_tokens;
+        private int prompt_tokens;
+        private int total_tokens;
+    }
+}

+ 14 - 0
src/main/java/com/njuzr/eaibackend/service/CourseService.java

@@ -0,0 +1,14 @@
+package com.njuzr.eaibackend.service;
+
+import com.njuzr.eaibackend.dto.CourseDTO;
+import com.njuzr.eaibackend.vo.CourseVO;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 22:08
+ * @Package: EAI-Backend
+ */
+
+public interface CourseService {
+    CourseVO createCourse(CourseDTO courseDTO);
+}

+ 73 - 0
src/main/java/com/njuzr/eaibackend/service/impl/CourseServiceImpl.java

@@ -0,0 +1,73 @@
+package com.njuzr.eaibackend.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.njuzr.eaibackend.dto.CourseDTO;
+import com.njuzr.eaibackend.exception.MyException;
+import com.njuzr.eaibackend.mapper.CourseMapper;
+import com.njuzr.eaibackend.po.Course;
+import com.njuzr.eaibackend.service.CourseService;
+import com.njuzr.eaibackend.utils.ConvertUtil;
+import com.njuzr.eaibackend.utils.ModelMapperUtil;
+import com.njuzr.eaibackend.vo.CourseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 22:09
+ * @Package: EAI-Backend
+ */
+
+@Slf4j
+@Service
+public class CourseServiceImpl implements CourseService {
+
+    private final CourseMapper courseMapper;
+
+    @Autowired
+    public CourseServiceImpl(CourseMapper courseMapper) {
+        this.courseMapper = courseMapper;
+    }
+
+
+    @Override
+    public CourseVO createCourse(CourseDTO courseDTO) {
+        log.info("解析CourseDTO:"+courseDTO.toString());
+        // 类型转换
+        Course targetCourse = new Course();
+        BeanUtils.copyProperties(courseDTO, targetCourse);
+        targetCourse.setTeacherIds(ConvertUtil.listToString(courseDTO.getTeacherIds()));
+        if (courseDTO.getCourseImages() != null)
+            targetCourse.setCourseImages(ConvertUtil.listToString(courseDTO.getCourseImages()));
+        targetCourse.setCreateTime(new Date());
+
+        log.info("类型转换后的targetCourse"+targetCourse);
+
+        // 插入数据库
+        int status = courseMapper.insert(targetCourse);
+        if (status == 0) {
+            log.error("创建课程失败,数据库插入错误!");
+            throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()+":"+"创建课程错误");
+        }
+
+        // 将结果输出
+        CourseVO res = new CourseVO();
+        BeanUtils.copyProperties(targetCourse, res);
+        res.setTeacherIds(ConvertUtil.stringToList(targetCourse.getTeacherIds(), Long::valueOf));
+        if (targetCourse.getCourseImages() != null)
+            res.setCourseImages(ConvertUtil.stringToList(targetCourse.getCourseImages(), String::valueOf));
+
+        return res;
+    }
+
+
+}

+ 28 - 0
src/main/java/com/njuzr/eaibackend/utils/ConvertUtil.java

@@ -0,0 +1,28 @@
+package com.njuzr.eaibackend.utils;
+
+import org.apache.poi.ss.formula.functions.T;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/7 - 08:35
+ * @Package: EAI-Backend
+ */
+
+public class ConvertUtil {
+
+    public static <T> String listToString(List<T> list) {
+        return list.stream().map(String::valueOf).collect(Collectors.joining(","));
+    }
+
+    public static <T> List<T> stringToList(String str, Function<String, T> converter) {
+        return Arrays.stream(str.split(","))
+                .map(String::trim)
+                .map(converter)
+                .collect(Collectors.toList());
+    }
+}

+ 16 - 0
src/main/java/com/njuzr/eaibackend/utils/ModelMapperUtil.java

@@ -1,8 +1,19 @@
 package com.njuzr.eaibackend.utils;
 
+import com.njuzr.eaibackend.dto.CourseDTO;
 import com.njuzr.eaibackend.exception.MyException;
+import com.njuzr.eaibackend.po.Course;
+import com.njuzr.eaibackend.vo.CourseVO;
 import lombok.extern.slf4j.Slf4j;
+import org.modelmapper.Conditions;
+import org.modelmapper.Converter;
 import org.modelmapper.ModelMapper;
+import org.modelmapper.convention.MatchingStrategies;
+import org.modelmapper.spi.MappingContext;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author: Leonezhurui
@@ -14,10 +25,15 @@ import org.modelmapper.ModelMapper;
 public class ModelMapperUtil {
     private static final ModelMapper modelMapper = new ModelMapper();
 
+    static {
+        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); // 开启严格模式,只有字段完全匹配才会映射
+    }
+
     private ModelMapperUtil() {
         // 私有构造函数,防止实例化
     }
 
+
     // 1 将entity转化成outClass类的实例对象
     public static <D, T> D map(final T entity, Class<D> outClass) {
         try {

+ 15 - 0
src/main/java/com/njuzr/eaibackend/utils/WebClientUtil.java

@@ -1,8 +1,12 @@
 package com.njuzr.eaibackend.utils;
 
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.reactive.function.client.WebClientResponseException;
+import reactor.netty.http.client.HttpClient;
 
 /**
  * @author: Leonezhurui
@@ -16,6 +20,17 @@ public class WebClientUtil {
 
     // 构造函数,使用baseUrl初始化WebClient
     public WebClientUtil(String baseUrl) {
+//        SslContextBuilder sslContextBuilder = SslContextBuilder
+//                .forClient()
+//                .trustManager(InsecureTrustManagerFactory.INSTANCE); // 信任所有证书
+//
+//        HttpClient httpClient = HttpClient.create()
+//                .secure(sslContextSpec -> sslContextSpec.sslContext(sslContextBuilder));
+//        this.webClient = WebClient.builder()
+//                .clientConnector(new ReactorClientHttpConnector(httpClient))
+//                .baseUrl(baseUrl)
+//                .build();
+
         this.webClient = WebClient.builder()
                 .baseUrl(baseUrl)
                 .build();

+ 17 - 0
src/main/java/com/njuzr/eaibackend/vo/AIDialogueVO.java

@@ -0,0 +1,17 @@
+package com.njuzr.eaibackend.vo;
+
+import com.njuzr.eaibackend.po.AIEntry;
+import lombok.Data;
+import org.springframework.data.domain.Page;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/3 - 23:23
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class AIDialogueVO {
+    private String dialogueId;
+    private Page<AIEntry> messages;
+}

+ 39 - 0
src/main/java/com/njuzr/eaibackend/vo/CourseVO.java

@@ -0,0 +1,39 @@
+package com.njuzr.eaibackend.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.njuzr.eaibackend.po.Course;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/5 - 22:10
+ * @Package: EAI-Backend
+ */
+
+@Data
+public class CourseVO {
+    private Long courseId;
+
+    private String courseName;
+
+    private List<Long> teacherIds; // 序列化好的字符串
+
+    private List<String> courseImages; // 序列化好的字符串
+
+    private String targetGrade;
+
+    private Date startTime;
+
+    private Date endTime;
+
+    private Date createTime;
+
+    private String description;
+}

+ 10 - 0
src/main/resources/application.yaml

@@ -7,6 +7,10 @@ spring:
     username: root
     password: Zr200012
     url: jdbc:mysql://localhost:3306/EAI?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
+  data:
+    mongodb:
+      uri: mongodb://admin:admin@localhost:27017/eai?authSource=admin
+      authentication-database: admin
   mail:
     host: smtp.163.com
     port: 25
@@ -15,6 +19,12 @@ spring:
     default-encoding: UTF-8
 
 
+# MyBatis配置
+mybatis-plus:
+  mapper-locations: classpath:mapper/*.xml
+  type-aliases-package: com/njuzr/eaibackend/po
+
+
 jwt:
   # jjwt密钥算法HS256,要求secret长度至少是256位
   secret: xTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguhxTv@UA7e2q$*n&92AHo5Wj$@KAnguh92AHo5Wj$@KAnguh92AHo5Wj$@KAnguh92AHo5Wj$@KAnguh

+ 2 - 2
src/main/resources/mapper/UserMapper.xml

@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.njuzr.eaibackend.mapper.UserMapper">
-    <select id="selectByOfficialNumber" resultType="com.njuzr.eaibackend.po.User">
+    <select id="selectByOfficialNumber" resultType="User">
         select *
         from users
         where official_number = #{officialNumber}
     </select>
 
-    <update id="updateUser" parameterType="com.njuzr.eaibackend.po.User">
+    <update id="updateUser" parameterType="User">
         UPDATE users
         <set>
             <if test="name != null">name=#{name},</if>

+ 25 - 0
src/test/java/com/njuzr/eaibackend/service/AIRequestServiceTest.java

@@ -0,0 +1,25 @@
+package com.njuzr.eaibackend.service;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * @author: Leonezhurui
+ * @Date: 2024/3/2 - 00:30
+ * @Package: EAI-Backend
+ */
+
+@SpringBootTest
+public class AIRequestServiceTest {
+    private final AIRequestService aiRequestService;
+
+    @Autowired
+    public AIRequestServiceTest(AIRequestService aiRequestService) {
+        this.aiRequestService = aiRequestService;
+    }
+
+    @Test
+    public void testChatGLM() {
+    }
+}