ソースを参照

Merge branch 'feat/classStu' into refactor

# Conflicts:
#	src/main/java/com/njuzr/eaibackend/mapper/StudentAssignmentMapper.java
#	src/main/java/com/njuzr/eaibackend/service/impl/AssignmentServiceImpl.java
zhi.li102 1 年間 前
コミット
6982622190
28 ファイル変更1037 行追加21 行削除
  1. 2 2
      application.yaml
  2. 23 0
      src/main/java/com/njuzr/eaibackend/controller/AssignmentController.java
  3. 104 1
      src/main/java/com/njuzr/eaibackend/controller/ClassController.java
  4. 24 4
      src/main/java/com/njuzr/eaibackend/controller/CourseController.java
  5. 9 0
      src/main/java/com/njuzr/eaibackend/enums/AssignmentCompletionStatus.java
  6. 37 0
      src/main/java/com/njuzr/eaibackend/enums/ClassStudentState.java
  7. 8 0
      src/main/java/com/njuzr/eaibackend/mapper/AssignmentMapper.java
  8. 12 0
      src/main/java/com/njuzr/eaibackend/mapper/ClassMapper.java
  9. 46 0
      src/main/java/com/njuzr/eaibackend/mapper/ClassStudentMapper.java
  10. 23 0
      src/main/java/com/njuzr/eaibackend/mapper/StudentAssignmentMapper.java
  11. 8 0
      src/main/java/com/njuzr/eaibackend/mapper/UserMapper.java
  12. 24 0
      src/main/java/com/njuzr/eaibackend/po/ClassStudent.java
  13. 14 0
      src/main/java/com/njuzr/eaibackend/po/StudentAssignment.java
  14. 11 4
      src/main/java/com/njuzr/eaibackend/service/AssignmentService.java
  15. 23 0
      src/main/java/com/njuzr/eaibackend/service/ClassService.java
  16. 9 0
      src/main/java/com/njuzr/eaibackend/service/CourseService.java
  17. 74 0
      src/main/java/com/njuzr/eaibackend/service/impl/AssignmentServiceImpl.java
  18. 329 1
      src/main/java/com/njuzr/eaibackend/service/impl/ClassServiceImpl.java
  19. 106 7
      src/main/java/com/njuzr/eaibackend/service/impl/CourseServiceImpl.java
  20. 13 0
      src/main/java/com/njuzr/eaibackend/vo/StudentAssignmentStatusVO.java
  21. 12 0
      src/main/java/com/njuzr/eaibackend/vo/StudentCourseAssignmentVO.java
  22. 12 0
      src/main/java/com/njuzr/eaibackend/vo/StudentCourseHomeworkVO.java
  23. 28 0
      src/main/java/com/njuzr/eaibackend/vo/StudentInfoVO.java
  24. 10 0
      src/main/resources/mapper/AssignmentMapper.xml
  25. 12 0
      src/main/resources/mapper/ClassMapper.xml
  26. 25 0
      src/main/resources/mapper/ClassStudentMapper.xml
  27. 24 1
      src/main/resources/mapper/StudentAssignmentMapper.xml
  28. 15 1
      src/main/resources/mapper/UserMapper.xml

+ 2 - 2
application.yaml

@@ -6,7 +6,7 @@ spring:
     driver-class-name: com.mysql.cj.jdbc.Driver
     username: root
     password: eai123456
-    url: jdbc:mysql://139.196.252.184:3306/eai?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true
+    url: jdbc:mysql://8.130.28.19:3306/eai?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true
 #    url: jdbc:mysql://127.0.0.1:3306/eai?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&allowPublicKeyRetrieval=true
   data:
     mongodb:
@@ -100,4 +100,4 @@ springdoc:
   swagger-ui:
     enabled: true
     path: /doc/swagger/swagger-ui.html
-    packagesToScan: com.njuzr.eaibackend.controller
+    packagesToScan: com.njuzr.eaibackend.controller

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

@@ -7,11 +7,13 @@ import com.njuzr.eaibackend.dto.AssignmentQueryDTO;
 import com.njuzr.eaibackend.dto.AssignmentUpdateDTO;
 import com.njuzr.eaibackend.dto.CorrectDTO;
 import com.njuzr.eaibackend.enums.AssignmentCompletionStatus;
+import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.po.Assignment;
 import com.njuzr.eaibackend.po.Engagement;
 import com.njuzr.eaibackend.po.MyUserDetails;
 import com.njuzr.eaibackend.service.AIDialogueService;
 import com.njuzr.eaibackend.service.AssignmentService;
+import com.njuzr.eaibackend.vo.StudentAssignmentStatusVO;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
@@ -22,6 +24,7 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Scanner;
 
 /**
@@ -241,4 +244,24 @@ public class AssignmentController {
         return MyResponse.success("");
     }
 
+
+    /**
+     * 查询班级学生的作业状态
+     * @param classId 班级ID
+     * @param assignmentId 作业ID
+     * @return 学生作业状态列表
+     */
+    @GetMapping("/class/{classId}/status")
+    public MyResponse getStudentsAssignmentStatus(
+            @PathVariable Long classId,
+            @RequestParam Long assignmentId) {
+        try {
+            List<StudentAssignmentStatusVO> result = assignmentService.getStudentsAssignmentStatus(classId, assignmentId);
+            return MyResponse.success(result);
+        } catch (Exception e) {
+            log.error("查询学生作业状态失败", e);
+            return MyResponse.error(500, "查询失败:" + e.getMessage());
+        }
+    }
+
 }

+ 104 - 1
src/main/java/com/njuzr/eaibackend/controller/ClassController.java

@@ -1,12 +1,19 @@
 package com.njuzr.eaibackend.controller;
 
 import com.njuzr.eaibackend.dto.ClassDTO;
+import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.po.Class;
 import com.njuzr.eaibackend.service.ClassService;
+import com.njuzr.eaibackend.vo.StudentInfoVO;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.annotations.Delete;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
-
+@Slf4j
 @RestController
 @RequestMapping("/api/class")
 public class ClassController {
@@ -45,4 +52,100 @@ public class ClassController {
                 return MyResponse.error(400, result);
         }
     }
