package com.github.tangyi.exam.service;

import com.github.pagehelper.PageInfo;
import com.github.tangyi.common.basic.vo.UserVo;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.MqConstant;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.ResponseUtil;
import com.github.tangyi.common.security.utils.SysUtil;
import com.github.tangyi.exam.api.constants.AnswerConstant;
import com.github.tangyi.exam.api.dto.AnswerDto;
import com.github.tangyi.exam.api.dto.RankInfoDto;
import com.github.tangyi.exam.api.dto.StartExamDto;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.enums.SubmitStatusEnum;
import com.github.tangyi.exam.api.module.Answer;
import com.github.tangyi.exam.api.module.Examination;
import com.github.tangyi.exam.api.module.ExaminationRecord;
import com.github.tangyi.exam.api.module.ExaminationSubject;
import com.github.tangyi.exam.enums.SubjectTypeEnum;
import com.github.tangyi.exam.handler.AnswerHandleResult;
import com.github.tangyi.exam.handler.impl.ChoicesAnswerHandler;
import com.github.tangyi.exam.handler.impl.JudgementAnswerHandler;
import com.github.tangyi.exam.handler.impl.MultipleChoicesAnswerHandler;
import com.github.tangyi.exam.handler.impl.ShortAnswerHandler;
import com.github.tangyi.exam.mapper.AnswerMapper;
import com.github.tangyi.exam.utils.AnswerHandlerUtil;
import com.github.tangyi.exam.utils.ExamRecordUtil;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 答题service
 *
 * @author tangyi
 * @date 2018/11/8 21:17
 */
@Slf4j
@AllArgsConstructor
@Service
public class AnswerService extends CrudService<AnswerMapper, Answer> {

	private final UserServiceClient userServiceClient;

    private final AmqpTemplate amqpTemplate;

    private final SubjectService subjectService;

    private final ExamRecordService examRecordService;

    private final ExaminationService examinationService;

    private final ExaminationSubjectService examinationSubjectService;

    private final ChoicesAnswerHandler choicesHandler;

	private final MultipleChoicesAnswerHandler multipleChoicesHandler;

	private final JudgementAnswerHandler judgementHandler;

	private final ShortAnswerHandler shortAnswerHandler;

	private final RedisTemplate<String, String> redisTemplate;

	/**
     * 查找答题
     *
     * @param answer answer
     * @return Answer
     * @author tangyi
     * @date 2019/1/3 14:27
     */
    @Override
    @Cacheable(value = "answer#" + CommonConstant.CACHE_EXPIRE, key = "#answer.id")
    public Answer get(Answer answer) {
        return super.get(answer);
    }

    /**
     * 根据用户ID、考试ID、考试记录ID、题目ID查找答题
     *
     * @param answer answer
     * @return Answer
     * @author tangyi
     * @date 2019/01/21 19:41
     */
    public Answer getAnswer(Answer answer) {
        return this.dao.getAnswer(answer);
    }

    /**
     * 更新答题
     *
     * @param answer answer
     * @return int
     * @author tangyi
     * @date 2019/1/3 14:27
     */
    @Override
    @Transactional
    @CacheEvict(value = "answer", key = "#answer.id")
    public int update(Answer answer) {
    	answer.setAnswer(AnswerHandlerUtil.replaceComma(answer.getAnswer()));
        return super.update(answer);
    }

