AssignmentServiceImpl.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. package com.njuzr.eaibackend.service.impl;
  2. import com.alibaba.fastjson2.JSONObject;
  3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4. import com.baomidou.mybatisplus.core.metadata.IPage;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.njuzr.eaibackend.dto.AssignmentDTO;
  7. import com.njuzr.eaibackend.dto.AssignmentQueryDTO;
  8. import com.njuzr.eaibackend.dto.AssignmentUpdateDTO;
  9. import com.njuzr.eaibackend.dto.CorrectDTO;
  10. import com.njuzr.eaibackend.enums.AssignmentCompletionStatus;
  11. import com.njuzr.eaibackend.enums.AssignmentStatus;
  12. import com.njuzr.eaibackend.enums.Role;
  13. import com.njuzr.eaibackend.exception.MyException;
  14. import com.njuzr.eaibackend.mapper.*;
  15. import com.njuzr.eaibackend.po.*;
  16. import com.njuzr.eaibackend.po.Class;
  17. import com.njuzr.eaibackend.service.AssignmentService;
  18. import com.njuzr.eaibackend.service.ClassService;
  19. import com.njuzr.eaibackend.service.TextAnalysisService;
  20. import com.njuzr.eaibackend.utils.*;
  21. import com.njuzr.eaibackend.vo.*;
  22. import lombok.extern.slf4j.Slf4j;
  23. import org.springframework.beans.BeanUtils;
  24. import org.springframework.cache.annotation.CacheEvict;
  25. import org.springframework.http.HttpStatus;
  26. import org.springframework.stereotype.Service;
  27. import org.springframework.transaction.annotation.Transactional;
  28. import java.time.LocalDateTime;
  29. import java.util.*;
  30. import java.util.function.Function;
  31. import java.util.stream.Collectors;
  32. /**
  33. * @author: Leonezhurui
  34. * @Date: 2024/3/15 - 00:40
  35. * @Package: EAI-Backend
  36. */
  37. @Slf4j
  38. @Service
  39. public class AssignmentServiceImpl implements AssignmentService {
  40. private final AssignmentMapper assignmentMapper;
  41. private final CourseMapper courseMapper;
  42. private final StudentAssignmentMapper studentAssignmentMapper;
  43. private final UserMapper userMapper;
  44. private final ClassService classService;
  45. private final OssUtil ossUtil;
  46. private final FileUtil fileUtil = new FileUtil();
  47. private final TextAnalysisService textAnalysisService;
  48. public AssignmentServiceImpl(AssignmentMapper assignmentMapper,
  49. CourseMapper courseMapper,
  50. StudentAssignmentMapper studentAssignmentMapper,
  51. UserMapper userMapper,
  52. OssUtil ossUtil,
  53. CourseServiceImpl courseServiceImpl,
  54. CourseStudentMapper courseStudentMapper,
  55. ClassService classService,
  56. TextAnalysisService textAnalysisService) {
  57. this.assignmentMapper = assignmentMapper;
  58. this.courseMapper = courseMapper;
  59. this.studentAssignmentMapper = studentAssignmentMapper;
  60. this.userMapper = userMapper;
  61. this.ossUtil = ossUtil;
  62. this.textAnalysisService = textAnalysisService;
  63. this.classService = classService;
  64. }
  65. @Override
  66. public AssignmentVO createAssignment(MyUserDetails user, Long courseId, AssignmentDTO assignmentDTO) {
  67. Course course = courseMapper.selectById(courseId);
  68. if (course == null)
  69. throw MyException.create(HttpStatus.BAD_REQUEST, "课程不存在");
  70. if (user.getRole() != Role.ADMIN) {
  71. List<String> teacherIds = Collections.singletonList(Optional.ofNullable(course.getTeacherIds())
  72. .orElse(Collections.emptyList().toString()));
  73. String userIdStr = String.valueOf(user.getId());
  74. boolean hasTeacherId = teacherIds.contains(userIdStr);
  75. boolean pidMatches = Objects.equals(course.getTeacherPid(), user.getPid());
  76. if (!hasTeacherId && !pidMatches) {
  77. throw MyException.create(HttpStatus.BAD_REQUEST, "无权限创建");
  78. }
  79. }
  80. Assignment target = convertToPO(assignmentDTO);
  81. target.setCourseId(courseId);
  82. target.setTeacherId(user.getId());
  83. target.setEngageNumber(0);
  84. target.setCorrectNumber(0);
  85. // TODO:权宜之计
  86. // target.setTeacherId(Long.valueOf(user.getPid()));
  87. target.setCreateTime(new Date());
  88. try {
  89. int code = assignmentMapper.insert(target);
  90. if (code == 0)
  91. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "创建作业失败");
  92. } catch (Exception e) {
  93. log.error(e.getMessage());
  94. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "创建作业失败");
  95. }
  96. return convertToVO(target);
  97. }
  98. @Override
  99. // @Cacheable(value = "assignmentInfo", key = "'assignment_' + #assignmentId")
  100. public AssignmentVO findAssignmentById(Long assignmentId) {
  101. QueryWrapper<Assignment> wrapper = new QueryWrapper<>();
  102. wrapper.eq("assignment_id", assignmentId);
  103. Assignment res = assignmentMapper.selectOne(wrapper);
  104. log.info("数据库查询作业:{}", assignmentId);
  105. if (res == null)
  106. throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
  107. return convertToVO(res);
  108. }
  109. @Override
  110. public IPage<AssignmentVO> findAssignmentPage(Page<Assignment> page, AssignmentQueryDTO assignmentQueryDTO) {
  111. log.info("作业查询服务:{}", assignmentQueryDTO);
  112. QueryWrapper<Assignment> wrapper = new QueryWrapper<>();
  113. if (assignmentQueryDTO.getAssignmentId() != null) { // assignmentId直接判断是否相等
  114. // TODO 检测assignmentId是否正确
  115. wrapper.eq("assignmentId", assignmentQueryDTO.getAssignmentId());
  116. }
  117. if (assignmentQueryDTO.getTeacherId() != null) { // teacherId直接匹配
  118. wrapper.eq("teacher_id", assignmentQueryDTO.getTeacherId());
  119. }
  120. if (assignmentQueryDTO.getCourseId() != null) { // teacherId直接匹配
  121. wrapper.eq("course_id", assignmentQueryDTO.getCourseId());
  122. }
  123. if (assignmentQueryDTO.getAssignmentName() != null) { // 模糊匹配作业名字
  124. wrapper.like("assignment_name", assignmentQueryDTO.getAssignmentName());
  125. }
  126. if(assignmentQueryDTO.getType() != null){
  127. wrapper.eq("type",assignmentQueryDTO.getType());
  128. }
  129. if (assignmentQueryDTO.getStatus() != null) {
  130. AssignmentStatus status = assignmentQueryDTO.getStatus();
  131. if (status == AssignmentStatus.NOT_STARTED) {
  132. wrapper.gt("start_time", LocalDateTime.now()); // 意思是start_time > now()
  133. } else if (status == AssignmentStatus.PROCEEDING) {
  134. wrapper.le("start_time", LocalDateTime.now())
  135. .ge("end_time", LocalDateTime.now());
  136. } else {
  137. wrapper.lt("end_time", LocalDateTime.now());
  138. }
  139. }
  140. IPage<Assignment> assignments = assignmentMapper.selectPage(page, wrapper);
  141. return PageMapperUtil.convert(assignments, this::convertToVO);
  142. }
  143. @Override
  144. @CacheEvict(value = "assignmentInfo", key = "'assignment_' + #assignmentId")
  145. public AssignmentVO updateAssignment(MyUserDetails user, Long assignmentId, AssignmentUpdateDTO assignmentUpdateDTO) {
  146. Assignment target = assignmentMapper.selectById(assignmentId);
  147. if (!Objects.equals(target.getTeacherId(), user.getId()) && !Objects.equals(target.getTeacherPid(), user.getPid()))
  148. throw MyException.create(HttpStatus.BAD_REQUEST, "无权限更新");
  149. Assignment ope = convertToPO(assignmentUpdateDTO);
  150. ope.setAssignmentId(assignmentId);
  151. try {
  152. int code = assignmentMapper.updateById(ope);
  153. if (code == 0)
  154. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "服务器更新失败");
  155. } catch (Exception e) {
  156. log.error(e.getMessage());
  157. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "服务器更新失败");
  158. }
  159. return convertToVO(assignmentMapper.selectById(assignmentId));
  160. }
  161. /**
  162. * 教师删除作业
  163. */
  164. @Override
  165. @Transactional
  166. @CacheEvict(value = "assignmentInfo", key = "'assignment_' + #assignmentId")
  167. public void deleteById(Long teacherId, Long assignmentId) {
  168. Assignment target = assignmentMapper.selectById(assignmentId);
  169. if (!Objects.equals(target.getTeacherId(), teacherId))
  170. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "无权限删除");
  171. try {
  172. List<Engagement> engagements = studentAssignmentMapper.findEngagementByAssignmentId(assignmentId);
  173. for(Engagement engagement : engagements) {
  174. studentAssignmentMapper.delete(engagement.getStudentId(), assignmentId);
  175. }
  176. int code = assignmentMapper.deleteById(assignmentId);
  177. if (code == 0) {
  178. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "删除作业错误");
  179. }
  180. } catch (Exception e) {
  181. log.error(e.getMessage());
  182. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "删除作业错误");
  183. }
  184. }
  185. @Override
  186. public void engageAssignment(Long studentId, Long assignmentId) {
  187. if(studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId) != null){
  188. return;
  189. }
  190. // 1创建Engagement实体类,初始化基本信息
  191. Engagement engagement = new Engagement();
  192. engagement.setStudentId(studentId);
  193. engagement.setAssignmentId(assignmentId);
  194. engagement.setVersion(1); // 设置这是version
  195. engagement.setStatus(AssignmentCompletionStatus.NOT_SUBMITTED); // 已经进入到页面编辑,但是还没有提交
  196. Assignment assignment = assignmentMapper.selectById(assignmentId);
  197. assignment.setEngageNumber(assignment.getEngageNumber() + 1);
  198. // // 2通过FileUtil创建一个空的docx文件
  199. // InputStream inputStream = fileUtil.createEmptyDocx();
  200. //
  201. // String filePath = assignmentId + "_" + studentId + ".docx";
  202. // // 3通过OssUtil工具类上传空文件,注意给其命名为`assignmentId/studentId.docx`,并得到文件链接
  203. // String fileName = ossUtil.uploadEnduringFile(inputStream, filePath);
  204. // engagement.setFileUrl(fileName);
  205. try {
  206. int code = studentAssignmentMapper.insert(engagement);
  207. int code1 = assignmentMapper.updateById(assignment);
  208. if (code == 0 || code1 == 0)
  209. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "参加作业失败");
  210. } catch (Exception e) {
  211. log.error(e.getMessage());
  212. // ossUtil.deleteFile(filePath);
  213. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "参加作业失败");
  214. }
  215. }
  216. /**
  217. * 根据两个外键,搜索学生参与作业的情况(需要在mysql中设置索引)
  218. *
  219. * @param studentId
  220. * @param assignmentId
  221. * @return
  222. */
  223. @Override
  224. public EngagementVO getEngagement(Long studentId, Long assignmentId) {
  225. Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
  226. if (engagement == null)
  227. throw MyException.create(HttpStatus.BAD_REQUEST, "参与作业情况不存在");
  228. return convertToVO(engagement);
  229. }
  230. // 返回顺序: 先是未批改的,然后是批改的按照成绩从高到低返回
  231. @Override
  232. public IPage<EngagementVO> findEngagementPage(Page<Engagement> page, Long assignmentId) {
  233. IPage<Engagement> engagements = studentAssignmentMapper.findEngagementByAssignmentId(page, assignmentId);
  234. return PageMapperUtil.convert(engagements, this::convertToVO);
  235. }
  236. @Override
  237. public EngagementDetailVO getDetailEngagement(Long studentId, Long assignmentId) {
  238. // 1 获取用户详细信息
  239. User student = userMapper.selectById(studentId);
  240. if (student == null)
  241. throw MyException.create(HttpStatus.BAD_REQUEST, "学生不存在");
  242. // 2 获取作业详细信息
  243. Assignment assignment = assignmentMapper.selectById(assignmentId);
  244. if (assignment == null)
  245. throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
  246. // 3 获取作业参与详细信息
  247. Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
  248. if (engagement == null)
  249. throw MyException.create(HttpStatus.BAD_REQUEST, "参与作业情况不存在");
  250. EngagementDetailVO detailVO = ModelMapperUtil.map(engagement, EngagementDetailVO.class);
  251. detailVO.setUserVO(ModelMapperUtil.map(student, UserVO.class));
  252. detailVO.setAssignmentVO(convertToVO(assignment));
  253. // detailVO.setFileKey(assignmentId + "_" + studentId + "_" + engagement.getVersion());
  254. log.info("请求到详细信息:" + detailVO);
  255. return detailVO;
  256. }
  257. /**
  258. * 作业提交,更新文件版本和作业状态
  259. *
  260. * @param studentId
  261. * @param assignmentId
  262. */
  263. @Override
  264. public void engagementSubmit(Long studentId, Long assignmentId) {
  265. Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
  266. if (engagement == null)
  267. throw MyException.create(HttpStatus.BAD_REQUEST, "参与作业情况不存在");
  268. int originalVersion = engagement.getVersion();
  269. engagement.setVersion(originalVersion + 1);
  270. engagement.setStatus(AssignmentCompletionStatus.SUBMITTED);
  271. try {
  272. int code = studentAssignmentMapper.updateById(engagement);
  273. if (code == 0)
  274. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "更新文件版本失败");
  275. } catch (Exception e) {
  276. log.error(e.getMessage());
  277. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "更新文件版本失败");
  278. }
  279. }
  280. /**
  281. * 作业暂存,更新文件版本,不修改作业状态
  282. *
  283. * @param studentId
  284. * @param assignmentId
  285. */
  286. @Override
  287. public void engagementStage(Long studentId, Long assignmentId, JSONObject jsonObject) {
  288. Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
  289. if (engagement == null)
  290. throw MyException.create(HttpStatus.BAD_REQUEST, "参与作业情况不存在");
  291. int originalVersion = engagement.getVersion();
  292. String quillContent = jsonObject.getString("quillContent");
  293. String textContent = jsonObject.getString("textContent");
  294. String htmlContent = jsonObject.getString("htmlContent");
  295. engagement.setVersion(originalVersion + 1);
  296. engagement.setQuillContent(quillContent);
  297. engagement.setTextContent(textContent);
  298. engagement.setHtmlContent(htmlContent);
  299. try {
  300. int code = studentAssignmentMapper.updateById(engagement);
  301. if (code == 0)
  302. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "更新文件版本失败");
  303. // 异步启动文本分析,不影响主流程返回速度
  304. try {
  305. textAnalysisService.analyzeAndSaveTextContentAsync(studentId, assignmentId);
  306. log.info("已启动异步文本分析 - studentId: {}, assignmentId: {}", studentId, assignmentId);
  307. } catch (Exception e) {
  308. log.error("启动异步文本分析失败 - studentId: {}, assignmentId: {}, error: {}",
  309. studentId, assignmentId, e.getMessage());
  310. // 不抛出异常,避免影响主流程
  311. }
  312. } catch (Exception e) {
  313. log.error(e.getMessage());
  314. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "更新文件版本失败");
  315. }
  316. }
  317. @Override
  318. public void engagementCorrect(Long teacherId, Long studentId, Long assignmentId, CorrectDTO correctDTO) {
  319. Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
  320. if (engagement == null)
  321. throw MyException.create(HttpStatus.BAD_REQUEST, "参与作业情况不存在");
  322. Assignment assignment = assignmentMapper.selectById(assignmentId);
  323. if (assignment == null)
  324. throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
  325. if (!Objects.equals(assignment.getTeacherId(), teacherId))
  326. throw MyException.create(HttpStatus.BAD_REQUEST, "无权限批改");
  327. if(engagement.getStatus() != AssignmentCompletionStatus.CORRECTED){
  328. assignment.setCorrectNumber(assignment.getCorrectNumber() + 1);
  329. engagement.setStatus(AssignmentCompletionStatus.CORRECTED);
  330. }
  331. engagement.setScore(correctDTO.getScore());
  332. engagement.setRemark(correctDTO.getRemark());
  333. try {
  334. int code = studentAssignmentMapper.updateById(engagement);
  335. int code1 = assignmentMapper.updateById(assignment);
  336. if (code == 0 || code1 == 0)
  337. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "批改作业失败");
  338. } catch (Exception e) {
  339. log.error(e.getMessage());
  340. throw MyException.create(HttpStatus.INTERNAL_SERVER_ERROR, "批改作业失败");
  341. }
  342. }
  343. @Override
  344. public long getNextStudentId(Long assignmentId, Long studentId) {
  345. List<Engagement> allRecords = studentAssignmentMapper.selectList(
  346. new QueryWrapper<Engagement>()
  347. .eq("assignment_id", assignmentId)
  348. // 可以在此添加其他查询条件
  349. );
  350. // 对记录进行排序:status为4或6的记录排到最后,其他记录保持原始顺序
  351. List<Engagement> sortedList = allRecords.stream()
  352. .sorted(Comparator.comparing(
  353. e -> e.getStatus() == AssignmentCompletionStatus.PUBLISHED ||
  354. e.getStatus() == AssignmentCompletionStatus.CORRECTED
  355. )).toList();
  356. int currentIndex = -1;
  357. for (int i = 0; i < sortedList.size(); i++) {
  358. if (sortedList.get(i).getStudentId().equals(studentId)) {
  359. currentIndex = i;
  360. break;
  361. }
  362. }
  363. if (currentIndex == allRecords.size() - 1 || currentIndex == -1) {
  364. return sortedList.get(0).getStudentId();
  365. }
  366. return sortedList.get(currentIndex + 1).getStudentId();
  367. }
  368. @Override
  369. public EngageNumberVo getEngageNumber(Long assignmentId) {
  370. Assignment assignment = assignmentMapper.selectById(assignmentId);
  371. if(assignment == null){
  372. throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
  373. }
  374. if(assignment.getEngageNumber() != null && assignment.getCorrectNumber() != null){
  375. return new EngageNumberVo(assignment.getEngageNumber(), assignment.getCorrectNumber());
  376. } else {
  377. throw MyException.create(HttpStatus.BAD_REQUEST, "未建立作业统计");
  378. }
  379. }
  380. private Assignment convertToPO(AssignmentDTO assignmentDTO) {
  381. Assignment assignment = new Assignment();
  382. BeanUtils.copyProperties(assignmentDTO, assignment);
  383. if (assignmentDTO.getAttachments() != null)
  384. assignment.setAttachments(ConvertUtil.listToString(assignmentDTO.getAttachments()));
  385. return assignment;
  386. }
  387. private Assignment convertToPO(AssignmentUpdateDTO assignmentDTO) {
  388. Assignment assignment = new Assignment();
  389. BeanUtils.copyProperties(assignmentDTO, assignment);
  390. if (assignmentDTO.getAttachments() != null)
  391. assignment.setAttachments(ConvertUtil.listToString(assignmentDTO.getAttachments()));
  392. return assignment;
  393. }
  394. private AssignmentVO convertToVO(Assignment assignment) {
  395. AssignmentVO res = new AssignmentVO();
  396. BeanUtils.copyProperties(assignment, res);
  397. if (assignment.getAttachments() != null)
  398. res.setAttachments(ConvertUtil.stringToList(assignment.getAttachments(), String::valueOf));
  399. res.setStatus(calculateStatus(res.getStartTime(), res.getEndTime()));
  400. // 组装老师姓名
  401. res.setTeacherName(userMapper.selectNameById(assignment.getTeacherId()));
  402. return res;
  403. }
  404. private EngagementVO convertToVO(Engagement engagement) {
  405. EngagementVO res = ModelMapperUtil.map(engagement, EngagementVO.class);
  406. // 组装文件key
  407. // res.setFileKey(res.getAssignmentId() + "_" + res.getStudentId() + "_" + res.getVersion());
  408. // 组装学生姓名
  409. String studentName = userMapper.selectNameById(engagement.getStudentId());
  410. res.setStudentName(studentName);
  411. return res;
  412. }
  413. private AssignmentStatus calculateStatus(Date startTime, Date endTime) {
  414. Date now = new Date();
  415. if (now.before(startTime)) {
  416. return AssignmentStatus.NOT_STARTED;
  417. } else if (now.after(endTime)) {
  418. return AssignmentStatus.FINISHED;
  419. } else {
  420. return AssignmentStatus.PROCEEDING;
  421. }
  422. }
  423. @Override
  424. public List<StudentAssignmentStatusVO> getStudentsAssignmentStatus(Long classId, Long assignmentId) {
  425. // 1. 验证班级和作业存在性
  426. Assignment assignment = assignmentMapper.selectById(assignmentId);
  427. if (assignment == null) {
  428. throw new RuntimeException("作业不存在"); // 建议替换为项目自定义异常
  429. }
  430. // 2. 获取班级所有学生(从class_student表)
  431. List<StudentInfoVO> classStudents = classService.getStudentsByClassId(classId);
  432. if (classStudents.isEmpty()) {
  433. return Collections.emptyList();
  434. }
  435. // 3. 提取学号列表,查询对应的用户ID(users表)
  436. List<String> officialNumbers = classStudents.stream()
  437. .map(StudentInfoVO::getOfficialNumber)
  438. .collect(Collectors.toList());
  439. List<User> users = userMapper.selectUserIdByOfficialNumbers(officialNumbers);
  440. Map<String, Long> officialNumberToUserIdMap = users.stream()
  441. .collect(Collectors.toMap(
  442. User::getOfficialNumber,
  443. User::getId
  444. ));
  445. // 4. 查询这些学生的作业提交情况(student_assignment表)
  446. List<Long> studentIds = new ArrayList<>(officialNumberToUserIdMap.values());
  447. List<StudentAssignment> studentAssignments = studentAssignmentMapper.findByAssignmentIdAndStudentIds(assignmentId, studentIds);
  448. // 5. 构建学生ID到作业情况的映射
  449. Map<Long, StudentAssignment> studentIdToAssignmentMap = studentAssignments.stream()
  450. .collect(Collectors.toMap(
  451. StudentAssignment::getStudentId,
  452. Function.identity()
  453. ));
  454. // 6. 组装返回结果
  455. return classStudents.stream()
  456. .map(student -> {
  457. StudentAssignmentStatusVO vo = new StudentAssignmentStatusVO();
  458. vo.setOfficialNumber(student.getOfficialNumber());
  459. vo.setStuName(student.getStuName());
  460. // 设置学生ID
  461. Long studentId = officialNumberToUserIdMap.get(student.getOfficialNumber());
  462. vo.setStudentId(studentId);
  463. // 设置状态和成绩
  464. StudentAssignment studentAssignment = studentIdToAssignmentMap.get(studentId);
  465. if (studentAssignment == null) {
  466. // 未参与作业,按未提交处理
  467. vo.setStatus(AssignmentCompletionStatus.NOT_SUBMITTED);
  468. vo.setScore(0);
  469. } else {
  470. // 转换状态为枚举
  471. vo.setStatus(AssignmentCompletionStatus.fromValue(studentAssignment.getStatus()));
  472. // 处理成绩(BigDecimal转Integer,未批改为0)
  473. vo.setScore(studentAssignment.getScore() != null ?
  474. studentAssignment.getScore().intValue() : 0);
  475. }
  476. return vo;
  477. })
  478. .collect(Collectors.toList());
  479. }
  480. }