+
+    /**
+     * 批量添加学生到班级
+     * @param classId 班级ID
+     * @param file 学生数据文件
+     * @return 操作结果
+     */
+    @PostMapping("/{classId}/students/batch")
+    @PreAuthorize("hasRole('ADMIN') or hasRole('TEACHER')")
+    public MyResponse batchAddStudents(
+            @PathVariable Long classId,
+            @RequestParam("file") MultipartFile file) {
+
+        try {
+            String result = classService.batchAddStudents(classId, file);
+            return MyResponse.success(result);
+        } catch (MyException e) {
+            return MyResponse.error(e.getErrCode(), e.getMessage());
+        } catch (Exception e) {
+            log.error("批量添加学生失败", e);
+            return MyResponse.error(500, "服务器内部错误");
+        }
+    }
+
+    /**
+     * 通过学号添加学生到班级
+     * @param classId 班级ID
+     * @param officialNumber 学号
+     * @return 操作结果
+     */
+    @PostMapping("/{classId}/students")
+    @PreAuthorize("hasRole('ROLE_TEACHER')")
+    public MyResponse addStudentByNumber(
+            @PathVariable Long classId,
+            @RequestParam String officialNumber) {
+
+        try {
+            String result = classService.addStudentByOfficialNumber(classId, officialNumber);
+            return MyResponse.success(result);
+        } catch (MyException e) {
+            return MyResponse.error(e.getErrCode(), e.getMessage());
+        } catch (Exception e) {
+            log.error("添加学生失败", e);
+            return MyResponse.error(500, "服务器内部错误");
+        }
+    }
+
+    /**
+     * 批量删除班级中的学生
+     * @param classId 班级ID
+     * @param request 包含学号列表的请求体
+     * @return 操作结果
+     */
+    @DeleteMapping("/{classId}/students/batch")
+    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_TEACHER')")
+    public MyResponse batchDeleteStudents(
+            @PathVariable Long classId,
+            @RequestBody List<String> request) {
+
+        try {
+            String result = classService.batchDeleteStudents(classId, request);
+            return MyResponse.success(result);
+        } catch (MyException e) {
+            return MyResponse.error(e.getErrCode(), e.getMessage());
+        } catch (Exception e) {
+            log.error("批量删除学生失败", e);
+            return MyResponse.error(500, "服务器内部错误");
+        }
+    }
+
+    @GetMapping("/{classId}")
+    public MyResponse getClassStudents(@PathVariable Long classId) {
+        try {
+            List<StudentInfoVO> students = classService.getStudentsByClassId(classId);
+            return MyResponse.success(students);
+        } catch (Exception e) {
+            // 记录日志
+            log.error("查询班级学生失败", e);
+            // 返回错误响应
+            return MyResponse.error(500, "查询班级学生失败: " + e.getMessage());
+        }
+    }
+
+
+    @GetMapping("/find/{classId}")
+    public MyResponse getClassById(@PathVariable Long classId) {
+        try {
+            Class courseClass = classService.getClassById(classId);
+            return MyResponse.success(courseClass);
+        } catch (IllegalArgumentException e) {
+            return MyResponse.error(400, e.getMessage());
+        } catch (Exception e) {
+            log.error("获取班级信息失败: {}", e.getMessage(), e);
+            return MyResponse.error(500, "服务器内部错误");
+        }
+    }
 }

+ 24 - 4
src/main/java/com/njuzr/eaibackend/controller/CourseController.java

@@ -6,12 +6,14 @@ import com.njuzr.eaibackend.dto.course.CourseDTO;
 import com.njuzr.eaibackend.dto.course.CourseQueryDTO;
 import com.njuzr.eaibackend.dto.course.CourseUpdateDTO;
 import com.njuzr.eaibackend.dto.course.EnrollDTO;
+import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.po.Course;
 import com.njuzr.eaibackend.po.MyUserDetails;
 import com.njuzr.eaibackend.po.User;
 import com.njuzr.eaibackend.service.CourseService;
 import com.njuzr.eaibackend.service.UserService;
 import com.njuzr.eaibackend.vo.CourseVO;
+import com.njuzr.eaibackend.vo.StudentCourseHomeworkVO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -251,9 +253,14 @@ public class CourseController {
             @AuthenticationPrincipal(expression = "id") Long studentId,
             @RequestBody EnrollDTO enrollDTO
     ) {
-        enrollDTO.setStudentId(studentId);
-        courseService.enroll(enrollDTO);
-        return MyResponse.success("选课成功");
+        try {
+            enrollDTO.setStudentId(studentId);
+            courseService.enroll(enrollDTO);
+            return MyResponse.success("选课成功");
+        }
+        catch (MyException e){
+            return MyResponse.error(e.getErrCode(),e.getMessage());
+        }
     }
 
     @GetMapping("/getEnrollments/byCourse")
@@ -300,6 +307,19 @@ public class CourseController {
     }
 
 
-
+    @GetMapping("/student/assignments")
+    public MyResponse getStudentCourseAssignments(
+            @RequestParam Long studentId,
+            @RequestParam Long courseId) {
+        try {
+            StudentCourseHomeworkVO result = courseService.getStudentCourseAssignments(studentId, courseId);
+            return MyResponse.success(result);
+        } catch (RuntimeException e) {
+            return MyResponse.error(400, e.getMessage());
+        } catch (Exception e) {
+            log.error("查询学生课程作业失败", e);
+            return MyResponse.error(500, "查询失败:" + e.getMessage());
+        }
+    }
 
 }

+ 9 - 0
src/main/java/com/njuzr/eaibackend/enums/AssignmentCompletionStatus.java

@@ -31,4 +31,13 @@ public enum AssignmentCompletionStatus {
         this.status = status;
     }
 
+    // 根据数据库存储值获取枚举
+    public static AssignmentCompletionStatus fromValue(String value) {
+        for (AssignmentCompletionStatus status : values()) {
+            if (status.code == Integer.parseInt(value)) {
+                return status;
+            }
+        }
+        throw new IllegalArgumentException("Invalid assignment status: " + value);
+    }
 }

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