    /**
     * 更新答题总分
     *
     * @param answer answer
     * @return int
     * @author tangyi
     * @date 2019/1/3 14:27
     */
    @Transactional
    @CacheEvict(value = "answer", key = "#answer.id")
    public int updateScore(Answer answer) {
        answer.setAnswer(AnswerHandlerUtil.replaceComma(answer.getAnswer()));
        // 加分减分逻辑
        Answer oldAnswer = this.get(answer);
        if (!oldAnswer.getAnswerType().equals(answer.getAnswerType())) {
            ExaminationRecord record = new ExaminationRecord();
            record.setId(oldAnswer.getExamRecordId());
            record = examRecordService.get(record);
            if (record == null) {
                throw new CommonException("ExamRecord is null");
            }
            Double oldScore = record.getScore();
            if (AnswerConstant.RIGHT.equals(answer.getAnswerType())) {
                // 加分
                record.setCorrectNumber(record.getInCorrectNumber() + 1);
                record.setInCorrectNumber(record.getInCorrectNumber() - 1);
                record.setScore(record.getScore() + answer.getScore());
            } else if (AnswerConstant.WRONG.equals(answer.getAnswerType())) {
                // 减分
                record.setCorrectNumber(record.getInCorrectNumber() - 1);
                record.setInCorrectNumber(record.getInCorrectNumber() + 1);
                record.setScore(record.getScore() - answer.getScore());
            }
            if (examRecordService.update(record) > 0) {
                log.info("Update answer success, examRecordId: {}, oldScore: {}, newScore: {}", oldAnswer.getExamRecordId(), oldScore, record.getScore());
            }
        }
        return super.update(answer);
    }

    /**
     * 删除答题
     *
     * @param answer answer
     * @return int
     * @author tangyi
     * @date 2019/1/3 14:27
     */
    @Override
    @Transactional
    @CacheEvict(value = "answer", key = "#answer.id")
    public int delete(Answer answer) {
        return super.delete(answer);
    }

    /**
     * 批量删除答题
     *
     * @param ids ids
     * @return int
     * @author tangyi
     * @date 2019/1/3 14:27
     */
    @Override
    @Transactional
    @CacheEvict(value = "answer", allEntries = true)
    public int deleteAll(Long[] ids) {
        return super.deleteAll(ids);
    }

    /**
     * 保存
     *
     * @param answer answer
     * @return int
     * @author tangyi
     * @date 2019/04/30 18:03
     */
    @Transactional
    public int save(Answer answer) {
        answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
		answer.setAnswer(AnswerHandlerUtil.replaceComma(answer.getAnswer()));
		return super.save(answer);
    }

    /**
     * 保存答题,返回下一题信息
     *
     * @param answerDto       answerDto
     * @param type            0:下一题,1:上一题
     * @param nextSubjectId   nextSubjectId
     * @param nextSubjectType 下一题的类型,选择题、判断题
     * @return SubjectDto
     * @author tangyi
     * @date 2019/05/01 11:42
     */
    @Transactional
    public SubjectDto saveAndNext(AnswerDto answerDto, Integer type, Long nextSubjectId, Integer nextSubjectType) {
		String userCode = SysUtil.getUser();
		String sysCode = SysUtil.getSysCode();
		String tenantCode = SysUtil.getTenantCode();
		if (this.save(answerDto, userCode, sysCode, tenantCode) > 0) {
			// 查询下一题
			return this.subjectAnswer(answerDto.getSubjectId(), answerDto.getExamRecordId(),
					type, nextSubjectId, nextSubjectType);
		}
        return null;
    }

	/**
	 * 保存答题
	 *
	 * @param answerDto       answerDto
	 * @param userCode       userCode
	 * @param sysCode       sysCode
	 * @param tenantCode       tenantCode
	 * @return int
	 * @author tangyi
	 * @date 2019/05/01 11:42
	 */
	@Transactional
	public int save(AnswerDto answerDto, String userCode, String sysCode, String tenantCode) {
		Answer answer = new Answer();
		BeanUtils.copyProperties(answerDto, answer);
		Answer tempAnswer = this.getAnswer(answer);
		if (tempAnswer != null) {
			tempAnswer.setCommonValue(userCode, sysCode, tenantCode);
			tempAnswer.setAnswer(answer.getAnswer());
			tempAnswer.setType(answer.getType());
			tempAnswer.setEndTime(tempAnswer.getModifyDate());
			return this.update(tempAnswer);
		} else {
			answer.setCommonValue(userCode, sysCode, tenantCode);
			answer.setMarkStatus(AnswerConstant.TO_BE_MARKED);
			answer.setAnswerType(AnswerConstant.WRONG);
			answer.setEndTime(answer.getModifyDate());
			return this.insert(answer);
		}
	}

