AssignmentServiceImpl.java 29 KB

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