package com.qmth.teachcloud.report.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.itextpdf.text.pdf.PdfReader;
import com.qmth.boot.api.exception.ApiException;
import com.qmth.teachcloud.common.cache.ThirdUserAuthCacheUtil;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.BasicAttachment;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.entity.TBSession;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.UploadFileEnum;
import com.qmth.teachcloud.common.service.BasicAttachmentService;
import com.qmth.teachcloud.common.service.CommonCacheService;
import com.qmth.teachcloud.common.service.SysUserService;
import com.qmth.teachcloud.common.service.TBSessionService;
import com.qmth.teachcloud.common.util.*;
import com.qmth.teachcloud.report.business.bean.result.*;
import com.qmth.teachcloud.report.business.entity.*;
import com.qmth.teachcloud.report.business.enums.GradeScopeEnum;
import com.qmth.teachcloud.report.business.enums.PaperStructJudgeEnum;
import com.qmth.teachcloud.report.business.enums.PiecewiseScopeEnum;
import com.qmth.teachcloud.report.business.enums.PublishStatusEnum;
import com.qmth.teachcloud.report.business.service.*;
import com.qmth.teachcloud.report.business.utils.AnalyzeScopeUtil;
import com.qmth.teachcloud.report.business.utils.MathUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 教研公共服务
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2021/6/4
 */
@Service
public class ReportCommonServiceImpl implements ReportCommonService {
    private final static Logger log = LoggerFactory.getLogger(ReportCommonServiceImpl.class);

    @Resource
    TBPaperService tbPaperService;

    @Resource
    TAExamCourseCollegeInspectService taExamCourseCollegeInspectService;

    @Resource
    TAExamCourseRecordService taExamCourseRecordService;

    @Resource
    TBDimensionService tbDimensionService;

    @Resource
    TAExamCourseCollegeTeacherService taExamCourseCollegeTeacherService;

    @Resource
    TAExamCourseDifficultService taExamCourseDifficultService;

    @Resource
    TAExamCourseTeacherDifficultService taExamCourseTeacherDifficultService;

    @Resource
    TBPaperStructService tbPaperStructService;

    @Resource
    TAExamCourseService taExamCourseService;

    @Resource
    TBExamCourseService tbExamCourseService;

    @Resource
    TBExamService tbExamService;

    @Resource
    TAExamCourseTeacherCollegeDifficultService taExamCourseTeacherCollegeDifficultService;

    @Resource
    private FileStoreUtil fileStoreUtil;

    @Resource
    private DictionaryConfig dictionaryConfig;

    @Resource
    BasicAttachmentService basicAttachmentService;

    @Resource
    SysUserService sysUserService;

    @Resource
    TBSessionService tbSessionService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    CommonCacheService commonCacheService;

    /**
     * 学院学科报表查询科目信息
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @return return
     */
    @Override
    public CourseInfoResult findCourseInfo(Long examId, String courseCode, Long collegeId) {
        //查找试卷
        QueryWrapper<TBPaper> tbPaperQueryWrapper = new QueryWrapper<>();
        tbPaperQueryWrapper.lambda().eq(TBPaper::getExamId, examId)
                .eq(TBPaper::getCourseCode, courseCode);
        List<TBPaper> tbPaperList = tbPaperService.list(tbPaperQueryWrapper);

        //查找学院科目维度
        List<TAExamCourseCollegeInspectResult> taExamCourseCollegeInspectResultList = taExamCourseCollegeInspectService.findCourseDescriptiveStatisticsForCollege(examId, courseCode, collegeId);
        //查找学校科目维度
        List<TAExamCourseCollegeInspectResult> taExamCourseSchoolInspectResultList = taExamCourseCollegeInspectService.findCourseDescriptiveStatisticsForSchool(examId, courseCode);

        // 课程概况
        if (tbPaperList.size() < 1) {
            throw ExceptionResultEnum.NO_DATA.exception();
        }
        Set<String> paperTypeSet = tbPaperList.stream().map(TBPaper::getPaperType).collect(Collectors.toSet());
        String paperType = String.join(",", paperTypeSet); // 多种试卷类型的试卷类型合并','号隔开
        Gson gson = new Gson();
        TBPaperInfoResult TBPaperInfoResult = gson.fromJson(gson.toJson(tbPaperList.get(0)), TBPaperInfoResult.class);
        TBPaperInfoResult.setPaperType(paperType);
        TBPaperInfoResult.setCollegeCount(taExamCourseCollegeInspectResultList.size());

//        // 学院该科目的描述统计
//        List<TAExamCourseCollegeInspectResult> collegeDescriptiveStatisticsList = taExamCourseCollegeInspectResultList
//                .stream().filter(e -> collegeId.longValue() == e.getCollegeId().longValue()).collect(Collectors.toList());
//        if (collegeDescriptiveStatisticsList.size() != 1) {
//            throw ExceptionResultEnum.DATA_COUNT_EXCEPTION.exception();
//        }
        if (taExamCourseCollegeInspectResultList.size() != 1) {
            throw ExceptionResultEnum.DATA_COUNT_EXCEPTION.exception();
        }
        TAExamCourseCollegeInspectResult collegeDescriptiveStatistics = taExamCourseCollegeInspectResultList.get(0);

        // 全校该科目的描述统计
        if (taExamCourseSchoolInspectResultList.size() != 1) {
            throw ExceptionResultEnum.DATA_COUNT_EXCEPTION.exception();
        }
        TAExamCourseCollegeInspectResult schoolDescriptiveStatistics = taExamCourseSchoolInspectResultList.get(0);
        DescriptiveStatisticsResult descriptiveStatisticsResult = new DescriptiveStatisticsResult(collegeDescriptiveStatistics, schoolDescriptiveStatistics);
        return new CourseInfoResult(TBPaperInfoResult, descriptiveStatisticsResult);
    }