    /**
     * 提交答卷,自动统计选择题得分
     *
     * @param answer answer
     * @author tangyi
     * @date 2018/12/26 14:09
     */
    @Transactional
    public void submit(Answer answer) {
        long start = System.currentTimeMillis();
        String currentUsername = answer.getModifier();
        // 查找已提交的题目
        List<Answer> answerList = findList(answer);
        if (CollectionUtils.isEmpty(answerList))
            return;
        // 成绩
        ExaminationRecord record = new ExaminationRecord();
        // 分类题目
        Map<String, List<Answer>> distinctAnswer = this.distinctAnswer(answerList);
		AnswerHandleResult result = handleAll(distinctAnswer);
		// 记录总分、正确题目数、错误题目数
		record.setScore(result.getScore());
		record.setCorrectNumber(result.getCorrectNum());
		record.setInCorrectNumber(result.getInCorrectNum());
		// 更新答题状态
		distinctAnswer.values().forEach(answers -> answers.forEach(this::update));
        // 更新状态为统计完成,否则需要阅卷完成后才更改统计状态
        record.setSubmitStatus(SubmitStatusEnum.CALCULATED.getValue());
        // 保存成绩
        record.setCommonValue(currentUsername, SysUtil.getSysCode(), SysUtil.getTenantCode());
        record.setId(answer.getExamRecordId());
        record.setEndTime(record.getCreateDate());
        examRecordService.update(record);
        // 更新排名数据
		updateRank(record);
        log.debug("Submit examination, username: {},time consuming: {}ms", currentUsername, System.currentTimeMillis() - start);
    }

    /**
     * 更新排名信息
	 * 基于Redis的sort set数据结构
     * @param record record
     * @author tangyi
     * @date 2019/12/8 23:21
     */
    private void updateRank(ExaminationRecord record) {
		redisTemplate.opsForZSet().add(AnswerConstant.CACHE_PREFIX_RANK + record.getExaminationId(), JsonMapper.getInstance().toJson(record), record.getScore());
	}

    /**
     * 通过mq异步处理
     * 1. 先发送消息
     * 2. 发送消息成功,更新提交状态,发送失败,返回提交失败
     * 3. 消费消息,计算成绩
     *
     * @param answer answer
     * @return boolean
     * @author tangyi
     * @date 2019/05/03 14:35
     */
    @Transactional
    public boolean submitAsync(Answer answer) {
        long start = System.currentTimeMillis();
        String currentUsername = SysUtil.getUser();
        String applicationCode = SysUtil.getSysCode();
        String tenantCode = SysUtil.getTenantCode();
        answer.setModifier(currentUsername);
        answer.setApplicationCode(applicationCode);
        answer.setTenantCode(tenantCode);

        ExaminationRecord examRecord = new ExaminationRecord();
        examRecord.setCommonValue(currentUsername, applicationCode, tenantCode);
        examRecord.setId(answer.getExamRecordId());
        // 提交时间
        examRecord.setEndTime(examRecord.getCreateDate());
        examRecord.setSubmitStatus(SubmitStatusEnum.SUBMITTED.getValue());
        // 1. 发送消息
        amqpTemplate.convertAndSend(MqConstant.SUBMIT_EXAMINATION_QUEUE, answer);
        // 2. 更新考试状态
        boolean success = examRecordService.update(examRecord) > 0;
		log.debug("Submit examination, username: {},time consuming: {}ms", currentUsername, System.currentTimeMillis() - start);
		return success;
    }

    /**
     * 开始考试
     *
     * @param examRecord examRecord
     * @return StartExamDto
     * @author tangyi
     * @date 2019/04/30 23:06
     */
    @Transactional
    public StartExamDto start(ExaminationRecord examRecord) {
        String currentUsername = SysUtil.getUser();
        // 创建考试记录
        if (examRecord.getExaminationId() == null)
            throw new CommonException("参数校验失败,考试id为空!");
        if (examRecord.getUserId() == null)
            throw new CommonException("参数校验失败,用户id为空!");
		return this.start(examRecord.getUserId(), currentUsername, examRecord.getExaminationId(), SysUtil.getSysCode(), SysUtil.getTenantCode());
    }

