package nju.seec.helper.service.impl;
import cn.hutool.core.util.IdUtil;
import com.google.common.collect.Lists;
import nju.seec.helper.aspect.auth.LoginUser;
import nju.seec.helper.dao.*;
import nju.seec.helper.dto.question.BaseQuestionDTO;
import nju.seec.helper.dto.question.StudentAnswerDTO;
import nju.seec.helper.entity.QuestionRecord;
import nju.seec.helper.entity.Quiz;
import nju.seec.helper.enums.ExceptionType;
import nju.seec.helper.enums.QuizState;
import nju.seec.helper.enums.UserRole;
import nju.seec.helper.exception.HelperException;
import nju.seec.helper.service.QuestionService;
import nju.seec.helper.sys.bok.api.QuestionApi;
import nju.seec.helper.sys.bok.api.QuestionQueryApi;
import nju.seec.helper.sys.bok.api.RecordApi;
import nju.seec.helper.sys.bok.api.UserInteractiveApi;
import nju.seec.helper.sys.bok.dto.CollectDTO;
import nju.seec.helper.sys.bok.dto.RateDTO;
import nju.seec.helper.sys.bok.dto.RecordDTO;
import nju.seec.helper.sys.bok.vo.InteractiveVO;
import nju.seec.helper.sys.bok.vo.QuestionVO;
import nju.seec.helper.sys.bok.vo.RecordQuestionVO;
import nju.seec.helper.sys.dataanalysis.api.ExerciseApi;
import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord;
import nju.seec.helper.sys.ml.api.RecommendationApi;
import nju.seec.helper.vo.question.BaseQuestionVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author xst
*
* updated by cst
*/
@Service
public class QuestionServiceImpl implements QuestionService {
private final QuizDAO quizDAO;
private final UserDAO userDAO;
private final QuestionRecordDAO questionRecordDAO;
private final ChooseDAO chooseDAO;
private final CourseDAO courseDAO;
private final QuestionApi questionApi;
private final QuestionQueryApi questionQueryApi;
private final UserInteractiveApi userInteractiveApi;
private final RecordApi recordApi;
private final RecommendationApi recommendationApi;
private final ExerciseApi exerciseApi;
@Autowired
public QuestionServiceImpl(QuizDAO quizDAO
, UserDAO userDAO
, QuestionRecordDAO questionRecordDAO
, ChooseDAO chooseDAO
, CourseDAO courseDAO
, QuestionApi questionApi
, QuestionQueryApi questionQueryApi
, UserInteractiveApi userInteractiveApi
, RecordApi recordApi
, RecommendationApi recommendationApi
, ExerciseApi exerciseApi
) {
this.quizDAO = quizDAO;
this.userDAO = userDAO;
this.questionRecordDAO = questionRecordDAO;
this.chooseDAO = chooseDAO;
this.courseDAO = courseDAO;
this.questionApi = questionApi;
this.questionQueryApi = questionQueryApi;
this.userInteractiveApi = userInteractiveApi;
this.recordApi = recordApi;
this.recommendationApi = recommendationApi;
this.exerciseApi = exerciseApi;
}
@Override
public Page getQuestions(LoginUser user, String stem, String tag, String knowledgeId, Pageable pageable) {
return questionApi.getQuestionList(stem, tag, knowledgeId, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true));
}
@Transactional(readOnly = true)
@Override
public List getQuestionsByQuizId(LoginUser user, Long quizId) {
Quiz quiz = quizDAO.findQuizById(quizId);
return questionQueryApi.getQuestionListById(quiz.getQuestions())
.stream()
.map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, quiz.getState() == QuizState.CLOSED || user.getRole() == UserRole.TEACHER))
.collect(Collectors.toList());
}
@Override
public List getQuestionsByIds(List questionIds) {
return questionQueryApi.getQuestionListById(questionIds)
.stream()
.map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
.collect(Collectors.toList());
}
@Override
public BaseQuestionVO getOneQuestion(LoginUser user, String questionId) {
return BaseQuestionVO.convertBokQuestionToVO(questionApi.getQuestionsById(questionId), true);
}
@Transactional(rollbackFor = Exception.class)
@Override
public BaseQuestionVO createQuestion(LoginUser user, BaseQuestionDTO baseQuestionDTO) {
String questionId;
do {
// 保证不产生重复QuestionId
questionId = IdUtil.fastSimpleUUID();
} while (questionRecordDAO.existsByQuestionId(questionId));
QuestionVO questionVO = questionApi.createQuestion(questionId, baseQuestionDTO);
questionRecordDAO.save(new QuestionRecord()
.setTeacher(userDAO.findUserById(user.getId()))
.setQuestionId(questionVO.getId()));
return BaseQuestionVO.convertBokQuestionToVO(questionVO, true);
}
@Transactional(readOnly = true)
@Override
public BaseQuestionVO modifyQuestion(LoginUser user, String questionId, BaseQuestionDTO baseQuestionDTO) {
QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId)
.orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该问题"));
QuestionVO questionVO = questionApi.updateQuestion(questionId, baseQuestionDTO);
questionRecordDAO.save(questionRecord);
return BaseQuestionVO.convertBokQuestionToVO(questionVO, true);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteQuestion(LoginUser user, String questionId) {
QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId)
.orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该问题"));
questionApi.deleteQuestion(questionId);
questionRecordDAO.delete(questionRecord);
}
@Transactional(readOnly = true)
@Override
public Page getCreatedQuestions(LoginUser user, Pageable pageable) {
Page questionIdPage = questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId()), pageable);
return new PageImpl<>(
questionQueryApi.getQuestionListById(questionIdPage.getContent())
.stream()
.map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
.collect(Collectors.toList())
, pageable
, questionIdPage.getTotalElements()
);
}
@Transactional(readOnly = true)
@Override
public Set getIdsOfCreatedQuestions(LoginUser user) {
return questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId()));
}
@Override
public void collectQuestion(LoginUser user, String questionId, CollectDTO collectDTO) {
userInteractiveApi.collectQuestion(String.valueOf(user.getPid()), questionId, collectDTO);
}
@Override
public void rateQuestion(LoginUser user, String questionId, RateDTO rateDTO) {
userInteractiveApi.rateQuestion(String.valueOf(user.getPid()), questionId, rateDTO);
}
@Transactional(readOnly = true)
@Override
public Page getCollectedAndRatedQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable) {
return convert(user, userInteractiveApi.getCollectAndRateQuestions(String.valueOf(user.getPid()), collect, rate, pageable));
}
@Transactional(readOnly = true)
@Override
public Page getCompletedQuestions(LoginUser user, Pageable pageable) {
RecordQuestionVO recordQuestionVO = recordApi.getUserRecordQuestions(String.valueOf(user.getPid()), pageable);
return convert(user, new PageImpl<>(recordQuestionVO.getQuestions(), pageable, recordQuestionVO.getPage().getTotalElements()));
}
@Override
public List getInteractiveList(LoginUser user, String questionId) {
return userInteractiveApi.getInteractive(String.valueOf(user.getPid()), questionId);
}
@Override
public List getRecommendationQuestions(LoginUser user, List knowledgeId, Integer questionNum) {
return questionQueryApi.getQuestionListById(recommendationApi.getRecommendResult(String.valueOf(user.getPid()), knowledgeId, questionNum))
.stream()
.map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, true))
.collect(Collectors.toList());
}
@Transactional(readOnly = true)
@Override
public List getRecommendationQuestionsByCourse(LoginUser user, Long courseId, Integer questionNum) {
return this.getRecommendationQuestions(user, courseDAO.findCourseById(courseId).getKnowledgeId(), questionNum);
}
@Override
public void record(LoginUser user, StudentAnswerDTO studentAnswerDTO) {
if (studentAnswerDTO.getRecords() == null || studentAnswerDTO.getRecords().isEmpty()) {
return;
}
Map recordMap = studentAnswerDTO.getRecords()
.entrySet()
.stream()
.filter(entry -> entry.getValue().getStartAt().isBefore(entry.getValue().getEndAt()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (recordMap.isEmpty()) {
return;
}
List baseQuestionVOList = questionQueryApi.getQuestionListById(Lists.newArrayList(recordMap.keySet()))
.stream()
.map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, true))
.collect(Collectors.toList());
// 创建两个列表
int size = recordMap.size();
List exerciseRecords = Lists.newArrayListWithExpectedSize(size);
List records = Lists.newArrayListWithExpectedSize(size);
// 填充列表
String userId = String.valueOf(user.getPid());
baseQuestionVOList.forEach(baseQuestionVO -> {
String questionId = baseQuestionVO.getId();
StudentAnswerDTO.AnswerRecordDTO answerRecord = recordMap.get(questionId);
Object answer = answerRecord.getAnswer();
Boolean pass = baseQuestionVO.pass(answer);
exerciseRecords.add(exerciseApi.createExerciseRecordDTO(userId, questionId, answerRecord.getStartAt(), answerRecord.getEndAt(), pass));
records.add(recordApi.createRecordDTO(userId, questionId, answer, pass, answerRecord.getStartAt(), answerRecord.getEndAt()));
});
exerciseApi.reportExercise(exerciseRecords);
recordApi.reportUserRecord(records);
}
private static final Set HIDE_QUESTION_ANSWER_STATES = Collections.singleton(QuizState.ONGOING);
private Page convert(LoginUser user, Page questionVOList) {
List hideQuestionIds = quizDAO.findByCourseIdInAndStateIn(chooseDAO.findCourseIdsByStudentId(user.getId()), HIDE_QUESTION_ANSWER_STATES)
.stream()
.flatMap(quiz -> quiz.getQuestions().stream())
.collect(Collectors.toList());
return questionVOList.map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, !hideQuestionIds.contains(questionVO.getId())));
}
}