    /**
     * 学院学科报表科目描述
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @return return
     */
    @Override
    public CollegeAndSchoolGradeDistributionResult findCourseDistribution(Long examId, String courseCode, Long collegeId) {
        List<ExamRecordResult> examPaperTikForSchool = taExamCourseRecordService.findExamRecordByExamIdAndCourseCode(examId, courseCode);
        List<ExamRecordResult> examPaperTikForCollege = examPaperTikForSchool
                .stream().filter(e -> collegeId.longValue() == e.getCollegeId().longValue()).collect(Collectors.toList());

        double colTotalCount = examPaperTikForCollege.size(); // 学院总参考人数
        double schTotalCount = examPaperTikForSchool.size(); // 全校总参考人数
        if (schTotalCount < 1 || colTotalCount < 1) {
            throw ExceptionResultEnum.DATA_ERROR.exception();
        }

        // 计算成绩分布图
        List<GradeDistributionResult> gradeDistributionList = new ArrayList<>();
        for (GradeScopeEnum value : GradeScopeEnum.values()) {
            String describe = value.getDescribe();
            double colCount = 0;
            double schCount = 0;

            Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(value.getScope());
            String minSign = String.valueOf(scopeMap.get("minSign"));
            String maxSign = String.valueOf(scopeMap.get("maxSign"));
            double minValue = Double.parseDouble(String.valueOf(scopeMap.get("minValue")));
            double maxValue = Double.parseDouble(String.valueOf(scopeMap.get("maxValue")));
            if ("(".equals(minSign) && ")".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue < e.getAssignedScore() && maxValue > e.getAssignedScore()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue < e.getAssignedScore() && maxValue > e.getAssignedScore()).count();

            } else if ("(".equals(minSign) && "]".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue < e.getAssignedScore() && maxValue >= e.getAssignedScore()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue < e.getAssignedScore() && maxValue >= e.getAssignedScore()).count();

            } else if ("[".equals(minSign) && ")".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue <= e.getAssignedScore() && maxValue > e.getAssignedScore()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue <= e.getAssignedScore() && maxValue > e.getAssignedScore()).count();

            } else if ("[".equals(minSign) && "]".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue <= e.getAssignedScore() && maxValue >= e.getAssignedScore()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue <= e.getAssignedScore() && maxValue >= e.getAssignedScore()).count();
            }

            gradeDistributionList.add(new GradeDistributionResult(colCount, schCount, describe));
        }

        // 计算分数段分布
        List<PiecewiseDistributionResult> piecewiseDistributionList = new ArrayList<>();
        for (PiecewiseScopeEnum value : PiecewiseScopeEnum.values()) {
            String remark = value.getRemark();
            String name = value.getName();
            double colCount = 0;
            double schCount = 0;

            Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(value.getScope());
            String minSign = String.valueOf(scopeMap.get("minSign"));
            String maxSign = String.valueOf(scopeMap.get("maxSign"));
            double minValue = Double.parseDouble(String.valueOf(scopeMap.get("minValue")));
            double maxValue = Double.parseDouble(String.valueOf(scopeMap.get("maxValue")));
            if ("(".equals(minSign) && ")".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue < e.getPercentGrade() && maxValue > e.getPercentGrade()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue < e.getPercentGrade() && maxValue > e.getPercentGrade()).count();

            } else if ("(".equals(minSign) && "]".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue < e.getPercentGrade() && maxValue >= e.getPercentGrade()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue < e.getPercentGrade() && maxValue >= e.getPercentGrade()).count();

            } else if ("[".equals(minSign) && ")".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue <= e.getPercentGrade() && maxValue > e.getPercentGrade()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue <= e.getPercentGrade() && maxValue > e.getPercentGrade()).count();

            } else if ("[".equals(minSign) && "]".equals(maxSign)) {
                colCount = (int) examPaperTikForCollege.stream()
                        .filter(e -> minValue <= e.getPercentGrade() && maxValue >= e.getPercentGrade()).count();
                schCount = (int) examPaperTikForSchool.stream()
                        .filter(e -> minValue <= e.getPercentGrade() && maxValue >= e.getPercentGrade()).count();
            }
            double colCountRate = MathUtil.formatDouble2(colCount / colTotalCount * 100); //本院比率 保留2位小数
            double schCountRate = MathUtil.formatDouble2(schCount / schTotalCount * 100); //全校比率 保留2位小数

            piecewiseDistributionList.add(new PiecewiseDistributionResult(name, colCount, colCountRate, schCount, schCountRate, remark));
        }
        return new CollegeAndSchoolGradeDistributionResult(gradeDistributionList, piecewiseDistributionList);
    }

    /**
     * 开课学院分数段
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @param filter     filter
     * @return return
     */
    @Override
    public List<SurveyTeacherGradeDistributionResult> surveyTeacherDistribution(Long examId, String courseCode, Long collegeId, boolean filter) {
        List<SurveyTeacherGradeDistributionResult> surveyTeacherGradeDistributionResultList = new ArrayList<>();
        List<SurveyTeacherDistributionResult> surveyTeacherDistributionResultList = taExamCourseRecordService.surveyTeacherDistribution(examId, courseCode, filter, collegeId);
        if (Objects.isNull(surveyTeacherDistributionResultList) && surveyTeacherDistributionResultList.size() > 0) {
            return null;
        }
        surveyTeacherDistributionResultList = surveyTeacherDistributionResultList.stream().filter(e -> !e.getAbsent()).collect(Collectors.toList());
        //过滤应届
        List<SurveyTeacherDistributionResult> currentDistributionResultList = surveyTeacherDistributionResultList.stream().filter(e -> e.getStudentCurrent() && !e.getAbsent()).collect(Collectors.toList());
        double allTotalCount = surveyTeacherDistributionResultList.size(); // 总体人数
        double currentTotalCount = currentDistributionResultList.size(); // 应届人数

        // 计算成绩分布图
        for (GradeScopeEnum value : GradeScopeEnum.values()) {
            String describe = value.getDescribe();
            double allCountBefore = 0, allCountAfter = 0, currentCountBefore = 0, currentCountAfter = 0,
                    allCountRateBefore, allCountRateAfter, currentCountRateBefore, currentCountRateAfter;

            Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(value.getScope());
            String minSign = String.valueOf(scopeMap.get("minSign"));
            String maxSign = String.valueOf(scopeMap.get("maxSign"));
            double minValue = Double.parseDouble(String.valueOf(scopeMap.get("minValue")));
            double maxValue = Double.parseDouble(String.valueOf(scopeMap.get("maxValue")));
            if ("(".equals(minSign) && ")".equals(maxSign)) {
                allCountBefore = (int) surveyTeacherDistributionResultList.stream()//赋分前
                        .filter(e -> minValue < e.getTotalScore().doubleValue() && maxValue > e.getTotalScore().doubleValue()).count();
                allCountAfter = (int) surveyTeacherDistributionResultList.stream()
                        .filter(e -> minValue < e.getAssignedScore().doubleValue() && maxValue > e.getAssignedScore().doubleValue()).count();
                currentCountBefore = (int) currentDistributionResultList.stream()//赋分前
                        .filter(e -> minValue < e.getTotalScore().doubleValue() && maxValue > e.getTotalScore().doubleValue()).count();
                currentCountAfter = (int) currentDistributionResultList.stream()
                        .filter(e -> minValue < e.getAssignedScore().doubleValue() && maxValue > e.getAssignedScore().doubleValue()).count();
            } else if ("(".equals(minSign) && "]".equals(maxSign)) {
                allCountBefore = (int) surveyTeacherDistributionResultList.stream()//赋分前
                        .filter(e -> minValue < e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                allCountAfter = (int) surveyTeacherDistributionResultList.stream()
                        .filter(e -> minValue < e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
                currentCountBefore = (int) currentDistributionResultList.stream()//赋分前
                        .filter(e -> minValue < e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                currentCountAfter = (int) currentDistributionResultList.stream()
                        .filter(e -> minValue < e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
            } else if ("[".equals(minSign) && ")".equals(maxSign)) {
                if (maxValue == 100) {
                    allCountBefore = (int) surveyTeacherDistributionResultList.stream()//赋分前
                            .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                    allCountAfter = (int) surveyTeacherDistributionResultList.stream()
                            .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
                    currentCountBefore = (int) currentDistributionResultList.stream()//赋分前
                            .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                    currentCountAfter = (int) currentDistributionResultList.stream()
                            .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
                } else {
                    allCountBefore = (int) surveyTeacherDistributionResultList.stream()//赋分前
                            .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue > e.getTotalScore().doubleValue()).count();
                    allCountAfter = (int) surveyTeacherDistributionResultList.stream()
                            .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue > e.getAssignedScore().doubleValue()).count();
                    currentCountBefore = (int) currentDistributionResultList.stream()//赋分前
                            .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue > e.getTotalScore().doubleValue()).count();
                    currentCountAfter = (int) currentDistributionResultList.stream()
                            .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue > e.getAssignedScore().doubleValue()).count();
                }
            } else if ("[".equals(minSign) && "]".equals(maxSign)) {
                allCountBefore = (int) surveyTeacherDistributionResultList.stream()//赋分前
                        .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                allCountAfter = (int) surveyTeacherDistributionResultList.stream()
                        .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
                currentCountBefore = (int) currentDistributionResultList.stream()//赋分前
                        .filter(e -> minValue <= e.getTotalScore().doubleValue() && maxValue >= e.getTotalScore().doubleValue()).count();
                currentCountAfter = (int) currentDistributionResultList.stream()
                        .filter(e -> minValue <= e.getAssignedScore().doubleValue() && maxValue >= e.getAssignedScore().doubleValue()).count();
            }
            BigDecimal hundredBig = new BigDecimal("100");
            allCountRateBefore = allTotalCount > 0 ? new BigDecimal(allCountBefore).multiply(hundredBig).divide(new BigDecimal(allTotalCount), SystemConstant.FINAL_SCALE, RoundingMode.HALF_UP).doubleValue() : 0;
            allCountRateAfter = allTotalCount > 0 ? new BigDecimal(allCountAfter).multiply(hundredBig).divide(new BigDecimal(allTotalCount), SystemConstant.FINAL_SCALE, RoundingMode.HALF_UP).doubleValue() : 0;
            currentCountRateBefore = currentTotalCount > 0 ? new BigDecimal(currentCountBefore).multiply(hundredBig).divide(new BigDecimal(currentTotalCount), SystemConstant.FINAL_SCALE, RoundingMode.HALF_UP).doubleValue() : 0;
            currentCountRateAfter = currentTotalCount > 0 ? new BigDecimal(currentCountAfter).multiply(hundredBig).divide(new BigDecimal(currentTotalCount), SystemConstant.FINAL_SCALE, RoundingMode.HALF_UP).doubleValue() : 0;

            surveyTeacherGradeDistributionResultList.add(new SurveyTeacherGradeDistributionResult((int) Math.abs(allCountBefore), (int) Math.abs(allCountAfter), (int) Math.abs(currentCountBefore), (int) Math.abs(currentCountAfter),
                    allCountRateBefore, allCountRateAfter, currentCountRateBefore, currentCountRateAfter, describe));
        }
        return surveyTeacherGradeDistributionResultList;
    }

    /**
     * 学院学科报表查询维度
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @param teacherId  teacherId
     * @return return
     */
    @Override
    public DimensionAnalyzeResult findDimensionInfo(Long examId, String courseCode, Long collegeId, Long teacherId) {
        boolean deanLookCollege = false;
        boolean deanLookTeacher = false;
        boolean teacherLookSelf = false;
        //查找维度
        List<CourseDimensionResult> dimensionInfoDatasource = null;
        if (SystemConstant.longNotNull(collegeId) && !SystemConstant.longNotNull(teacherId)) {
            // 有学院id无教师id -> 开课院长看各考察学院数据
            dimensionInfoDatasource = tbDimensionService.findDimensionInfo(examId, courseCode, collegeId);
            deanLookCollege = true;
        } else if (SystemConstant.longNotNull(teacherId) && !SystemConstant.longNotNull(collegeId)) {
            // 有教师id无学院id -> 开课院长看各授课教师数据
            dimensionInfoDatasource = tbDimensionService.findDimensionByTeacherInfo(examId, courseCode, teacherId);
            deanLookTeacher = true;
        } else if (SystemConstant.longNotNull(teacherId) && SystemConstant.longNotNull(collegeId)) {
            // 既有教师id又有学院id -> 任课教师看某个学院下自己的数据
            dimensionInfoDatasource = tbDimensionService.findDimensionByTeacherCollegeInfo(examId, courseCode, teacherId, collegeId);
            teacherLookSelf = true;
        }
        List<ModuleDimensionResult> dimensionAnalyzeList = new ArrayList<>();
        if (dimensionInfoDatasource != null && dimensionInfoDatasource.size() > 0) {
            Set<String> moduleSet = dimensionInfoDatasource.stream().map(CourseDimensionResult::getModule).collect(Collectors.toSet()); // 考查模块集合
            for (String module : moduleSet) {
                List<CourseDimensionResult> dimensionInfoList = dimensionInfoDatasource
                        .stream().filter(e -> module.equals(e.getModule())).collect(Collectors.toList());

                List<DimensionInfoResult> dioList = new ArrayList<>();
                List<DimensionInfoResult> batterThanAll = new ArrayList<>(); // 掌握较好
                List<DimensionInfoResult> worseThanAll = new ArrayList<>(); // 掌握较差
                for (CourseDimensionResult courseDimensionResult : dimensionInfoList) {
                    String dimensionCode = String.valueOf(courseDimensionResult.getDimensionCode());
                    String dimensionName = String.valueOf(courseDimensionResult.getDimensionName());
                    double referenceScoreRate = courseDimensionResult.getReferenceScoreRate();
                    double myScoreRate = courseDimensionResult.getMyScoreRate();
                    if (myScoreRate >= referenceScoreRate) {
                        batterThanAll.add(new DimensionInfoResult(dimensionCode, dimensionName));
                    } else {
                        worseThanAll.add(new DimensionInfoResult(dimensionCode, dimensionName));
                    }

                    String schScoreRate = null;
                    String colScoreRate = null;
                    String teaScoreRate = null;
                    if (deanLookCollege) {
                        schScoreRate = String.valueOf(referenceScoreRate);
                        colScoreRate = String.valueOf(myScoreRate);
                    } else if (deanLookTeacher) {
                        schScoreRate = String.valueOf(referenceScoreRate);
                        teaScoreRate = String.valueOf(myScoreRate);
                    } else if (teacherLookSelf) {
                        colScoreRate = String.valueOf(referenceScoreRate);
                        teaScoreRate = String.valueOf(myScoreRate);
                    }
                    dioList.add(new DimensionInfoResult(dimensionCode, dimensionName, String.valueOf(courseDimensionResult.getTotalCount()), schScoreRate, colScoreRate, teaScoreRate, courseDimensionResult.getDegree()));
                }
                dimensionAnalyzeList.add(new ModuleDimensionResult(module, dioList, batterThanAll, worseThanAll));
            }
        }
        return new DimensionAnalyzeResult(dimensionAnalyzeList);
    }

    /**
     * 查找题目相关
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @param teacherId  teacherId
     * @return return
     */
    @Override
    public QuestionInfoResult findSituationOfQuestions(Long examId, String courseCode, Long collegeId, Long teacherId) {
        //查找答题记录
        List<CellResult> answerDetailDatasource = null;
        if (SystemConstant.longNotNull(collegeId) && !SystemConstant.longNotNull(teacherId)) {
            // 有学院id无教师id -> 开课院长看各考察学院数据
            answerDetailDatasource = taExamCourseDifficultService.findValidAnswerDetail(examId, courseCode, collegeId);
        } else if (SystemConstant.longNotNull(teacherId) && !SystemConstant.longNotNull(collegeId)) {
            // 有教师id无学院id -> 开课院长看各授课教师数据
            answerDetailDatasource = taExamCourseTeacherDifficultService.findValidAnswerDetail(examId, courseCode, teacherId);
        } else if (SystemConstant.longNotNull(teacherId) && SystemConstant.longNotNull(collegeId)) {
            // 既有教师id又有学院id -> 任课教师看某个学院下自己的数据
            answerDetailDatasource = taExamCourseTeacherCollegeDifficultService.findValidAnswerDetail(examId, courseCode, teacherId, collegeId);
        }
        List<PaperTypeResult> questionInfoList = new ArrayList<>();
        if (answerDetailDatasource != null && answerDetailDatasource.size() > 0) {
            Set<String> paperTypeList = answerDetailDatasource.stream().map(CellResult::getPaperType).collect(Collectors.toSet());
            for (String paperType : paperTypeList) {
                questionInfoList.add(new PaperTypeResult(paperType, answerDetailDatasource.stream().filter(e -> Objects.equals(paperType, e.getPaperType())).collect(Collectors.toList())));
            }
        }
        return new QuestionInfoResult(questionInfoList);
    }

    /**
     * 查找老师信息
     *
     * @param examId     examId
     * @param courseCode courseCode
     * @param collegeId  collegeId
     * @return return
     */
    @Override
    public TeacherInfoListResult findTeacherInfo(Long examId, String courseCode, Long collegeId) {
        //查找老师得分
        List<TAExamCourseTeacherResult> taExamCourseTeacherResultList = taExamCourseCollegeTeacherService.findTeacherInfo(examId, courseCode, collegeId);
        if (taExamCourseTeacherResultList.size() < 1) {
            throw ExceptionResultEnum.DATA_ERROR.exception();
        }
        return new TeacherInfoListResult(taExamCourseTeacherResultList);
    }

    /**
     * 查找题目信息
     *
     * @param examId           考试id
     * @param courseCode       课程编号
     * @param teachCollegeId   开课学院id
     * @param inspectCollegeId 考察学院id
     * @param teacherId        授课教师id
     * @return 结果
     */
    @Override
    public QuestionListResult findQuestionInfo(Long examId, String courseCode, Long teachCollegeId, Long inspectCollegeId, Long teacherId) {
        //查找试卷结构
        List<TBPaperStructResult> questionDatasource;
        if (SystemConstant.longNotNull(inspectCollegeId) && SystemConstant.longNotNull(teacherId)) {
            // 教师课堂查询 -> 教师在某课程学院下学生数据集
            questionDatasource = tbPaperStructService.findQuestionInfoByTeacherCollege(examId, courseCode, inspectCollegeId, teacherId);
        } else {
            // 开课学院和考察学院查询 -> 全校学生某课程数据集
            questionDatasource = tbPaperStructService.findQuestionInfo(examId, courseCode);
        }
        //查找维度
        QueryWrapper<TBDimension> tbDimensionQueryWrapper = new QueryWrapper<>();
        tbDimensionQueryWrapper.lambda().eq(TBDimension::getExamId, examId)
                .eq(TBDimension::getCourseCode, courseCode);
        List<TBDimension> dimensionDataSource = tbDimensionService.list(tbDimensionQueryWrapper);

        Set<String> paperTypeSet = questionDatasource.stream().map(TBPaperStructResult::getPaperType).collect(Collectors.toSet());
        List<QuestionResult> questionList = new ArrayList<>();
        for (String paperType : paperTypeSet) {
            // 考题
            List<TBPaperStructResult> cellList = questionDatasource.stream()
                    .filter(e -> paperType.equals(e.getPaperType())).collect(Collectors.toList());

            for (TBPaperStructResult cell : cellList) {
                cell.setPaperType(null);
            }
            Set<String> knowledgeDimensionSet = new HashSet<>(), abilityDimensionSet = new HashSet<>();
            // 错题
            List<TBPaperStructResult> errorCellList = cellList.stream().filter(e -> PaperStructJudgeEnum.NOT_QUITE_RIGHT.equals(e.getPaperStructJudge())).collect(Collectors.toList());
            // 获取考察点集合
            for (TBPaperStructResult cell : errorCellList) {
                if (Objects.nonNull(cell.getKnowledgeDimension())) {
                    String knowledgeDimension = cell.getKnowledgeDimension();
                    String[] arr = knowledgeDimension.split(",");
                    knowledgeDimensionSet.addAll(Arrays.asList(arr));
                }
                if (Objects.nonNull(cell.getAbilityDimension())) {
                    String abilityDimension = cell.getAbilityDimension();
                    String[] arrAbilityDimension = abilityDimension.split(",");
                    abilityDimensionSet.addAll(Arrays.asList(arrAbilityDimension));
                }
            }
            List<String> knowledgeDimensionSort = MathUtil.sortDimension(new ArrayList<>(knowledgeDimensionSet)); // 经过特殊排序的知识点集合
            List<String> abilityDimensionSort = MathUtil.sortDimension(new ArrayList<>(abilityDimensionSet)); // 经过特殊排序的知识点集合

            List<DimensionInfoResult> dimensionList = new ArrayList<>();
            for (String dimensionCode : knowledgeDimensionSort) {
                String dimensionName;
                List<String> nameTwoList = dimensionDataSource.stream()
                        .filter(e -> dimensionCode.equals(e.getCodeSecond()) && "知识".equals(e.getDimensionType()))
                        .map(TBDimension::getNameSecond).collect(Collectors.toList());
                if (nameTwoList.size() < 1) {
                    List<String> tmpList = dimensionDataSource.stream()
                            .filter(e -> dimensionCode.equals(e.getCodePrimary()))
                            .map(TBDimension::getNamePrimary).collect(Collectors.toList());
                    if (tmpList.size() == 0) {
                        continue;
                    }
                    dimensionName = tmpList.get(0);
                } else {
                    dimensionName = nameTwoList.get(0);
                }
                dimensionList.add(new DimensionInfoResult(dimensionCode, dimensionName));
            }

            List<DimensionInfoResult> abilityDimensionList = new ArrayList<>();
            for (String dimensionCode : abilityDimensionSort) {
                String dimensionName;
                List<String> nameTwoList = dimensionDataSource.stream()
                        .filter(e -> dimensionCode.equals(e.getCodeSecond()))
                        .map(TBDimension::getNameSecond).collect(Collectors.toList());
                if (nameTwoList.size() < 1) {
                    List<String> tmpList = dimensionDataSource.stream()
                            .filter(e -> dimensionCode.equals(e.getCodePrimary()) && "能力".equals(e.getDimensionType()))
                            .map(TBDimension::getNamePrimary).collect(Collectors.toList());
                    if (tmpList.size() == 0) {
                        continue;
                    }
                    dimensionName = tmpList.get(0);
                } else {
                    dimensionName = nameTwoList.get(0);
                }
                abilityDimensionList.add(new DimensionInfoResult(dimensionCode, dimensionName));
            }
            questionList.add(new QuestionResult(paperType, cellList, dimensionList, abilityDimensionList));
        }
        return new QuestionListResult(questionList);
    }

    /**
     * 取总体平均分
     *
     * @param schoolId   schoolId
     * @param courseCode courseCode
     * @param examId     examId
     * @param filter     filter
     * @return 结果
     */
    @Override
    public SurveyTeacherExamCourseResult findAvgScore(Long schoolId, String courseCode, Long examId, boolean filter) {
        //取总体和应届平均分
        QueryWrapper<TAExamCourse> taExamCourseQueryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(schoolId)) {
            taExamCourseQueryWrapper.lambda().eq(TAExamCourse::getSchoolId, schoolId);
        }
        if (Objects.nonNull(examId)) {
            taExamCourseQueryWrapper.lambda().eq(TAExamCourse::getExamId, examId);
        }
        if (Objects.nonNull(courseCode)) {
            taExamCourseQueryWrapper.lambda().eq(TAExamCourse::getCourseCode, courseCode);
        }
        if (filter) {
            QueryWrapper<TBExamCourse> tbExamCourseQueryWrapper = new QueryWrapper<>();
            tbExamCourseQueryWrapper.lambda().eq(TBExamCourse::getSchoolId, schoolId);
            if (Objects.nonNull(examId)) {
                tbExamCourseQueryWrapper.lambda().eq(TBExamCourse::getExamId, examId);
            }
            if (Objects.nonNull(courseCode)) {
                tbExamCourseQueryWrapper.lambda().eq(TBExamCourse::getCourseCode, courseCode);
            }
            tbExamCourseQueryWrapper.lambda().eq(TBExamCourse::getPublishStatus, PublishStatusEnum.PUBLISH);
            TBExamCourse tbExamCourse = tbExamCourseService.getOne(tbExamCourseQueryWrapper);
            if (Objects.isNull(tbExamCourse)) {
                BigDecimal bigDecimal = new BigDecimal(0);
                return new SurveyTeacherExamCourseResult(bigDecimal, bigDecimal, bigDecimal, bigDecimal);
            }
        }
        TAExamCourse taExamCourse = taExamCourseService.getOne(taExamCourseQueryWrapper);
        BigDecimal bigDecimal = new BigDecimal(1);
        return new SurveyTeacherExamCourseResult(taExamCourse.getAvgScore(), bigDecimal.subtract(taExamCourse.getPassRate()).multiply(new BigDecimal("100")), taExamCourse.getCurrentAvgScore(), bigDecimal.subtract(taExamCourse.getCurrentPassRate()).multiply(new BigDecimal("100")));
    }

    /**
     * 武汉大学退出
     */
    @Override
    public void whuLogout() throws IOException {
        HttpServletRequest request = ServletUtil.getRequest();
        HttpServletResponse response = ServletUtil.getResponse();
        HttpSession session = request.getSession();
        if (Objects.nonNull(session)) {
            session.invalidate();
            log.info("logout is come in,session:{}", JacksonUtil.parseJson(session));
        }
        String casLogoutURL = "http://cas.whu.edu.cn/authserver/logout";
        // service后面带的参数为应用的访问地址，需要使用URLEncoder进行编码
        String redirectURL = casLogoutURL + "?service=" + URLEncoder.encode("https://ggjckcjfx.whu.edu.cn/api/report/wuda/open/user/authentication", SystemConstant.CHARSET_NAME);
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.sendRedirect(redirectURL);
    }

    /**
     * 第三方退出
     *
     * @param tbSession
     */
    @Override
    public void thirdLogout(TBSession tbSession) throws IOException {
        SysUser sysUser = commonCacheService.userCache(Long.parseLong(tbSession.getIdentity()));
        try {
            tbSessionService.removeById(tbSession.getId());
            redisUtil.deleteUserSession(tbSession.getId());
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (Objects.nonNull(sysUser)) {
                HttpServletResponse response = ServletUtil.getResponse();
                String redirectURL = ThirdUserAuthCacheUtil.getAuthReturnUrl(String.valueOf(sysUser.getId()));
                ThirdUserAuthCacheUtil.deleteCache(sysUser.getId());
                response.setHeader("Access-Control-Allow-Origin", "*");
                response.sendRedirect(redirectURL);
            }
        }
    }

    /**
     * 获取开课课程考试课程总览
     *
     * @param semesterId semesterId
     * @param examId     examId
     * @param courseCode courseCode
     * @return return
     */
    @Override
    public TeachCourseSurveyResult getTeachCourseSurveyInfo(Long semesterId, Long examId, String courseCode) {
        TBExam tbExam = tbExamService.getById(examId);
        TAExamCourse taExamCourse = taExamCourseService.getOne(new QueryWrapper<TAExamCourse>().lambda().eq(TAExamCourse::getExamId, examId).eq(TAExamCourse::getCourseCode, courseCode));
        TeachCourseSurveyResult teachCourseSurveyResult = new TeachCourseSurveyResult();
        teachCourseSurveyResult.setExamTime(tbExam.getExamTime());
        teachCourseSurveyResult.setCourseCode(taExamCourse.getCourseCode());
        teachCourseSurveyResult.setCourseName(taExamCourse.getCourseName());
        teachCourseSurveyResult.setFullScore(taExamCourse.getTotalScore());
        teachCourseSurveyResult.setRealityCount(taExamCourse.getRealityCount());
        teachCourseSurveyResult.setInspectCollegeCount(taExamCourse.getInspectCollegeCount());
        teachCourseSurveyResult.setAbsentCount(taExamCourse.getAbsentCount());
        teachCourseSurveyResult.setCoefficient(taExamCourse.getCoefficient());
        teachCourseSurveyResult.setTeachCollegeName(taExamCourse.getTeachCollegeName());
        teachCourseSurveyResult.setInspectCollegeNames(taExamCourse.getInspectCollegeNames());
        return teachCourseSurveyResult;
    }

    @Override
    @Transactional
    public BasicAttachment saveAttachment(MultipartFile file, String md5, UploadFileEnum type) {
        return saveAttachmentCommon(file, md5, type, null);
    }

    /**
     * 保存附件公用
     *
     * @param file
     * @param md5
     * @param type
     * @param objId
     * @return
     */
    @Override
    public BasicAttachment saveAttachmentCommon(MultipartFile file, String md5, UploadFileEnum type, Long objId) {
        if (Objects.isNull(type)) {
            throw ExceptionResultEnum.ATTACHMENT_TYPE_EMPTY.exception();
        }
        BasicAttachment basicAttachment = null;
        try {
            SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
            int temp = file.getOriginalFilename().lastIndexOf(".");
            String fileName = file.getOriginalFilename().substring(0, temp);
            String format = file.getOriginalFilename().substring(temp, file.getOriginalFilename().length());
            List<String> attachmentTypeList = dictionaryConfig.sysDomain().getAttachmentType();
            if (Objects.nonNull(format)) {
                long count = attachmentTypeList.stream().filter(s -> format.equalsIgnoreCase(s)).count();
                if (count == 0) {
                    throw ExceptionResultEnum.ERROR.exception("文件格式只能为" + attachmentTypeList.toString());
                }
            }
            int attachmentLength = dictionaryConfig.sysDomain().getAttachmentLength().intValue();
            if (Objects.nonNull(fileName) && fileName.length() > attachmentLength) {
                throw ExceptionResultEnum.ERROR.exception("文件名长度不能超过" + attachmentLength + "个字符");
            }
            long size = file.getSize();
            BigDecimal b = new BigDecimal(size);
            BigDecimal num = new BigDecimal(1024);
            b = b.divide(num, 2, BigDecimal.ROUND_HALF_UP).divide(num, 2, BigDecimal.ROUND_HALF_UP)
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            double attachmentSize = dictionaryConfig.sysDomain().getAttachmentSize().doubleValue();
            if (b.doubleValue() > attachmentSize) {
                throw ExceptionResultEnum.ERROR.exception("文件大小不能超过" + attachmentSize + "MB");
            }
            log.info("fileName:{}", fileName);
            log.info("format:{}", format);
            log.info("size:{}", b);
            log.info("getOriginalFilename:{}", file.getOriginalFilename());
            String fileMd5 = DigestUtils.md5Hex(file.getBytes());
            log.info("fileMd5:{}", fileMd5);
            log.info("md5:{}", md5);
            if (!Objects.equals(fileMd5, md5)) {
                throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
            }
            boolean oss = dictionaryConfig.sysDomain().isOss();
            LocalDateTime nowTime = LocalDateTime.now();
            StringJoiner stringJoiner = new StringJoiner("");
            if (!oss) {
                stringJoiner.add(SystemConstant.TEMP_FILES_DIR).add(File.separator);
            }
            stringJoiner.add(type.getTitle()).add(File.separator);
//            else if (type == UploadFileEnum.PAPER) {//试卷需要单独
//                stringJoiner.add(type.getTitle()).add(File.separator);
//            } else if (type == UploadFileEnum.UPLOAD) {
//                stringJoiner.add(type.getTitle()).add(File.separator);
//            }
            stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
                    .add(String.format("%02d", nowTime.getMonthValue())).add(File.separator)
                    .add(String.format("%02d", nowTime.getDayOfMonth()));

            JSONObject jsonObject = new JSONObject();
            stringJoiner.add(File.separator).add(SystemConstant.getUuid()).add(format);
            if (oss) {//上传至oss\
                String dirName = stringJoiner.toString().replaceAll("\\\\", "/");
                fileStoreUtil.ossUpload(dirName, file.getInputStream(), md5, type.getFssType());

                jsonObject.put(SystemConstant.TYPE, SystemConstant.OSS);
                jsonObject.put(SystemConstant.PATH, dirName);
            } else {//上传至服务器
                File finalFile = new File(stringJoiner.toString());
                if (!finalFile.exists()) {
                    finalFile.getParentFile().mkdirs();
                    finalFile.createNewFile();
                }
                FileUtils.copyInputStreamToFile(file.getInputStream(), finalFile);
                jsonObject.put(SystemConstant.TYPE, SystemConstant.LOCAL);
                jsonObject.put(SystemConstant.PATH, stringJoiner.toString());
            }
            jsonObject.put(SystemConstant.UPLOAD_TYPE, type);

            basicAttachment = new BasicAttachment(jsonObject.toJSONString(), fileName, format, b, fileMd5, requestUser.getId(), objId);
            basicAttachmentService.save(basicAttachment);

            // pdf需要读取总页数
            Integer pages = 0;
            if (".pdf".equals(format)) {
                PdfReader pdfReader = new PdfReader(file.getBytes());
                pages = pdfReader.getNumberOfPages();
            }
            basicAttachment.setPages(pages);
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
            basicAttachmentService.deleteAttachment(basicAttachment);
            if (e instanceof ApiException) {
                ResultUtil.error((ApiException) e, e.getMessage());
            } else {
                ResultUtil.error(e.getMessage());
            }
        }
        return basicAttachment;
    }
}