    /**
     * 查询题目和答题
     *
     * @param subjectId       subjectId
     * @param examRecordId    examRecordId
     * @param nextType        -1:当前题目,0:下一题,1:上一题
     * @param nextSubjectId   nextSubjectId
     * @param nextSubjectType 下一题的类型,选择题、判断题
     * @return SubjectDto
     * @author tangyi
     * @date 2019/04/30 17:10
     */
    @Transactional
    public SubjectDto subjectAnswer(Long subjectId, Long examRecordId, Integer nextType, Long nextSubjectId, Integer nextSubjectType) {
		// 查找考试记录
    	ExaminationRecord examRecord = examRecordService.get(examRecordId);
        if (examRecord == null)
            throw new CommonException("考试记录不存在.");

        // 考试ID,题目ID查找关联关系
        ExaminationSubject examinationSubject = new ExaminationSubject();
        examinationSubject.setExaminationId(examRecord.getExaminationId());
        examinationSubject.setSubjectId(subjectId);
        PageInfo<ExaminationSubject> examinationSubjectPageInfo = examinationSubjectService.findPage(
                PageUtil.pageInfo(CommonConstant.PAGE_NUM_DEFAULT, CommonConstant.PAGE_SIZE_DEFAULT, "id",
                        CommonConstant.PAGE_ORDER_DEFAULT), examinationSubject);
        if (CollectionUtils.isEmpty(examinationSubjectPageInfo.getList()))
            throw new CommonException("序号为" + subjectId + "的题目不存在.");

        // 查询下一题
        SubjectDto subject;
        if (nextSubjectId != null) {
            subject = subjectService.get(nextSubjectId, nextSubjectType);
        } else {
            subject = subjectService.getNextByCurrentIdAndType(examRecord.getExaminationId(), subjectId, examinationSubjectPageInfo.getList().get(0).getType(), nextType);
        }
        if (subject == null) {
            log.error("Subject does not exist: {}", subjectId);
            return null;
        }

        // 查找答题
        Answer answer = new Answer();
        answer.setSubjectId(subject.getId());
        answer.setExamRecordId(examRecordId);
        Answer userAnswer = this.getAnswer(answer);
        userAnswer = userAnswer == null ? new Answer() : userAnswer;
        // 设置答题
        subject.setAnswer(userAnswer);
        subject.setExaminationRecordId(examRecordId);
        return subject;
    }

    /**
     * 分类答题
     *
     * @param answers answers
     * @return Map
     * @author tangyi
     * @date 2019/06/18 16:32
     */
    private Map<String, List<Answer>> distinctAnswer(List<Answer> answers) {
        Map<String, List<Answer>> distinctMap = new HashMap<>();
        answers.stream().collect(Collectors.groupingBy(Answer::getType, Collectors.toList())).forEach((type, temp) -> {
            // 匹配类型
            SubjectTypeEnum subjectType = SubjectTypeEnum.matchByValue(type);
            if (subjectType != null) {
                switch (subjectType) {
                    case CHOICES:
                        distinctMap.put(SubjectTypeEnum.CHOICES.name(), temp);
                        break;
					case MULTIPLE_CHOICES:
						distinctMap.put(SubjectTypeEnum.MULTIPLE_CHOICES.name(), temp);
						break;
                    case SHORT_ANSWER:
                        distinctMap.put(SubjectTypeEnum.SHORT_ANSWER.name(), temp);
                        break;
					case JUDGEMENT:
						distinctMap.put(SubjectTypeEnum.JUDGEMENT.name(), temp);
						break;
					default:
						break;
                }
            }
        });
        return distinctMap;
    }