@@ -0,0 +1,37 @@
+package com.njuzr.eaibackend.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+
+public enum ClassStudentState {
+    NOT_JOINED(0, "未加入班级"),
+    JOINED(1, "已加入班级");
+
+    @EnumValue  // 标记数据库存储的实际值
+    private final int code;
+    private final String state;
+
+    ClassStudentState(int code, String state) {
+        this.code = code;
+        this.state = state;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    /**
+     * 根据状态码获取对应枚举
+     */
+    public static ClassStudentState fromCode(int code) {
+        for (ClassStudentState status : values()) {
+            if (status.code == code) {
+                return status;
+            }
+        }
+        throw new IllegalArgumentException("无效的状态码: " + code);
+    }
+}

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

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.njuzr.eaibackend.po.Assignment;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * @author: Leonezhurui
  * @Date: 2024/3/5 - 16:32
@@ -12,4 +14,10 @@ import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
 public interface AssignmentMapper extends BaseMapper<Assignment> {
+    /**
+     * 根据课程ID查询作业
+     * @param courseId 课程ID
+     * @return 作业列表
+     */
+    List<Assignment> selectByCourseId(Long courseId);
 }

+ 12 - 0
src/main/java/com/njuzr/eaibackend/mapper/ClassMapper.java

@@ -3,7 +3,19 @@ package com.njuzr.eaibackend.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.njuzr.eaibackend.po.Class;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
 
 @Mapper
 public interface ClassMapper extends BaseMapper<Class> {
+    int increaseStuNumber(@Param("classId") Long classId, @Param("count") int count);
+
+    /**
+     * 减少班级人数
+     * @param classId 班级ID
+     * @param count 减少的数量
+     * @return 更新的行数
+     */
+    @Update("UPDATE class SET stu_number = stu_number - #{count} WHERE class_id = #{classId}")
+    int decreaseStuNumber(@Param("classId") Long classId, @Param("count") int count);
 }

+ 46 - 0
src/main/java/com/njuzr/eaibackend/mapper/ClassStudentMapper.java

@@ -0,0 +1,46 @@
+package com.njuzr.eaibackend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.njuzr.eaibackend.po.ClassStudent;
+import com.njuzr.eaibackend.vo.StudentInfoVO;
+import org.apache.ibatis.annotations.*;
+
+import java.util.List;
+
+@Mapper
+public interface ClassStudentMapper extends BaseMapper<ClassStudent> {
+    int batchInsert(@Param("list") List<ClassStudent> students);
+
+    /**
+     * 根据班级ID和学号列表批量删除学生
+     * @param classId 班级ID
+     * @param officialNumbers 学号列表
+     * @return 删除的行数
+     */
+    int batchDeleteByClassIdAndNumbers(
+            @Param("classId") Long classId,
+            @Param("officialNumbers") List<String> officialNumbers);
+
+    @Select("SELECT official_number, stu_name, state FROM class_student WHERE class_id = #{classId}")
+    @Results({
+            @Result(property = "officialNumber", column = "official_number"),
+            @Result(property = "stuName", column = "stu_name")
+    })
+    List<StudentInfoVO> selectStudentsByClassId(@Param("classId") Long classId);
+
+    /**
+     * 根据学生ID和课程ID查找班级学生记录
+     */
+    @Select("SELECT cs.* FROM class_student cs " +
+            "JOIN class c ON cs.class_id = c.class_id " +
+            "JOIN users u ON cs.official_number = u.official_number " +
+            "WHERE u.id = #{studentId} AND c.course_id = #{courseId}")
+    ClassStudent findClassStudentByStudentAndCourse(@Param("studentId") Long studentId,
+                                                    @Param("courseId") Long courseId);
+
+    /**
+     * 更新学生加入状态
+     */
+    @Update("UPDATE class_student SET state = #{state} WHERE id = #{id}")
+    int updateStateById(@Param("id") Long id, @Param("state") int state);
+}

+ 23 - 0
src/main/java/com/njuzr/eaibackend/mapper/StudentAssignmentMapper.java

@@ -3,7 +3,9 @@ package com.njuzr.eaibackend.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.njuzr.eaibackend.po.Engagement;
+import com.njuzr.eaibackend.po.StudentAssignment;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -21,7 +23,28 @@ public interface StudentAssignmentMapper extends BaseMapper<Engagement> {
     Page<Engagement> findEngagementByAssignmentId(Page<Engagement> page, Long assignmentId);
 
     List<Engagement> findEngagementByAssignmentId(Long assignmentId);
+
     void delete(Long studentId, Long assignmentId);
 
     String getTextContent(Long studentId, Long assignmentId);
+
+    /**
+     * 根据作业ID和学生ID列表查询作业提交情况
+     * @param assignmentId 作业ID
+     * @param studentIds 学生ID列表
+     * @return 学生作业列表
+     */
+    List<StudentAssignment> findByAssignmentIdAndStudentIds(
+            @Param("assignmentId") Long assignmentId,
+            @Param("studentIds") List<Long> studentIds);
+
+    /**
+     * 根据学生ID和作业ID列表查询提交情况
+     * @param studentId 学生ID
+     * @param assignmentIds 作业ID列表
+     * @return 学生作业列表
+     */
+    List<StudentAssignment> findByStudentIdAndAssignmentIds(
+            @Param("studentId") Long studentId,
+            @Param("assignmentIds") List<Long> assignmentIds);
 }

+ 8 - 0
src/main/java/com/njuzr/eaibackend/mapper/UserMapper.java

@@ -3,6 +3,7 @@ package com.njuzr.eaibackend.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.njuzr.eaibackend.po.User;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
@@ -68,4 +69,11 @@ public interface UserMapper extends BaseMapper<User> {
      * @return User
      */
     User selectByOfficialEmail(String officialEmail);
+
+    /**
+     * 根据学号列表查询用户ID和学号
+     * @param officialNumbers 学号列表
+     * @return 用户列表(包含id和officialNumber)
+     */
+    List<User> selectUserIdByOfficialNumbers(@Param("officialNumbers") List<String> officialNumbers);
 }

+ 24 - 0
src/main/java/com/njuzr/eaibackend/po/ClassStudent.java

@@ -0,0 +1,24 @@
+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 com.njuzr.eaibackend.enums.ClassStudentState;
+import lombok.Data;
+
+@Data
+@TableName("class_student")
+public class ClassStudent {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @TableField("state")
+    private ClassStudentState state;
+
+    private String officialNumber; // 学号
+
+    private String stuName; // 学生姓名
+
+    private Long classId; // 班级ID
+}

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

@@ -0,0 +1,14 @@
+package com.njuzr.eaibackend.po;
+
+import lombok.Data;
+import java.math.BigDecimal;
+
+@Data
+public class StudentAssignment {
+    private Long id;
+    private Long assignmentId;   // 作业ID
+    private Long studentId;      // 学生ID
+    private BigDecimal score;    // 成绩(数据库为decimal类型)
+    private String status;       // 状态(UNSUBMITTED/SUBMITTED/GRADED)
+    // 其他字段根据数据库表补充(如提交时间等)
+}

+ 11 - 4
src/main/java/com/njuzr/eaibackend/service/AssignmentService.java

@@ -10,10 +10,9 @@ import com.njuzr.eaibackend.dto.CorrectDTO;
 import com.njuzr.eaibackend.po.Assignment;
 import com.njuzr.eaibackend.po.Engagement;
 import com.njuzr.eaibackend.po.MyUserDetails;
-import com.njuzr.eaibackend.vo.AssignmentVO;
-import com.njuzr.eaibackend.vo.EngageNumberVo;
-import com.njuzr.eaibackend.vo.EngagementDetailVO;
-import com.njuzr.eaibackend.vo.EngagementVO;
+import com.njuzr.eaibackend.vo.*;
+
+import java.util.List;
 
 /**
  * @author: Leonezhurui
@@ -52,4 +51,12 @@ public interface AssignmentService {
     long getNextStudentId(Long assignmentId, Long studentId);
 
     EngageNumberVo getEngageNumber(Long assignmentId);
+
+    /**
+     * 根据班级ID和作业ID查询学生作业状态
+     * @param classId 班级ID
+     * @param assignmentId 作业ID
+     * @return 学生作业状态列表
+     */
+    List<StudentAssignmentStatusVO> getStudentsAssignmentStatus(Long classId, Long assignmentId);
 }