    /**
     * 答题详情
     *
     * @param recordId         recordId
     * @param currentSubjectId currentSubjectId
     * @param nextSubjectType  nextSubjectType
     * @param nextType         nextType
     * @return AnswerDto
     * @author tangyi
     * @date 2019/06/18 23:05
     */
    public AnswerDto answerInfo(Long recordId, Long currentSubjectId, Integer nextSubjectType, Integer nextType) {
        ExaminationRecord record = examRecordService.get(recordId);
        SubjectDto subjectDto;
        // 题目为空,则加载第一题
        if (currentSubjectId == null) {
            subjectDto = subjectService.findFirstSubjectByExaminationId(record.getExaminationId());
        } else {
            ExaminationSubject examinationSubject = new ExaminationSubject();
            examinationSubject.setExaminationId(record.getExaminationId());
            examinationSubject.setSubjectId(currentSubjectId);

            // 查询该考试和指定序号的题目的关联信息
            // 下一题
            if (AnswerConstant.NEXT.equals(nextType)) {
                examinationSubject = examinationSubjectService.getByPreviousId(examinationSubject);
            } else if (AnswerConstant.PREVIOUS.equals(nextType)) {
                // 上一题
                examinationSubject = examinationSubjectService.getPreviousByCurrentId(examinationSubject);
            } else {
                examinationSubject = examinationSubjectService.findByExaminationIdAndSubjectId(examinationSubject);
            }
            if (examinationSubject == null)
                throw new CommonException("ID为" + currentSubjectId + "的题目不存在");
            // 查询题目的详细信息
            subjectDto = subjectService.get(examinationSubject.getSubjectId(), examinationSubject.getType());
        }
        AnswerDto answerDto = new AnswerDto();
        answerDto.setSubject(subjectDto);
        // 查询答题
        Answer answer = new Answer();
        answer.setSubjectId(subjectDto.getId());
        answer.setExamRecordId(recordId);
        Answer userAnswer = this.getAnswer(answer);
        if (userAnswer == null)
            userAnswer = answer;
        BeanUtils.copyProperties(userAnswer, answerDto);
        answerDto.setDuration(ExamRecordUtil.getExamDuration(userAnswer.getStartTime(), userAnswer.getEndTime()));
        // 判断正误
        SubjectTypeEnum subjectType = SubjectTypeEnum.matchByValue(subjectDto.getType());
        if (subjectType != null) {
            switch (subjectType) {
                case CHOICES:
                    choicesHandler.judgeOptionRight(userAnswer, subjectDto);
                    break;
                case MULTIPLE_CHOICES:
                    multipleChoicesHandler.judgeOptionRight(userAnswer, subjectDto);
                    break;
                case SHORT_ANSWER:
                    shortAnswerHandler.judgeRight(userAnswer, subjectDto);
                    break;
                case JUDGEMENT:
                    judgementHandler.judgeRight(userAnswer, subjectDto);
                    break;
                default:
                    break;
            }
        }
        ResponseBean<List<UserVo>> userVoResponseBean = userServiceClient.findUserById(new Long[] {record.getUserId()});
        if (ResponseUtil.isSuccess(userVoResponseBean) && CollectionUtils.isNotEmpty(userVoResponseBean.getData())) {
            UserVo userVo = userVoResponseBean.getData().get(0);
            answerDto.setUserName(userVo.getName());
        }
        return answerDto;
    }

    /**
     * 完成批改
     *
     * @param examRecord examRecord
     * @return Boolean
     * @author tangyi
     * @date 2019/06/19 14:44
     */
    public Boolean completeMarking(ExaminationRecord examRecord) {
        long start = System.currentTimeMillis();
        examRecord = examRecordService.get(examRecord);
        if (examRecord == null)
            throw new CommonException("考试记录不存在.");
        Answer answer = new Answer();
        answer.setExamRecordId(examRecord.getId());
        List<Answer> answers = this.findList(answer);
        if (CollectionUtils.isNotEmpty(answers)) {
            long correctNumber = answers.stream()
                    .filter(tempAnswer -> tempAnswer.getAnswerType().equals(AnswerConstant.RIGHT)).count();
            // 总分
            Double score = answers.stream().mapToDouble(Answer::getScore).sum();
            examRecord.setScore(score);
            examRecord.setSubmitStatus(SubmitStatusEnum.CALCULATED.getValue());
            examRecord.setCorrectNumber((int) correctNumber);
            examRecord.setInCorrectNumber(answers.size() - examRecord.getCorrectNumber());
            examRecordService.update(examRecord);
            log.debug("Submit done, username: {}, examinationId: {}, score: {}, time consuming: {}ms", examRecord.getCreator(), examRecord.getExaminationId(),
                    score, System.currentTimeMillis() - start);
        }
        return Boolean.TRUE;
    }

    /**
     * 获取排名数据
     * @param recordId recordId
     * @return List
     * @author tangyi
     * @date 2019/12/8 23:36
     */
	public List<RankInfoDto> getRankInfo(Long recordId) {
		List<RankInfoDto> rankInfos = new ArrayList<>();
		// 查询缓存
		Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
				.reverseRangeByScoreWithScores(AnswerConstant.CACHE_PREFIX_RANK + recordId, 0, Integer.MAX_VALUE);
		if (typedTuples != null) {
			// 用户ID列表
			Set<Long> userIds = new HashSet<>();
			typedTuples.forEach(typedTuple -> {
				ExaminationRecord record = JsonMapper.getInstance()
						.fromJson(typedTuple.getValue(), ExaminationRecord.class);
				if (record != null) {
					RankInfoDto rankInfo = new RankInfoDto();
					rankInfo.setUserId(record.getUserId());
					userIds.add(record.getUserId());
					rankInfo.setScore(typedTuple.getScore());
					rankInfos.add(rankInfo);
				}
			});
			if (!userIds.isEmpty()) {
				ResponseBean<List<UserVo>> userResponse = userServiceClient.findUserById(userIds.toArray(new Long[0]));
				if (ResponseUtil.isSuccess(userResponse)) {
					rankInfos.forEach(rankInfo -> {
						userResponse.getData().stream().filter(user -> user.getId().equals(rankInfo.getUserId()))
								.findFirst().ifPresent(user -> {
							// 设置考生信息
							rankInfo.setName(user.getName());
							rankInfo.setAvatarUrl(user.getAvatarUrl());
						});
					});
				}
			}
		}
		return rankInfos;
	}