+ 23 - 0
src/main/java/com/njuzr/eaibackend/service/ClassService.java

@@ -2,6 +2,8 @@ package com.njuzr.eaibackend.service;
 
 import com.njuzr.eaibackend.dto.ClassDTO;
 import com.njuzr.eaibackend.po.Class;
+import com.njuzr.eaibackend.vo.StudentInfoVO;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -18,4 +20,25 @@ public interface ClassService {
      * @return 删除结果信息
      */
     String deleteClassWithCheck(Long classId);
+
+    String batchAddStudents(Long classId, MultipartFile file);
+    /**
+     * 通过学号添加学生到班级
+     * @param classId 班级ID
+     * @param officialNumber 学生学号
+     * @return 操作结果
+     */
+    String addStudentByOfficialNumber(Long classId, String officialNumber);
+
+    /**
+     * 批量删除班级中的学生
+     * @param classId 班级ID
+     * @param officialNumbers 学号列表
+     * @return 操作结果
+     */
+    String batchDeleteStudents(Long classId, List<String> officialNumbers);
+
+    List<StudentInfoVO> getStudentsByClassId(Long classId);
+
+    Class getClassById(Long classId);
 }

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

@@ -11,6 +11,7 @@ import com.njuzr.eaibackend.po.Course;
 import com.njuzr.eaibackend.po.MyUserDetails;
 import com.njuzr.eaibackend.po.User;
 import com.njuzr.eaibackend.vo.CourseVO;
+import com.njuzr.eaibackend.vo.StudentCourseHomeworkVO;
 import com.njuzr.eaibackend.vo.UserVO;
 import org.springframework.web.bind.annotation.RequestParam;
 
@@ -41,4 +42,12 @@ public interface CourseService {
 
     void dropCourse(Long studentId, Long courseId);
 
+    // 添加新方法
+    /**
+     * 根据学生ID和课程ID查询该学生的课程作业情况
+     * @param studentId 学生ID
+     * @param courseId 课程ID
+     * @return 学生课程作业情况
+     */
+    StudentCourseHomeworkVO getStudentCourseAssignments(Long studentId, Long courseId);
 }

+ 74 - 0
src/main/java/com/njuzr/eaibackend/service/impl/AssignmentServiceImpl.java

@@ -14,7 +14,9 @@ import com.njuzr.eaibackend.enums.Role;
 import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.mapper.*;
 import com.njuzr.eaibackend.po.*;
+import com.njuzr.eaibackend.po.Class;
 import com.njuzr.eaibackend.service.AssignmentService;
+import com.njuzr.eaibackend.service.ClassService;
 import com.njuzr.eaibackend.service.TextAnalysisService;
 import com.njuzr.eaibackend.utils.*;
 import com.njuzr.eaibackend.vo.*;
@@ -27,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -47,6 +50,7 @@ public class AssignmentServiceImpl implements AssignmentService {
 
     private final UserMapper userMapper;
 
+    private final ClassService classService;
     private final OssUtil ossUtil;
 
     private final FileUtil fileUtil = new FileUtil();
@@ -60,6 +64,7 @@ public class AssignmentServiceImpl implements AssignmentService {
                                  OssUtil ossUtil,
                                  CourseServiceImpl courseServiceImpl,
                                  CourseStudentMapper courseStudentMapper,
+                                 ClassService classService,
                                  TextAnalysisService textAnalysisService) {
         this.assignmentMapper = assignmentMapper;
         this.courseMapper = courseMapper;
@@ -67,6 +72,7 @@ public class AssignmentServiceImpl implements AssignmentService {
         this.userMapper = userMapper;
         this.ossUtil = ossUtil;
         this.textAnalysisService = textAnalysisService;
+        this.classService = classService;
     }
 
     @Override
@@ -484,4 +490,72 @@ public class AssignmentServiceImpl implements AssignmentService {
             return AssignmentStatus.PROCEEDING;
         }
     }
+
+
+
+    @Override
+    public List<StudentAssignmentStatusVO> getStudentsAssignmentStatus(Long classId, Long assignmentId) {
+        // 1. 验证班级和作业存在性
+        Assignment assignment = assignmentMapper.selectById(assignmentId);
+        if (assignment == null) {
+            throw new RuntimeException("作业不存在");  // 建议替换为项目自定义异常
+        }
+
+        // 2. 获取班级所有学生(从class_student表)
+        List<StudentInfoVO> classStudents = classService.getStudentsByClassId(classId);
+        if (classStudents.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // 3. 提取学号列表,查询对应的用户ID(users表)
+        List<String> officialNumbers = classStudents.stream()
+                .map(StudentInfoVO::getOfficialNumber)
+                .collect(Collectors.toList());
+
+        List<User> users = userMapper.selectUserIdByOfficialNumbers(officialNumbers);
+        Map<String, Long> officialNumberToUserIdMap = users.stream()
+                .collect(Collectors.toMap(
+                        User::getOfficialNumber,
+                        User::getId
+                ));
+
+        // 4. 查询这些学生的作业提交情况(student_assignment表)
+        List<Long> studentIds = new ArrayList<>(officialNumberToUserIdMap.values());
+        List<StudentAssignment> studentAssignments = studentAssignmentMapper.findByAssignmentIdAndStudentIds(assignmentId, studentIds);
+
+        // 5. 构建学生ID到作业情况的映射
+        Map<Long, StudentAssignment> studentIdToAssignmentMap = studentAssignments.stream()
+                .collect(Collectors.toMap(
+                        StudentAssignment::getStudentId,
+                        Function.identity()
+                ));
+
+        // 6. 组装返回结果
+        return classStudents.stream()
+                .map(student -> {
+                    StudentAssignmentStatusVO vo = new StudentAssignmentStatusVO();
+                    vo.setOfficialNumber(student.getOfficialNumber());
+                    vo.setStuName(student.getStuName());
+
+                    // 设置学生ID
+                    Long studentId = officialNumberToUserIdMap.get(student.getOfficialNumber());
+                    vo.setStudentId(studentId);
+
+                    // 设置状态和成绩
+                    StudentAssignment studentAssignment = studentIdToAssignmentMap.get(studentId);
+                    if (studentAssignment == null) {
+                        // 未参与作业,按未提交处理
+                        vo.setStatus(AssignmentCompletionStatus.NOT_SUBMITTED);
+                        vo.setScore(0);
+                    } else {
+                        // 转换状态为枚举
+                        vo.setStatus(AssignmentCompletionStatus.fromValue(studentAssignment.getStatus()));
+                        // 处理成绩(BigDecimal转Integer,未批改为0)
+                        vo.setScore(studentAssignment.getScore() != null ?
+                                studentAssignment.getScore().intValue() : 0);
+                    }
+                    return vo;
+                })
+                .collect(Collectors.toList());
+    }
 }

+ 329 - 1
src/main/java/com/njuzr/eaibackend/service/impl/ClassServiceImpl.java

@@ -2,24 +2,44 @@ package com.njuzr.eaibackend.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.njuzr.eaibackend.dto.ClassDTO;
+import com.njuzr.eaibackend.enums.ClassStudentState;
 import com.njuzr.eaibackend.exception.MyException;
 import com.njuzr.eaibackend.mapper.ClassMapper;
+import com.njuzr.eaibackend.mapper.ClassStudentMapper;
+import com.njuzr.eaibackend.mapper.UserMapper;
 import com.njuzr.eaibackend.po.Class;
+import com.njuzr.eaibackend.po.ClassStudent;
+import com.njuzr.eaibackend.po.User;
 import com.njuzr.eaibackend.service.ClassService;
+import com.njuzr.eaibackend.vo.StudentInfoVO;
+import com.opencsv.CSVReader;
+import com.opencsv.exceptions.CsvValidationException;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service
 public class ClassServiceImpl implements ClassService {
     private final ClassMapper classMapper;
+    private final ClassStudentMapper classStudentMapper;
+    private final UserMapper userMapper;
 
-    public ClassServiceImpl(ClassMapper classMapper) {
+    public ClassServiceImpl(ClassMapper classMapper, ClassStudentMapper classStudentMapper, UserMapper userMapper) {
         this.classMapper = classMapper;
+        this.classStudentMapper = classStudentMapper;
+        this.userMapper = userMapper;
     }
 
     @Override
@@ -92,5 +112,313 @@ public class ClassServiceImpl implements ClassService {
         return "删除成功";
     }
 
+    @Override
+    @Transactional
+    public String batchAddStudents(Long classId, MultipartFile file) {
+        // 检查班级是否存在
+        Class cls = classMapper.selectById(classId);
+        if (cls == null) {
+            throw new MyException(404, "班级不存在");
+        }
+
+        // 解析文件
+        List<ClassStudent> students;
+        String fileName = file.getOriginalFilename();
+
+        try {
+            if (fileName != null && (fileName.endsWith(".xls") || fileName.endsWith(".xlsx"))) {
+                students = parseExcelFile(file, classId);
+            } else if (fileName != null && fileName.endsWith(".csv")) {
+                students = parseCsvFile(file, classId);
+            } else {
+                throw new MyException(400, "不支持的文件类型");
+            }
+        } catch (Exception e) {
+            log.error("解析学生文件失败", e);
+            throw new MyException(500, "解析学生文件失败: " + e.getMessage());
+        }
+
+        if (students.isEmpty()) {
+            return "未找到有效学生数据";
+        }
+
+        // 批量插入学生
+        int insertedCount = classStudentMapper.batchInsert(students);
+
+        // 更新班级人数
+        classMapper.increaseStuNumber(classId, insertedCount);
+
+        return "成功添加 " + insertedCount + " 名学生";
+    }
+
+    @Override
+    @Transactional
+    public String addStudentByOfficialNumber(Long classId, String officialNumber) {
+        // 1. 检查班级是否存在
+        Class cls = classMapper.selectById(classId);
+        if (cls == null) {
+            throw new MyException(404, "班级不存在");
+        }
+
+        // 2. 根据学号查询用户
+        QueryWrapper<User> userWrapper = new QueryWrapper<>();
+        userWrapper.eq("official_number", officialNumber);
+        User user = userMapper.selectOne(userWrapper);
+
+        if (user == null) {
+            throw new MyException(404, "未找到学号为 " + officialNumber + " 的学生");
+        }
+
+        // 3. 检查是否已在班级中
+        QueryWrapper<ClassStudent> existWrapper = new QueryWrapper<>();
+        existWrapper.eq("class_id", classId)
+                .eq("official_number", officialNumber);
+
+        if (classStudentMapper.selectCount(existWrapper) > 0) {
+            throw new MyException(400, "该学生已在班级中");
+        }
+
+        // 4. 创建学生记录
+        ClassStudent student = new ClassStudent();
+        student.setClassId(classId);
+        student.setOfficialNumber(officialNumber);
+        student.setStuName(user.getName()); // 从用户表获取姓名
+        student.setState(ClassStudentState.NOT_JOINED); // 默认状态
+
+        // 5. 插入记录
+        int insertResult = classStudentMapper.insert(student);
+        if (insertResult <= 0) {
+            throw new MyException(500, "添加学生失败");
+        }
+
+        // 6. 更新班级人数
+        classMapper.increaseStuNumber(classId, 1);
+
+        return "成功添加学生: " + user.getName() + " (" + officialNumber + ")";
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String batchDeleteStudents(Long classId, List<String> officialNumbers) {
+        // 1. 检查班级是否存在
+        Class cls = classMapper.selectById(classId);
+        if (cls == null) {
+            throw new MyException(404, "班级不存在");
+        }
+
+        // 2. 验证输入
+        if (officialNumbers == null || officialNumbers.isEmpty()) {
+            throw new MyException(400, "学号列表不能为空");
+        }
+
+        // 3. 检查学生是否在班级中
+        QueryWrapper<ClassStudent> wrapper = new QueryWrapper<>();
+        wrapper.eq("class_id", classId)
+                .in("official_number", officialNumbers);
+
+        List<ClassStudent> students = classStudentMapper.selectList(wrapper);
+        if (students.isEmpty()) {
+            throw new MyException(404, "没有找到匹配的学生");
+        }
+
+        // 4. 获取实际存在的学号列表
+        List<String> existingNumbers = students.stream()
+                .map(ClassStudent::getOfficialNumber)
+                .collect(Collectors.toList());
+
+        // 5. 批量删除学生记录
+        int deleteCount = classStudentMapper.batchDeleteByClassIdAndNumbers(classId, existingNumbers);
+        if (deleteCount <= 0) {
+            throw new MyException(500, "删除学生失败");
+        }
+
+        // 6. 更新班级人数
+        int updateCount = classMapper.decreaseStuNumber(classId, deleteCount);
+        if (updateCount <= 0) {
+            throw new MyException(500, "更新班级人数失败");
+        }
+
+        // 7. 处理部分成功的情况
+        List<String> notFoundNumbers = officialNumbers.stream()
+                .filter(number -> !existingNumbers.contains(number))
+                .collect(Collectors.toList());
+
+        String successMsg = "成功删除 " + deleteCount + " 名学生";
+        if (!notFoundNumbers.isEmpty()) {
+            String notFoundMsg = ",但以下学号未在班级中找到: " + String.join(", ", notFoundNumbers);
+            successMsg += notFoundMsg;
+        }
+
+        return successMsg;
+    }
+
+    @Override
+    public List<StudentInfoVO> getStudentsByClassId(Long classId) {
+        return classStudentMapper.selectStudentsByClassId(classId);
+    }
+
+    @Override
+    public Class getClassById(Long classId) {
+        if (classId == null || classId <= 0) {
+            log.warn("无效的 classId: {}", classId);
+            throw new IllegalArgumentException("无效的班级ID");
+        }
+        Class courseClass = classMapper.selectById(classId);
+        return courseClass;
+    }
+
+    private List<ClassStudent> parseExcelFile(MultipartFile file, Long classId) throws IOException {
+        List<ClassStudent> students = new ArrayList<>();
+        Workbook workbook = new XSSFWorkbook(file.getInputStream());
+        Sheet sheet = workbook.getSheetAt(0);
+
+        // 查找列索引
+        Row headerRow = sheet.getRow(0);
+        int nameCol = findColumnIndex(headerRow, "学生姓名");
+        int numberCol = findColumnIndex(headerRow, "学生学号");
+
+        if (nameCol == -1 || numberCol == -1) {
+            throw new MyException(400, "文件缺少必要列:学生姓名 或 学生学号");
+        }
+
+        // 创建数据格式化器
+        DataFormatter formatter = new DataFormatter();
+
+        // 处理数据行
+        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
+            Row row = sheet.getRow(i);
+            if (row == null) continue;
+
+            ClassStudent student = new ClassStudent();
+
+            // 获取学生姓名
+            Cell nameCell = row.getCell(nameCol);
+            student.setStuName(nameCell != null ?
+                    formatter.formatCellValue(nameCell).trim() : "");
+
+            // 获取学号(特殊处理大数字)
+            Cell numberCell = row.getCell(numberCol);
+            String officialNumber = "";
+            if (numberCell != null) {
+                if (numberCell.getCellType() == CellType.NUMERIC) {
+                    // 处理科学计数法
+                    double numericValue = numberCell.getNumericCellValue();
+                    if (String.valueOf(numericValue).contains("E")) {
+                        BigDecimal bigDecimal = BigDecimal.valueOf(numericValue);
+                        officialNumber = bigDecimal.toPlainString();
+                    } else {
+                        // 避免尾数出现 .0
+                        if (numericValue % 1 == 0) {
+                            officialNumber = String.valueOf((long) numericValue);
+                        } else {
+                            officialNumber = String.valueOf(numericValue);
+                        }
+                    }
+                } else {
+                    officialNumber = formatter.formatCellValue(numberCell).trim();
+                }
+            }
+            student.setOfficialNumber(officialNumber);
+
+            student.setClassId(classId);
+            student.setState(ClassStudentState.NOT_JOINED); // 默认状态
+
+            // 验证必要字段
+            if (student.getStuName().isEmpty() || student.getOfficialNumber().isEmpty()) {
+                log.warn("跳过无效学生数据行: {}", i + 1);
+                continue;
+            }
+
+            students.add(student);
+        }
+
+        return students;
+    }
+    private List<ClassStudent> parseCsvFile(MultipartFile file, Long classId)
+            throws IOException, CsvValidationException {
+
+        List<ClassStudent> students = new ArrayList<>();
+        CSVReader csvReader = new CSVReader(new InputStreamReader(file.getInputStream(), "UTF-8"));
+
+        // 读取表头
+        String[] header = csvReader.readNext();
+        if (header == null) {
+            throw new MyException(400, "CSV文件为空");
+        }
+
+        int nameCol = findColumnIndex(header, "学生姓名");
+        int numberCol = findColumnIndex(header, "学生学号");
+
+        if (nameCol == -1 || numberCol == -1) {
+            throw new MyException(400, "文件缺少必要列:学生姓名 或 学生学号");
+        }
+
+        // 处理数据行
+        String[] nextRecord;
+        while ((nextRecord = csvReader.readNext()) != null) {
+            if (nextRecord.length < Math.max(nameCol, numberCol) + 1) {
+                log.warn("跳过无效数据行: {}", String.join(",", nextRecord));
+                continue;
+            }
+
+            ClassStudent student = new ClassStudent();
+            student.setStuName(nextRecord[nameCol].trim());
+
+            // 处理可能包含逗号的数值
+            String officialNumber = nextRecord[numberCol].trim();
+            if (officialNumber.contains(",")) {
+                officialNumber = officialNumber.replace(",", "");
+            }
+            student.setOfficialNumber(officialNumber);
+
+            student.setClassId(classId);
+            student.setState(ClassStudentState.NOT_JOINED); // 默认状态
+
+            // 验证必要字段
+            if (student.getStuName().isEmpty() || student.getOfficialNumber().isEmpty()) {
+                log.warn("跳过无效学生数据: {}", String.join(",", nextRecord));
+                continue;
+            }
+
+            students.add(student);
+        }
+
+        return students;
+    }
+    private int findColumnIndex(Row headerRow, String columnName) {
+        for (Cell cell : headerRow) {
+            if (cell.getStringCellValue().trim().equalsIgnoreCase(columnName)) {
+                return cell.getColumnIndex();
+            }
+        }
+        return -1;
+    }
+
+    private int findColumnIndex(String[] header, String columnName) {
+        for (int i = 0; i < header.length; i++) {
+            if (header[i].trim().equalsIgnoreCase(columnName)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private String getCellStringValue(Cell cell) {
+        if (cell == null) return "";
+
+        DataFormatter formatter = new DataFormatter();
+
+        // 使用 DataFormatter 获取单元格的字符串表示
+        String cellValue = formatter.formatCellValue(cell).trim();
+
+        // 特殊处理科学计数法表示的大数字
+        if (cellValue.contains("E") && cell.getCellType() == CellType.NUMERIC) {
+            BigDecimal bigDecimal = BigDecimal.valueOf(cell.getNumericCellValue());
+            cellValue = bigDecimal.toPlainString();
+        }
+
+        return cellValue;
+    }
+
 
 }

+ 106 - 7
src/main/java/com/njuzr/eaibackend/service/impl/CourseServiceImpl.java

@@ -20,21 +20,26 @@ import com.njuzr.eaibackend.utils.PageMapperUtil;
 import com.njuzr.eaibackend.vo.CourseVO;
 import com.njuzr.eaibackend.vo.UserVO;
 import lombok.extern.slf4j.Slf4j;
-import org.bouncycastle.jcajce.provider.symmetric.TEA;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.CachePut;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.cache.annotation.Caching;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-
-import java.net.StandardSocketOptions;
+import com.njuzr.eaibackend.enums.AssignmentCompletionStatus;
+import com.njuzr.eaibackend.mapper.AssignmentMapper;
+import com.njuzr.eaibackend.mapper.StudentAssignmentMapper;
+import com.njuzr.eaibackend.mapper.UserMapper;
+import com.njuzr.eaibackend.po.Assignment;
+import com.njuzr.eaibackend.po.StudentAssignment;
+import com.njuzr.eaibackend.po.User;
+import com.njuzr.eaibackend.vo.StudentCourseAssignmentVO;
+import com.njuzr.eaibackend.vo.StudentCourseHomeworkVO;
+import org.springframework.stereotype.Service;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @author: Leonezhurui
@@ -56,13 +61,16 @@ public class CourseServiceImpl implements CourseService {
 
     private final StudentAssignmentMapper studentAssignmentMapper;
 
+    private final ClassStudentMapper classStudentMapper;
+
     @Autowired
-    public CourseServiceImpl(CourseMapper courseMapper, UserMapper userMapper, CourseStudentMapper courseStudentMapper, AssignmentMapper assignmentMapper, StudentAssignmentMapper studentAssignmentMapper) {
+    public CourseServiceImpl(CourseMapper courseMapper, UserMapper userMapper, CourseStudentMapper courseStudentMapper, AssignmentMapper assignmentMapper, StudentAssignmentMapper studentAssignmentMapper, ClassStudentMapper classStudentMapper) {
         this.courseMapper = courseMapper;
         this.userMapper = userMapper;
         this.courseStudentMapper = courseStudentMapper;
         this.assignmentMapper = assignmentMapper;
         this.studentAssignmentMapper = studentAssignmentMapper;
+        this.classStudentMapper = classStudentMapper;
     }
 
 
@@ -310,6 +318,7 @@ public class CourseServiceImpl implements CourseService {
      * @param enrollDTO
      */
     @Override
+    @Transactional
     public void enroll(EnrollDTO enrollDTO) {
         Course target = courseMapper.selectById(enrollDTO.getCourseId());
 
@@ -320,6 +329,27 @@ public class CourseServiceImpl implements CourseService {
 
         Enrollment enrollment = ModelMapperUtil.map(enrollDTO, Enrollment.class);
         courseStudentMapper.insert(enrollment);
+
+        Long studentId = enrollDTO.getStudentId();
+        Long courseId = enrollDTO.getCourseId();
+
+
+        ClassStudent classStudent = classStudentMapper.findClassStudentByStudentAndCourse(studentId, courseId);
+
+        if (classStudent == null) {
+            log.warn("未找到学生 {} 在课程 {} 中的班级记录", studentId, courseId);
+            throw MyException.create(HttpStatus.BAD_REQUEST, "老师未将学生添加到课程班级,无法选课");
+        }
+        int result = classStudentMapper.updateStateById(classStudent.getId(), 1);
+
+
+        if (result > 0) {
+            log.info("成功更新学生 {} 在课程 {} 中的加入状态", studentId, courseId);
+            return;
+        }
+
+        log.error("更新学生 {} 在课程 {} 中的加入状态失败", studentId, courseId);
+        throw MyException.create(HttpStatus.BAD_REQUEST, "失败");
     }
 
 
@@ -443,4 +473,73 @@ public class CourseServiceImpl implements CourseService {
         return res;
     }
 
+
+    @Override
+    public StudentCourseHomeworkVO getStudentCourseAssignments(Long studentId, Long courseId) {
+        // 1. 验证学生是否存在
+        User student = userMapper.selectById(studentId);
+        if (student == null) {
+            throw new RuntimeException("学生不存在");
+        }
+
+        // 2. 验证课程是否存在
+        Course course = courseMapper.selectById(courseId);
+        if (course == null) {
+            throw new RuntimeException("课程不存在");
+        }
+
+        // 3. 查询该课程下的所有作业
+        List<Assignment> courseAssignments = assignmentMapper.selectByCourseId(courseId);
+        if (courseAssignments.isEmpty()) {
+            StudentCourseHomeworkVO result = new StudentCourseHomeworkVO();
+            result.setStudentId(studentId);
+            result.setOfficialNumber(student.getOfficialNumber());
+            result.setStuName(student.getName());
+            return result;
+        }
+
+        // 4. 查询学生在该课程下的所有作业提交情况
+        List<Long> assignmentIds = courseAssignments.stream()
+                .map(Assignment::getAssignmentId)
+                .collect(Collectors.toList());
+
+        List<StudentAssignment> studentAssignments = studentAssignmentMapper.findByStudentIdAndAssignmentIds(studentId, assignmentIds);
+
+        // 5. 构建作业ID到提交情况的映射
+        Map<Long, StudentAssignment> assignmentMap = studentAssignments.stream()
+                .collect(Collectors.toMap(
+                        StudentAssignment::getAssignmentId,
+                        item -> item
+                ));
+
+        // 6. 组装作业列表
+        List<StudentCourseAssignmentVO> assignmentVOs = courseAssignments.stream()
+                .map(assignment -> {
+                    StudentCourseAssignmentVO vo = new StudentCourseAssignmentVO();
+                    vo.setAssignmentId(assignment.getAssignmentId());
+                    vo.setAssignmentName(assignment.getAssignmentName());
+
+                    // 处理作业状态和分数
+                    StudentAssignment studentAssignment = assignmentMap.get(assignment.getAssignmentId());
+                    if (studentAssignment == null) {
+                        vo.setStatus(AssignmentCompletionStatus.NOT_SUBMITTED);
+                        vo.setScore(0);
+                    } else {
+                        vo.setStatus(AssignmentCompletionStatus.fromValue(studentAssignment.getStatus()));
+                        vo.setScore(studentAssignment.getScore() != null ?
+                                studentAssignment.getScore().intValue() : 0);
+                    }
+                    return vo;
+                })
+                .collect(Collectors.toList());
+
+        // 7. 组装返回结果
+        StudentCourseHomeworkVO result = new StudentCourseHomeworkVO();
+        result.setStudentId(studentId);
+        result.setOfficialNumber(student.getOfficialNumber());
+        result.setStuName(student.getName());
+        result.setAssignments(assignmentVOs);
+
+        return result;
+    }
 }

+ 13 - 0
src/main/java/com/njuzr/eaibackend/vo/StudentAssignmentStatusVO.java

@@ -0,0 +1,13 @@
+package com.njuzr.eaibackend.vo;
+
+import com.njuzr.eaibackend.enums.AssignmentCompletionStatus;
+import lombok.Data;
+
+@Data
+public class StudentAssignmentStatusVO {
+    private Long studentId;
+    private String officialNumber;
+    private String stuName;
+    private AssignmentCompletionStatus status;
+    private Integer score; // 成绩,未批改为0
+}

+ 12 - 0
src/main/java/com/njuzr/eaibackend/vo/StudentCourseAssignmentVO.java

@@ -0,0 +1,12 @@
+package com.njuzr.eaibackend.vo;
+
+import com.njuzr.eaibackend.enums.AssignmentCompletionStatus;
+import lombok.Data;
+
+@Data
+public class StudentCourseAssignmentVO {
+    private Long assignmentId; // 作业ID
+    private String assignmentName; // 作业名称
+    private AssignmentCompletionStatus status; // 作业状态
+    private Integer score; // 作业分数(未批改为0)
+}

+ 12 - 0
src/main/java/com/njuzr/eaibackend/vo/StudentCourseHomeworkVO.java

@@ -0,0 +1,12 @@
+package com.njuzr.eaibackend.vo;
+
+import lombok.Data;
+import java.util.List;
+
+@Data
+public class StudentCourseHomeworkVO {
+    private Long studentId; // 学生ID
+    private String officialNumber; // 学号
+    private String stuName; // 学生姓名
+    private List<StudentCourseAssignmentVO> assignments; // 作业列表
+}

+ 28 - 0
src/main/java/com/njuzr/eaibackend/vo/StudentInfoVO.java

@@ -0,0 +1,28 @@
+package com.njuzr.eaibackend.vo;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.njuzr.eaibackend.enums.ClassStudentState;
+import lombok.Data;
+
+@Data
+public class StudentInfoVO {
+    private String officialNumber;
+    private String stuName;
+    @JsonIgnore
+    private ClassStudentState state;
+    // 返回状态码
+    public int getStateCode() {
+        return state.getCode();
+    }
+
+    // 返回状态描述
+    public String getStateDesc() {
+        return state.getState();
+    }
+
+    public StudentInfoVO(String officialNumber, String stuName, ClassStudentState state) {
+        this.officialNumber = officialNumber;
+        this.stuName = stuName;
+        this.state = state;
+    }
+}

+ 10 - 0
src/main/resources/mapper/AssignmentMapper.xml

@@ -0,0 +1,10 @@
+<?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.AssignmentMapper">
+
+    <select id="selectByCourseId" resultType="com.njuzr.eaibackend.po.Assignment">
+        SELECT * FROM assignment
+        WHERE course_id = #{courseId}
+        ORDER BY create_time DESC
+    </select>
+</mapper>

+ 12 - 0
src/main/resources/mapper/ClassMapper.xml

@@ -0,0 +1,12 @@
+<?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.ClassMapper">
+
+    <!-- 其他内容 -->
+
+    <update id="increaseStuNumber">
+        UPDATE class
+        SET stu_number = stu_number + #{count}
+        WHERE class_id = #{classId}
+    </update>
+</mapper>

+ 25 - 0
src/main/resources/mapper/ClassStudentMapper.xml

@@ -0,0 +1,25 @@
+<?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.ClassStudentMapper">
+    <insert id="batchInsert" parameterType="list">
+        INSERT INTO class_student (state, official_number, stu_name, class_id)
+        VALUES
+        <foreach collection="list" item="student" separator=",">
+            (
+            #{student.state},
+            #{student.officialNumber},
+            #{student.stuName},
+            #{student.classId}
+            )
+        </foreach>
+    </insert>
+    <!-- 批量删除方法 -->
+    <delete id="batchDeleteByClassIdAndNumbers">
+        DELETE FROM class_student
+        WHERE class_id = #{classId}
+        AND official_number IN
+        <foreach collection="officialNumbers" item="number" open="(" separator="," close=")">
+            #{number}
+        </foreach>
+    </delete>
+</mapper>

+ 24 - 1
src/main/resources/mapper/StudentAssignmentMapper.xml

@@ -26,4 +26,27 @@
         WHERE student_id = #{studentId} AND assignment_id = #{assignmentId}
     </delete>
 
-</mapper>
+    <!-- 根据作业ID和学生ID列表查询 -->
+    <select id="findByAssignmentIdAndStudentIds" resultType="com.njuzr.eaibackend.po.StudentAssignment">
+        SELECT
+        *
+        FROM
+        student_assignment
+        WHERE
+        assignment_id = #{assignmentId}
+        AND student_id IN
+        <foreach collection="studentIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+    <select id="findByStudentIdAndAssignmentIds" resultType="com.njuzr.eaibackend.po.StudentAssignment">
+        SELECT * FROM student_assignment
+        WHERE student_id = #{studentId}
+        AND assignment_id IN
+        <foreach collection="assignmentIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+</mapper>

+ 15 - 1
src/main/resources/mapper/UserMapper.xml

@@ -61,4 +61,18 @@
         from users
         where pid = #{pid}
     </select>
-</mapper>
+
+    <!-- 根据学号列表查询用户ID -->
+    <select id="selectUserIdByOfficialNumbers" resultType="com.njuzr.eaibackend.po.User">
+        SELECT
+        id,
+        official_number
+        FROM
+        users
+        WHERE
+        official_number IN
+        <foreach collection="officialNumbers" item="number" open="(" separator="," close=")">
+            #{number}
+        </foreach>
+    </select>
+</mapper>