    /**
     * 获取错题列表
     *
     * @param pageNum  pageNum
     * @param pageSize pageSize
     * @param sort     sort
     * @param order    order
     * @param recordId recordId
     * @param answer   answer
     * @return List
     * @author tangyi
     * @date 2020/02/19 22:50
     */
	public PageInfo<AnswerDto> answerListInfo(String pageNum, String pageSize, String sort, String order, Long recordId, Answer answer) {
        List<AnswerDto> answerDtos = new ArrayList<>();
        answer.setExamRecordId(recordId);
        PageInfo<Answer> answerPageInfo = this.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), answer);
        if (CollectionUtils.isNotEmpty(answerPageInfo.getList())) {
            answerDtos = answerPageInfo.getList().stream().map(tempAnswer -> {
                AnswerDto answerDto = new AnswerDto();
                BeanUtils.copyProperties(tempAnswer, answerDto);
                SubjectDto subjectDto = subjectService.get(tempAnswer.getSubjectId(), tempAnswer.getType());
                answerDto.setSubject(subjectDto);
                // 判断正误
                SubjectTypeEnum subjectType = SubjectTypeEnum.matchByValue(subjectDto.getType());
                if (subjectType != null) {
                    switch (subjectType) {
                        case CHOICES:
                            choicesHandler.judgeOptionRight(tempAnswer, subjectDto);
                            break;
                        case MULTIPLE_CHOICES:
                            multipleChoicesHandler.judgeOptionRight(tempAnswer, subjectDto);
                            break;
                        case SHORT_ANSWER:
                            shortAnswerHandler.judgeRight(tempAnswer, subjectDto);
                            break;
                        case JUDGEMENT:
                            judgementHandler.judgeRight(tempAnswer, subjectDto);
                            break;
                        default:
                            break;
                    }
                }
                return answerDto;
            }).collect(Collectors.toList());
        }
        PageInfo<AnswerDto> answerDtoPageInfo = new PageInfo<>();
        answerDtoPageInfo.setList(answerDtos);
        answerDtoPageInfo.setTotal(answerPageInfo.getTotal());
        answerDtoPageInfo.setPageNum(answerPageInfo.getPageNum());
        answerDtoPageInfo.setPageSize(answerPageInfo.getPageSize());
        return answerDtoPageInfo;
    }

	/**
	 * 根据examRecordId查询
	 * @param examRecordId examRecordId
	 * @return List
	 * @author tangyi
	 * @date 2020/2/21 1:08 下午
	 */
	public List<Answer> findListByExamRecordId(Long examRecordId) {
		return this.dao.findListByExamRecordId(examRecordId);
	}

    /**
     * 移动端提交答题
     * @param examinationId examinationId
     * @return ResponseBean
     * @author tangyi
     * @date 2020/03/15 16:08
     */
    @Transactional
	public boolean anonymousUserSubmit(Long examinationId, String identifier, List<SubjectDto> subjectDtos) {
        long start = System.currentTimeMillis();
        if (StringUtils.isBlank(identifier) || CollectionUtils.isEmpty(subjectDtos)) {
            return false;
        }
        Examination examination = examinationService.get(examinationId);
        if (examination == null) {
            return false;
        }
        String tenantCode = SysUtil.getTenantCode();
        String sysCode = SysUtil.getSysCode();
        Date currentDate = DateUtils.asDate(LocalDateTime.now());
        // 判断用户是否存在,不存在则自动创建
        ResponseBean<UserVo> userVoResponseBean = userServiceClient.findUserByIdentifier(identifier, tenantCode);
        if (!ResponseUtil.isSuccess(userVoResponseBean) || userVoResponseBean.getData() == null) {
            return false;
        }
        // TODO 自动注册账号
        UserVo user = userVoResponseBean.getData();
        // 保存考试记录
        ExaminationRecord record = new ExaminationRecord();
        record.setCommonValue(identifier, sysCode, tenantCode);
        record.setUserId(user.getUserId());

        // 初始化Answer
        List<Answer> answers = new ArrayList<>(subjectDtos.size());
        subjectDtos.forEach(subjectDto -> {
            Answer answer = new Answer();
            answer.setCommonValue(identifier, sysCode, tenantCode);
            answer.setAnswer(subjectDto.getAnswer().getAnswer());
            answer.setExamRecordId(record.getId());
            answer.setEndTime(currentDate);
            answer.setSubjectId(subjectDto.getId());
            answer.setType(subjectDto.getType());
            answer.setAnswerType(AnswerConstant.WRONG);
            answers.add(answer);
        });
        // 分类题目
        Map<String, List<Answer>> distinctAnswer = this.distinctAnswer(answers);
        AnswerHandleResult result = handleAll(distinctAnswer);
        // 记录总分、正确题目数、错误题目数
        record.setScore(result.getScore());
        record.setCorrectNumber(result.getCorrectNum());
        record.setInCorrectNumber(result.getInCorrectNum());
        // 更新状态为统计完成,否则需要阅卷完成后才更改统计状态
        record.setExaminationId(examinationId);
        record.setSubmitStatus(SubmitStatusEnum.CALCULATED.getValue());
        record.setStartTime(currentDate);
        record.setEndTime(currentDate);
        examRecordService.insert(record);
        answers.forEach(this::insert);
        log.info("AnonymousUser submit, examinationId:{}, identifier: {}, time consuming: {}ms", examinationId, identifier, System.currentTimeMillis() - start);
        return true;
    }

    /**
     * 自动判分
     * @param distinctAnswer distinctAnswer
     * @return ResponseBean
     * @author tangyi
     * @date 2020/03/15 16:21
     */
    public AnswerHandleResult handleAll(Map<String, List<Answer>> distinctAnswer) {
        // 暂时只自动统计单选题、多选题、判断题,简答题由老师阅卷批改
        AnswerHandleResult choiceResult = choicesHandler.handle(distinctAnswer.get(SubjectTypeEnum.CHOICES.name()));
        AnswerHandleResult multipleResult = multipleChoicesHandler.handle(distinctAnswer.get(SubjectTypeEnum.MULTIPLE_CHOICES.name()));
        AnswerHandleResult judgementResult = judgementHandler.handle(distinctAnswer.get(SubjectTypeEnum.JUDGEMENT.name()));
        AnswerHandleResult shortAnswerResult = shortAnswerHandler.handle(distinctAnswer.get(SubjectTypeEnum.SHORT_ANSWER.name()));
        return AnswerHandlerUtil.addAll(Arrays.asList(choiceResult, multipleResult, judgementResult, shortAnswerResult));
    }

    /**
     * 开始考试
     *
     * @param examinationId examinationId
     * @param identifier identifier
     * @return StartExamDto
     * @author tangyi
     * @date 2020/3/21 5:51 下午
     */
    @Transactional
    public StartExamDto anonymousUserStart(Long examinationId, String identifier) {
        String applicationCode = SysUtil.getSysCode();
        String tenantCode = SysUtil.getTenantCode();
        // 创建考试记录
        if (examinationId == null)
            throw new CommonException("参数校验失败,考试id为空!");
        if (identifier == null)
            throw new CommonException("参数校验失败,用户identifier为空!");
        // 查询用户信息
        ResponseBean<UserVo> userVoResponseBean = userServiceClient.findUserByIdentifier(identifier, tenantCode);
        if (!ResponseUtil.isSuccess(userVoResponseBean)) {
            throw new CommonException("获取用户" + identifier + "信息失败!");
        }
        return this.start(userVoResponseBean.getData().getUserId(), identifier, examinationId, applicationCode, tenantCode);
    }

    /**
     * 开始考试
     *
     * @param userId userId
     * @param identifier identifier
     * @param examinationId examinationId
     * @param applicationCode applicationCode
     * @param tenantCode tenantCode
     * @return StartExamDto
     * @author tangyi
     * @date 2019/04/30 23:06
     */
    @Transactional
    public StartExamDto start(Long userId, String identifier, Long examinationId, String applicationCode, String tenantCode) {
        StartExamDto startExamDto = new StartExamDto();
        // 查找考试信息
        Examination examination = examinationService.get(examinationId);
        ExaminationRecord examRecord = new ExaminationRecord();
        examRecord.setCommonValue(identifier, applicationCode, tenantCode);
        examRecord.setUserId(userId);
        examRecord.setExaminationId(examinationId);
        examRecord.setStartTime(examRecord.getCreateDate());

        // 默认未提交状态
        examRecord.setSubmitStatus(SubmitStatusEnum.NOT_SUBMITTED.getValue());
        // 保存考试记录
        if (examRecordService.insert(examRecord) > 0) {
            startExamDto.setExamination(examination);
            startExamDto.setExamRecord(examRecord);
            // 根据题目ID,类型获取第一题的详细信息
            SubjectDto subjectDto = subjectService.findFirstSubjectByExaminationId(examRecord.getExaminationId());
            startExamDto.setSubjectDto(subjectDto);
            // 创建第一题的答题
            Answer answer = new Answer();
            answer.setCommonValue(identifier, applicationCode, tenantCode);
            answer.setExamRecordId(examRecord.getId());
            answer.setSubjectId(subjectDto.getId());
            // 默认待批改状态
            answer.setMarkStatus(AnswerConstant.TO_BE_MARKED);
            answer.setAnswerType(AnswerConstant.WRONG);
            answer.setStartTime(answer.getCreateDate());
            // 保存答题
            this.save(answer);
            subjectDto.setAnswer(answer);
        }
        return startExamDto;
    }
}