package com.ningdatech.carapi.driver.manage;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.carapi.archives.entity.ExamPaper;
import com.ningdatech.carapi.archives.entity.ExamPaperQuestion;
import com.ningdatech.carapi.archives.entity.PaperQuestion;
import com.ningdatech.carapi.archives.enums.DriverRelationConstant;
import com.ningdatech.carapi.archives.enums.ExamPaperQualifiedScoreConstant;
import com.ningdatech.carapi.archives.enums.IsRandomEnum;
import com.ningdatech.carapi.archives.enums.PaperQuestionStatusEnum;
import com.ningdatech.carapi.archives.manage.ExaminationPaperManage;
import com.ningdatech.carapi.archives.model.ExamRuleInfo;
import com.ningdatech.carapi.archives.model.po.ReqExamRulePO;
import com.ningdatech.carapi.archives.model.po.ReqSaveExamPaperPO;
import com.ningdatech.carapi.archives.model.vo.ResExamRuleVO;
import com.ningdatech.carapi.archives.service.IExamPaperQuestionService;
import com.ningdatech.carapi.archives.service.IExamPaperService;
import com.ningdatech.carapi.archives.service.IPaperQuestionService;
import com.ningdatech.carapi.common.constant.GenderEnum;
import com.ningdatech.carapi.common.constant.UserRoleTypeEnum;
import com.ningdatech.carapi.common.constant.VehicleTypeEnum;
import com.ningdatech.carapi.common.contants.DefValConstants;
import com.ningdatech.carapi.common.helper.UserInfoHelper;
import com.ningdatech.carapi.common.util.ExcelDownUtil;
import com.ningdatech.carapi.common.util.ExcelExportStyle;
import com.ningdatech.carapi.datascope.aop.LambdaDataScope;
import com.ningdatech.carapi.datascope.contants.LambdaDataScopeTypeEnum;
import com.ningdatech.carapi.driver.assembler.TrainingAssembler;
import com.ningdatech.carapi.driver.constant.*;
import com.ningdatech.carapi.driver.constants.DriverConstant;
import com.ningdatech.carapi.driver.entity.*;
import com.ningdatech.carapi.driver.helper.DriverBehaviorScoreHelper;
import com.ningdatech.carapi.driver.helper.DriverInfoHelper;
import com.ningdatech.carapi.driver.model.*;
import com.ningdatech.carapi.driver.model.bo.CodingRuleBO;
import com.ningdatech.carapi.driver.model.dto.TrainerInfoExportDTO;
import com.ningdatech.carapi.driver.model.po.*;
import com.ningdatech.carapi.driver.model.query.TrainingOrgTrainersQuery;
import com.ningdatech.carapi.driver.model.vo.*;
import com.ningdatech.carapi.driver.service.*;
import com.ningdatech.carapi.scheduler.contants.TaskConstant;
import com.ningdatech.carapi.sys.entity.Company;
import com.ningdatech.carapi.sys.entity.dto.RegionDTO;
import com.ningdatech.carapi.sys.entity.enumeration.CodingRuleBehaviorTypeEnum;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;
import com.ningdatech.carapi.sys.service.CompanyService;
import com.ningdatech.carapi.user.model.vo.UserBasicInfoVO;
import com.ningdatech.carapi.user.security.auth.model.UserInfoDetails;
import com.ningdatech.carapi.user.util.LoginUserUtil;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.RequiredArgsConstructor;

/**
 * @author liuxinxin
 * @date 2022/10/10 上午10:00
 */
@Component
@RequiredArgsConstructor
public class TrainingOrganizationManage {
    private final ITrainingOrganizationService iTrainingOrganizationService;
    private final RegionsCacheHelper regionsCacheHelper;
    private final ITrainingOrganizationMaterialService iTrainingOrganizationMaterialService;
    private final ITrainingOrganizationTrainerService iTrainingOrganizationTrainerService;
    private final CompanyService companyService;
    private final IDriverInfoService driverInfoService;
    private final UserInfoHelper userInfoHelper;
    private final ITrainingMaterialService iTrainingMaterialService;
    private final IExamRecordService iExamRecordService;
    private final IExamRecordDetailService iExamRecordDetailService;
    private final IDriverScoreInfoService iDriverScoreInfoService;
    private final IDriverScoreRecordService iDriverScoreRecordService;
    private final DriverBehaviorScoreHelper driverBehaviorScoreHelper;
    private final DriverInfoHelper driverInfoHelper;
    private final IStarManageService starManageService;
    private final ITrainingOrganizationTrainerService trainingOrganizationTrainerService;
    private final IDriverInfoService iDriverInfoService;
    private final ExaminationPaperManage examinationPaperManage;
    private final IPaperQuestionService iPaperQuestionService;
    private final IExamPaperQuestionService iExamPaperQuestionService;
    private final IExamPaperService iExamPaperService;
    private final ICertificateService certificateService;


    public PageVo<ResTrainingOrganizationListVO> trainingOrganizationList(ReqTrainingOrganizationListPO request) {
        Integer pageNumber = request.getPageNumber();
        Integer pageSize = request.getPageSize();
        LambdaQueryWrapper<TrainingOrganization> wrapper = Wrappers.lambdaQuery(TrainingOrganization.class);
        TrainingOrganizationManage context = (TrainingOrganizationManage) AopContext.currentProxy();
        context.buildQuery(wrapper, request);
        Page<TrainingOrganization> page = iTrainingOrganizationService.page(new Page<>(pageNumber, pageSize), wrapper);

        PageVo<ResTrainingOrganizationListVO> pageVo = new PageVo<>();
        long total = page.getTotal();
        pageVo.setTotal(total);
        if (total == 0) {
            return pageVo;
        }
        List<TrainingOrganization> records = page.getRecords();
        // 获取培训组织关联的试卷信息
        List<Long> examPaperIds = records.stream().map(TrainingOrganization::getExamPaperId).collect(Collectors.toList());
        Map<Long, ExamPaper> examPaperMap = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class)
                        .in(ExamPaper::getId, examPaperIds)).stream()
                .collect(Collectors.toMap(ExamPaper::getId, e -> e));

        List<ResTrainingOrganizationListVO> resVoList = records.stream().map(r -> {
            ResTrainingOrganizationListVO resVO = new ResTrainingOrganizationListVO();
            Long trainingOrganizationId = r.getId();
            resVO.setTrainingOrganizationId(trainingOrganizationId);
            resVO.setTrainingTopic(r.getTrainingTopic());
            resVO.setAddress(r.getAddress());
            resVO.setTrainingStartTime(r.getTrainingStartTime());
            resVO.setTrainingEndTime(r.getTrainingEndTime());
            Long trainingOrganizationRegionId = r.getRegionId();
            if (Objects.nonNull(trainingOrganizationRegionId)) {
                resVO.setRegionId(trainingOrganizationRegionId);
                resVO.setRegionName(regionsCacheHelper.getDisplayName(trainingOrganizationRegionId));
            }
            resVO.setCreateTime(r.getCreateOn());
            if (Objects.nonNull(r.getTrainingMode())) {
                resVO.setTrainingMode(TrainingModeEnum.valueOf(r.getTrainingMode()));
            }
            if (Objects.nonNull(r.getTrainingStatus())) {
                resVO.setTrainingStatus(TrainingStatusEnum.valueOf(r.getTrainingStatus()));
            }
            List<Long> examPaperIdList = Lists.newArrayList();
            List<String> examPaperNameList = Lists.newArrayList();
            // 判断培训组织关联的试卷信息是否是随机试卷
            if (DefValConstants.RANDOM_EXAM_PAPER_ID.equals(r.getExamPaperId()) &&
                    StringUtils.isNotBlank(r.getRandomExamPaperId())){
                // 是随机试卷,返回随机试卷信息
                List<Long> idList = Arrays.stream(r.getRandomExamPaperId().split(StrPool.COMMA)).map(Long::valueOf).collect(Collectors.toList());
                examPaperIdList.addAll(idList);
                List<String> nameList = Arrays.stream(r.getExamPaperName().split(StrPool.COMMA)).collect(Collectors.toList());
                examPaperNameList.addAll(nameList);
            }else {
                examPaperIdList.add(r.getExamPaperId());
                examPaperNameList.add(r.getExamPaperName());
            }
            resVO.setExamPaperId(examPaperIdList);
            resVO.setExamPaperName(examPaperNameList);
            // 是线上考试且考试未开始要可以设置考试时间
            String trainingMode = r.getTrainingMode();
            if (TrainingModeEnum.online.name().equals(trainingMode)){
                LocalDateTime examStartTime = r.getExamStartTime();
                if (Objects.isNull(examStartTime) || LocalDateTime.now().compareTo(examStartTime) < 0){
                    resVO.setCanSetExamTime(Boolean.TRUE);
                }
            }
            resVO.setExamStartTime(r.getExamStartTime());
            resVO.setExamEndTime(r.getExamEndTime());
            return resVO;
        }).collect(Collectors.toList());
        pageVo.setRecords(resVoList);
        return pageVo;
    }

    @LambdaDataScope(type = LambdaDataScopeTypeEnum.ONLY_REGION)
    public void buildQuery(LambdaQueryWrapper<TrainingOrganization> wrapper, ReqTrainingOrganizationListPO request) {
        // 获取入参
        String address = request.getAddress();
        String trainingTopic = request.getTrainingTopic();
        Long regionId = request.getRegionId();
        TrainingModeEnum trainingMode = request.getTrainingMode();
        // 转换参数
        String trainingModeStr = Objects.nonNull(trainingMode) ? trainingMode.name() : null;
        List<Long> regionIdList = new ArrayList<>();
        if (Objects.nonNull(regionId)) {
            regionIdList = regionsCacheHelper.listChildRegionId(regionId);
        }
        List<String> trainingTypeList = request.getTrainingTypeList();
        wrapper.in(CollectionUtil.isNotEmpty(regionIdList), TrainingOrganization::getRegionId, regionIdList)
                .eq(StringUtils.isNotBlank(trainingModeStr), TrainingOrganization::getTrainingMode, trainingModeStr)
                .like(StringUtils.isNotBlank(address), TrainingOrganization::getAddress, address)
                .eq(StringUtils.isNotBlank(trainingTopic), TrainingOrganization::getTrainingTopic, trainingTopic)
                .in(CollectionUtil.isNotEmpty(trainingTypeList), TrainingOrganization::getTrainingType, trainingTypeList)
                .orderByDesc(TrainingOrganization::getCreateOn);
    }

    public ResTrainingOrganizationDetailVO trainingOrganizationDetail(Long organizationId) {

        UserBasicInfoVO userFullInfo = userInfoHelper.getUserFullInfo();
        Long roleId = userFullInfo.getRoleId();
        Long userId = userFullInfo.getUserId();

        LambdaQueryWrapper<TrainingOrganization> wrapper = Wrappers.lambdaQuery(TrainingOrganization.class);
        TrainingOrganizationManage context = (TrainingOrganizationManage) AopContext.currentProxy();
        context.buildTrainingOrganizationQuery(wrapper, organizationId);
        TrainingOrganization trainingOrganization = iTrainingOrganizationService.getOne(wrapper);
        //TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(organizationId);
        if (Objects.isNull(trainingOrganization)) {
            throw new BizException("培训组织不存在");
        }
        // 装配培训基本信息
        TrainingBasicInfo trainingBasicInfo = TrainingAssembler.buildTrainingBasicInfo(trainingOrganization);

        // 装配培训资料
        List<TrainingOrganizationMaterial> trainingOrganizationMaterialList = iTrainingOrganizationMaterialService
                .list(Wrappers.lambdaQuery(TrainingOrganizationMaterial.class)
                        .eq(TrainingOrganizationMaterial::getTrainingOrganizationId, organizationId));
        List<Long> materialIdList = trainingOrganizationMaterialList.stream()
                .map(TrainingOrganizationMaterial::getTrainingMaterialId).collect(Collectors.toList());
        List<TrainingMaterial> materialList = iTrainingMaterialService.list(Wrappers.lambdaQuery(TrainingMaterial.class)
                .in(TrainingMaterial::getId, materialIdList));
        TrainingMaterialOverviewInfo trainingMaterialOverviewInfo = TrainingAssembler.buildTrainingMaterialOverviewInfo(materialList);

        // 装配试卷
        TrainingExamPaperInfo trainingExamPaperInfo = new TrainingExamPaperInfo();
        Long examPaperId = trainingOrganization.getExamPaperId();
        trainingExamPaperInfo.setExamPaperId(examPaperId);
        // 如果是随机试卷且登录用户是驾驶员
        if (DefValConstants.RANDOM_EXAM_PAPER_ID.equals(examPaperId)){
            // 随机试卷
            trainingExamPaperInfo.setExamPaperName(DefValConstants.RANDOM_PAPER);
            if (UserRoleTypeEnum.DRIVER_ROLE_ID.getId().equals(roleId)){
                ExamPaper examPaper = iExamPaperService.getOne(Wrappers.lambdaQuery(ExamPaper.class)
                        .eq(ExamPaper::getTrainingOrganizationId, organizationId)
                        .eq(ExamPaper::getDriverUserId, userId));
                if (Objects.isNull(examPaper)){
                    throw new BizException("未获取到随机试卷!");
                }
                trainingExamPaperInfo.setExamPaperName(examPaper.getExamPaperName());
                trainingExamPaperInfo.setExamPaperId(examPaper.getId());
            }
        } else {
            trainingExamPaperInfo.setExamPaperName(trainingOrganization.getExamPaperName());
        }

        // 装配培训人员信息
        List<TrainingOrganizationTrainer> trainerList = iTrainingOrganizationTrainerService
                .list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                        .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, organizationId));

        List<OrganizationTrainingTrainersInfo> trainersInfoList = trainerList.stream()
                .map(TrainingAssembler::buildOrganizationTrainingTrainersInfo).collect(Collectors.toList());

        ResTrainingOrganizationDetailVO resVO = new ResTrainingOrganizationDetailVO();
        resVO.setTrainingBasicInfo(trainingBasicInfo);
        resVO.setTrainingExamPaperInfo(trainingExamPaperInfo);
        resVO.setTrainingMaterialOverviewInfo(trainingMaterialOverviewInfo);
        resVO.setTrainingTrainersInfoList(trainersInfoList);
        return resVO;
    }

    @LambdaDataScope(type = LambdaDataScopeTypeEnum.ONLY_REGION)
    public void buildTrainingOrganizationQuery(LambdaQueryWrapper<TrainingOrganization> wrapper, Long organizationId) {
        wrapper.eq(TrainingOrganization::getId,organizationId);
    }

    /**
     * 新建培训组织(新接口)
     *
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResCreateTrainingOrgVO createTrainingOrganization(ReqCreateTrainingOrgPO request) {
        // 获取入参信息
        TrainingBasicInfo trainingBasicInfo = request.getTrainingBasicInfo();
        String trainingType = trainingBasicInfo.getTrainingType().name();
        TrainingExamPaperInfo trainingExamPaperInfo = request.getTrainingExamPaperInfo();
        List<TrainingTrainersInfo> trainingTrainersInfoList = request.getTrainingTrainersInfoList();
        TrainingMaterialOverviewInfo trainingMaterialOverviewInfo = request.getTrainingMaterialOverviewInfo();

        // 获取登陆账号信息
        UserInfoDetails loginUser = LoginUserUtil.loginUserDetail();
        Long loginUserId = loginUser.getUserId();
        // 获取登陆者的 regionId
        Long regionId = userInfoHelper.getUserRegionId();

        // 保存培训基础信息 考试试卷
        TrainingOrganization saveRecord = assemblerTrainingOrganization(trainingBasicInfo, trainingExamPaperInfo, trainingTrainersInfoList, null);
        saveRecord.setCreateBy(loginUserId);
        saveRecord.setCreateOn(LocalDateTime.now());
        saveRecord.setRegionId(regionId);
        iTrainingOrganizationService.save(saveRecord);
        ResCreateTrainingOrgVO resCreateTrainingOrgVO = new ResCreateTrainingOrgVO();
        resCreateTrainingOrgVO.setTrainingOrganizationId(saveRecord.getId());

        // 如果是随机试卷 保存生成的随机试卷与培训组织的关联
        if (DefValConstants.RANDOM_EXAM_PAPER_ID.equals(saveRecord.getExamPaperId()) &&
        StringUtils.isNotBlank(saveRecord.getRandomExamPaperId())){
            List<Long> idList = Arrays.stream(saveRecord.getRandomExamPaperId().split(StrPool.COMMA)).map(Long::valueOf).collect(Collectors.toList());
            List<ExamPaper> examPapers = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class).in(ExamPaper::getId, idList));
            List<ExamPaper> examPaperList = examPapers.stream().peek(e -> e.setTrainingOrganizationId(saveRecord.getId())).collect(Collectors.toList());
            iExamPaperService.updateBatchById(examPaperList);
        }

        // 保存培训资料
        // TODO 校验材料列表
        List<TrainingMaterialInfo> allList = trainingMaterialOverviewInfo.getAllList();
        List<TrainingOrganizationMaterial> materialSaveRecordList = new ArrayList<>();
        for (TrainingMaterialInfo trainingMaterialInfo : allList) {
            TrainingOrganizationMaterial materialSaveRecord = new TrainingOrganizationMaterial();
            materialSaveRecord.setCreateBy(loginUserId);
            materialSaveRecord.setCreateOn(LocalDateTime.now());
            materialSaveRecord.setUpdateOn(LocalDateTime.now());
            materialSaveRecord.setMaterialType(trainingMaterialInfo.getMaterialType().name());
            materialSaveRecord.setTrainingMaterialId(trainingMaterialInfo.getTrainingMaterialId());
            materialSaveRecord.setTrainingOrganizationId(saveRecord.getId());
            materialSaveRecordList.add(materialSaveRecord);
        }
        iTrainingOrganizationMaterialService.saveBatch(materialSaveRecordList);

        if (CollectionUtil.isNotEmpty(trainingTrainersInfoList)) {

            // 根据培训类型，将选择的培训人员设置为不能参加该类型的培训，直至上传合格证培训完成，再次更新
            List<Long> driverIds = trainingTrainersInfoList.stream().map(TrainingTrainersInfo::getDriverId).collect(Collectors.toList());
            List<DriverInfo> driverInfos = driverInfoService.listByIds(driverIds);
            for (DriverInfo driverInfo : driverInfos) {
                if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingType)){
                    driverInfo.setDriverRegistrationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                } else if (TrainingTypeEnum.continuing_education_training.name().equals(trainingType)) {
                    driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                } else if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingType)) {
                    driverInfo.setReplacementRegistrationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                }
                // 更新驾驶员是否可以参见培训报名标志
                driverInfo.setUpdateOn(LocalDateTime.now());
                driverInfoService.updateById(driverInfo);
            }
            List<TrainingTrainersInfo> distinctTrainingTrainersInfoList = new ArrayList<>();
            HashSet<Long> driverIdSet = new HashSet<>();
            for (TrainingTrainersInfo trainingTrainersInfo : trainingTrainersInfoList) {
                if (driverIdSet.add(trainingTrainersInfo.getDriverId())) {
                    distinctTrainingTrainersInfoList.add(trainingTrainersInfo);
                }
            }

            // 保存培训人员
            List<Long> companyIdList = distinctTrainingTrainersInfoList.stream().map(TrainingTrainersInfo::getCompanyId).collect(Collectors.toList());
            List<Long> driverIdList = distinctTrainingTrainersInfoList.stream().map(TrainingTrainersInfo::getDriverId).collect(Collectors.toList());

            Map<Long, Company> companyMap = companyService.listCompanyByIds(companyIdList);
            Map<Long, DriverInfo> driverInfoMap = driverInfoService.listDriverInfoByIds(driverIdList);
            List<TrainingOrganizationTrainer> trainerSaveRecordList =
                    assemblerTrainingOrganizationTrainerList(distinctTrainingTrainersInfoList, driverInfoMap, companyMap, saveRecord.getId()
                            , trainingExamPaperInfo.getExamPaperId(), trainingExamPaperInfo.getExamPaperName(),trainingBasicInfo,saveRecord);
            iTrainingOrganizationTrainerService.saveBatch(trainerSaveRecordList);
        }

        // 新建考试记录
        ExamRecord examRecordSaveRecord = assemblerSaveExamRecord(LoginUserUtil.getUserId()
                , request.getTrainingExamPaperInfo().getExamPaperId(), saveRecord.getId()
                , trainingBasicInfo.getTrainingType().name());
        iExamRecordService.save(examRecordSaveRecord);
        return resCreateTrainingOrgVO;
    }

    /**
     * 装配考试记录列表
     *
     * @return
     */
    private ExamRecord assemblerSaveExamRecord(Long createBy, Long examPaperId
            , Long trainingOrganizationId, String examType) {
        ExamRecord saveRecord = new ExamRecord();
        saveRecord.setCreateBy(createBy);
        saveRecord.setCreateOn(LocalDateTime.now());
        saveRecord.setUpdateOn(LocalDateTime.now());
        saveRecord.setExamPaperId(examPaperId);
        saveRecord.setTrainingOrganizationId(trainingOrganizationId);
        saveRecord.setExamType(examType);
        return saveRecord;
    }


    /**
     * 装配新的培训组织驾驶员列表 SaveRecordList
     */
    private List<TrainingOrganizationTrainer> assemblerTrainingOrganizationTrainerList(List<TrainingTrainersInfo> trainingTrainersInfoList
            , Map<Long, DriverInfo> driverInfoMap, Map<Long, Company> companyMap
            , Long trainingOrganizationId, Long examPaperId, String examPaperName, TrainingBasicInfo trainingBasicInfo, TrainingOrganization saveRecord) {
        List<TrainingOrganizationTrainer> trainerSaveRecordList = new ArrayList<>();
        Map<String, ExamPaper> examPaperMap = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class)
                        .eq(ExamPaper::getTrainingOrganizationId,saveRecord.getId())
                        .select(ExamPaper::getId, ExamPaper::getExamPaperName))
                .stream().collect(Collectors.toMap(ExamPaper::getExamPaperName, e -> e));
        for (TrainingTrainersInfo trainersInfo : trainingTrainersInfoList) {
            DriverInfo driverInfo = driverInfoMap.get(trainersInfo.getDriverId());
            Company company = companyMap.get(driverInfo.getCompanyId());

            TrainingOrganizationTrainer trainerSaveRecord = new TrainingOrganizationTrainer();
            trainerSaveRecord.setCreateOn(LocalDateTime.now());
            trainerSaveRecord.setUpdateOn(LocalDateTime.now());
            trainerSaveRecord.setTrainingOrganizationId(trainingOrganizationId);
            trainerSaveRecord.setDriverName(driverInfo.getDriverName());
            trainerSaveRecord.setDriverUserId(driverInfo.getUserId());
            trainerSaveRecord.setDriverId(driverInfo.getId());
            // 如果是随机试卷
            String trainingTopic = trainingBasicInfo.getTrainingTopic();
            if (DefValConstants.RANDOM_PAPER.equals(examPaperName)){
                String randomExamPaperName = trainingTopic + StrPool.DASH + trainersInfo.getDriverName()
                        + StrPool.DASH + DefValConstants.RANDOM_PAPER;
                trainerSaveRecord.setExamPaperName(randomExamPaperName);
                ExamPaper examPaper = examPaperMap.get(randomExamPaperName);
                if (Objects.nonNull(examPaper)) {
                    trainerSaveRecord.setExamPaperId(examPaper.getId());
                }
            }else {
                trainerSaveRecord.setExamPaperId(examPaperId);
                trainerSaveRecord.setExamPaperName(examPaperName);
            }
            trainerSaveRecord.setExamRegistrationNo(generateExamRegistrationNo());
            trainerSaveRecord.setExaminationStatus(ExaminationStatusEnum.waiting_examination.name());
            trainerSaveRecord.setCompanyId(company.getId());
            trainerSaveRecord.setCompanyName(company.getName());
            trainerSaveRecordList.add(trainerSaveRecord);
        }
        return trainerSaveRecordList;
    }

    /**
     * 保存培训组织(新接口)
     *
     * @param request
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResCreateTrainingOrgVO saveTrainingOrganization(ReqCreateTrainingOrgPO request) {
        Long organizationId = request.getOrganizationId();
        TrainingExamPaperInfo trainingExamPaperInfo = request.getTrainingExamPaperInfo();
        if (Objects.isNull( trainingExamPaperInfo.getExamPaperName())){
            throw new BizException("请选择考试试卷!");
        }

        if (Objects.isNull(organizationId)) {
            return createTrainingOrganization(request);
        } else {
            return updateTrainingOrganization(request);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ResCreateTrainingOrgVO updateTrainingOrganization(ReqCreateTrainingOrgPO request) {
        // 获取入参信息
        Long organizationId = request.getOrganizationId();
        TrainingBasicInfo trainingBasicInfo = request.getTrainingBasicInfo();
        String trainingType = trainingBasicInfo.getTrainingType().name();
        TrainingExamPaperInfo trainingExamPaperInfo = request.getTrainingExamPaperInfo();
        List<TrainingTrainersInfo> trainingTrainersInfoList = request.getTrainingTrainersInfoList();
        TrainingMaterialOverviewInfo trainingMaterialOverviewInfo = request.getTrainingMaterialOverviewInfo();

        // 获取登陆账号信息
        Long loginUserId = LoginUserUtil.getUserId();
        // 获取登陆者的 regionId
        Long regionId = userInfoHelper.getUserRegionId();

        TrainingOrganization currentTrainingOrganization = iTrainingOrganizationService.getById(organizationId);
        if (Objects.isNull(currentTrainingOrganization)) {
            throw new BizException("培训组织不存在");
        }

        // 保存培训基础信息 考试试卷
        TrainingOrganization updateRecord = assemblerTrainingOrganization(trainingBasicInfo, trainingExamPaperInfo, trainingTrainersInfoList,organizationId);
        updateRecord.setId(organizationId);
        iTrainingOrganizationService.updateById(updateRecord);
        ResCreateTrainingOrgVO resCreateTrainingOrgVO = new ResCreateTrainingOrgVO();
        resCreateTrainingOrgVO.setTrainingOrganizationId(updateRecord.getId());

        // 如果是随机试卷 保存生成的随机试卷与培训组织的关联
        if (DefValConstants.RANDOM_EXAM_PAPER_ID.equals(updateRecord.getExamPaperId()) &&
                StringUtils.isNotBlank(updateRecord.getRandomExamPaperId())){
            List<Long> idList = Arrays.stream(updateRecord.getRandomExamPaperId().split(StrPool.COMMA)).map(Long::valueOf).collect(Collectors.toList());
            List<ExamPaper> examPapers = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class).in(ExamPaper::getId, idList));
            List<ExamPaper> examPaperList = examPapers.stream().peek(e -> e.setTrainingOrganizationId(updateRecord.getId())).collect(Collectors.toList());
            iExamPaperService.updateBatchById(examPaperList);
        }

        // 保存培训资料 先删除后新增
        // TODO 校验材料列表
        iTrainingOrganizationMaterialService.remove(Wrappers.lambdaQuery(TrainingOrganizationMaterial.class)
                .eq(TrainingOrganizationMaterial::getTrainingOrganizationId, organizationId));
        List<TrainingMaterialInfo> allList = trainingMaterialOverviewInfo.getAllList();
        List<TrainingOrganizationMaterial> materialSaveRecordList = new ArrayList<>();
        for (TrainingMaterialInfo trainingMaterialInfo : allList) {
            TrainingOrganizationMaterial materialSaveRecord = new TrainingOrganizationMaterial();
            materialSaveRecord.setCreateBy(loginUserId);
            materialSaveRecord.setCreateOn(LocalDateTime.now());
            materialSaveRecord.setUpdateOn(LocalDateTime.now());
            materialSaveRecord.setMaterialType(trainingMaterialInfo.getMaterialType().name());
            materialSaveRecord.setTrainingMaterialId(trainingMaterialInfo.getTrainingMaterialId());
            materialSaveRecord.setTrainingOrganizationId(organizationId);
            materialSaveRecordList.add(materialSaveRecord);
        }
        iTrainingOrganizationMaterialService.saveBatch(materialSaveRecordList);


        // 获取原来培训组织选择的驾驶员
        List<TrainingOrganizationTrainer> currentTrainingOrganizationTrainerList = iTrainingOrganizationTrainerService
                .list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, organizationId));
        List<Long> currentDriverIdList = currentTrainingOrganizationTrainerList.stream()
                .map(TrainingOrganizationTrainer::getDriverId)
                .collect(Collectors.toList());


        if (CollectionUtil.isNotEmpty(trainingTrainersInfoList)) {
            List<TrainingTrainersInfo> distinctTrainingTrainersInfoList = new ArrayList<>();
            HashSet<Long> driverIdSet = new HashSet<>();
            for (TrainingTrainersInfo trainingTrainersInfo : trainingTrainersInfoList) {
                if (driverIdSet.add(trainingTrainersInfo.getDriverId())) {
                    distinctTrainingTrainersInfoList.add(trainingTrainersInfo);
                }
            }
            // 保存培训人员 先删除再新增
            List<Long> companyIdList = distinctTrainingTrainersInfoList.stream()
                    .map(TrainingTrainersInfo::getCompanyId)
                    .collect(Collectors.toList());
            List<Long> driverIdList = distinctTrainingTrainersInfoList.stream()
                    .map(TrainingTrainersInfo::getDriverId)
                    .collect(Collectors.toList());

            List<Long> addList = new ArrayList<>();
            List<Long> removeList = new ArrayList<>();

            // 新增列表
            for (Long driverId : driverIdList) {
                if (!currentDriverIdList.contains(driverId)) {
                    addList.add(driverId);
                }
            }

            // 删除列表
            for (Long driverId : currentDriverIdList) {
                if (!driverIdList.contains(driverId)) {
                    removeList.add(driverId);
                }
            }

            // 删除培训人员，并将其培训标志更新为可参加对应的培训报名
            if (CollectionUtil.isNotEmpty(removeList)) {
                // 移除掉该培训组织删除掉的培训人员
                iTrainingOrganizationTrainerService.remove(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                        .eq(TrainingOrganizationTrainer::getTrainingOrganizationId,organizationId)
                        .in(TrainingOrganizationTrainer::getDriverId, removeList));
                List<DriverInfo> removeDriverInfos = driverInfoService.listByIds(removeList);
                for (DriverInfo driverInfo : removeDriverInfos) {
                    if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingType)){
                        driverInfo.setDriverRegistrationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                    } else if (TrainingTypeEnum.continuing_education_training.name().equals(trainingType)) {
                        driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                    } else if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingType)) {
                        driverInfo.setReplacementRegistrationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                    }
                    // 更新驾驶员是否可以参见培训报名标志
                    driverInfo.setUpdateOn(LocalDateTime.now());
                    driverInfoService.updateById(driverInfo);
                }
            }

            // 新增培训人员
            if (CollectionUtil.isNotEmpty(addList)) {
                // 根据培训类型，将新选择的培训人员设置为不能参加该类型的培训，直至上传合格证培训完成，再次更新
                List<DriverInfo> driverInfos = driverInfoService.listByIds(addList);
                for (DriverInfo driverInfo : driverInfos) {
                    if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingType)){
                        driverInfo.setDriverRegistrationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                    } else if (TrainingTypeEnum.continuing_education_training.name().equals(trainingType)) {
                        driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                    } else if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingType)) {
                        driverInfo.setReplacementRegistrationFlag(TrainingRegistrationFlagEnum.CAN_NOT_TAKE.getCode());
                    }
                    // 更新驾驶员是否可以参见培训报名标志
                    driverInfo.setUpdateOn(LocalDateTime.now());
                    driverInfoService.updateById(driverInfo);
                }

                distinctTrainingTrainersInfoList = distinctTrainingTrainersInfoList.stream().filter(r -> addList.contains(r.getDriverId())).collect(Collectors.toList());
                Map<Long, Company> idCompanyMap = companyService.listCompanyByIds(companyIdList);
                Map<Long, DriverInfo> idDriverInfoMap = driverInfoService.listDriverInfoByIds(driverIdList);

                List<TrainingOrganizationTrainer> trainerSaveRecordList = new ArrayList<>();
                Long examPaperId = trainingExamPaperInfo.getExamPaperId();
                String examPaperName = trainingExamPaperInfo.getExamPaperName();
                Map<String, ExamPaper> examPaperMap = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class)
                                .eq(ExamPaper::getTrainingOrganizationId,organizationId)
                                .select(ExamPaper::getId, ExamPaper::getExamPaperName))
                        .stream().collect(Collectors.toMap(ExamPaper::getExamPaperName, e -> e));
                for (TrainingTrainersInfo trainersInfo : distinctTrainingTrainersInfoList) {
                    DriverInfo driverInfo = idDriverInfoMap.get(trainersInfo.getDriverId());
                    if (Objects.nonNull(driverInfo)) {
                        Company company = idCompanyMap.get(driverInfo.getCompanyId());
                        TrainingOrganizationTrainer trainerSaveRecord = new TrainingOrganizationTrainer();
                        trainerSaveRecord.setCreateOn(LocalDateTime.now());
                        trainerSaveRecord.setUpdateOn(LocalDateTime.now());
                        trainerSaveRecord.setTrainingOrganizationId(organizationId);
                        trainerSaveRecord.setDriverName(driverInfo.getDriverName());
                        trainerSaveRecord.setDriverUserId(driverInfo.getUserId());
                        trainerSaveRecord.setDriverId(driverInfo.getId());

                        // 如果是随机试卷
                        String trainingTopic = trainingBasicInfo.getTrainingTopic();
                        if (DefValConstants.RANDOM_PAPER.equals(examPaperName)){
                            String randomExamPaperName = trainingTopic + StrPool.DASH + trainersInfo.getDriverName()
                                    + StrPool.DASH + DefValConstants.RANDOM_PAPER;
                            trainerSaveRecord.setExamPaperName(randomExamPaperName);
                            ExamPaper examPaper = examPaperMap.get(randomExamPaperName);
                            if (Objects.nonNull(examPaper)) {
                                trainerSaveRecord.setExamPaperId(examPaper.getId());
                            }
                        }else {
                            trainerSaveRecord.setExamPaperId(examPaperId);
                            trainerSaveRecord.setExamPaperName(examPaperName);
                        }
                        trainerSaveRecord.setExamRegistrationNo(generateExamRegistrationNo());
                        trainerSaveRecord.setExaminationStatus(ExaminationStatusEnum.waiting_examination.name());
                        trainerSaveRecord.setCompanyId(company.getId());
                        trainerSaveRecord.setCompanyName(company.getName());
                        trainerSaveRecordList.add(trainerSaveRecord);
                    }
                }
                if (CollectionUtil.isNotEmpty(trainerSaveRecordList)) {
                    iTrainingOrganizationTrainerService.saveBatch(trainerSaveRecordList);
                }
            }
            // TODO 向所有驾驶员发送短信通知：
            //  {驾驶员姓名}您好：{散办名称}组织了关于{培训主题}的{培训模式}培训，
            //  培训时间为{培训开始 至 结束时间}，培训地点为{培训地点：线下培训时显示}，
            //  培训链接{生成培训链接，点击后跳转培训页面}，请准时参加培训。
            //  联系人：{联系人}，联系电话：{联系电话}，考证号：{准考证号}。
            for (TrainingTrainersInfo trainingTrainersInfo : trainingTrainersInfoList) {
                // 若是线下培训
                String offlineMessage = String.format(DefValConstants.OFFLINE_MESSAGE_TEMPLATE,trainingTrainersInfo.getDriverName() ,"散办名称", "培训主题","培训模式","培训开始时间","培训结束时间","培训地点","培训链接","联系人","联系电话","准考证号");
                // 若是线上培训
                String onlineMessage = String.format(DefValConstants.ONLINE_MESSAGE_TEMPLATE,trainingTrainersInfo.getDriverName() ,"散办名称", "培训主题","培训模式","培训开始时间","培训结束时间","培训链接","联系人","联系电话","准考证号");
                // TODO 调用发短信接口发送短信
            }
        }
        return resCreateTrainingOrgVO;
    }


    private TrainingOrganization assemblerTrainingOrganization(TrainingBasicInfo trainingBasicInfo, TrainingExamPaperInfo trainingExamPaperInfo, List<TrainingTrainersInfo> trainingTrainersInfoList, Long organizationId) {
        TrainingOrganization saveRecord = new TrainingOrganization();
        saveRecord.setUpdateOn(LocalDateTime.now());
        saveRecord.setTrainingTopic(trainingBasicInfo.getTrainingTopic());
        saveRecord.setTrainingStartTime(trainingBasicInfo.getTrainingStartTime());
        saveRecord.setTrainingEndTime(trainingBasicInfo.getTrainingEndTime());
        saveRecord.setAddress(trainingBasicInfo.getAddress());
        if (Objects.nonNull(trainingBasicInfo.getTrainingMode())) {
            saveRecord.setTrainingMode(trainingBasicInfo.getTrainingMode().name());
        }
        saveRecord.setTrainingType(trainingBasicInfo.getTrainingType().name());
        saveRecord.setContacts(trainingBasicInfo.getContacts());
        saveRecord.setContactNumber(trainingBasicInfo.getContactNumber());

        String trainingStatus = Objects.nonNull(trainingBasicInfo.getTrainingStatus()) ? trainingBasicInfo.getTrainingStatus().name() : TrainingStatusEnum.enable.name();
        saveRecord.setTrainingStatus(trainingStatus);
        saveRecord.setRemark(trainingBasicInfo.getRemark());
        Long examPaperId = trainingExamPaperInfo.getExamPaperId();
        String examPaperName = trainingExamPaperInfo.getExamPaperName();
        // 判断是否为随机试卷
        if (Objects.isNull(examPaperId) && DefValConstants.RANDOM_PAPER.equals(examPaperName)){
            List<ExamPaper> randomExamPaperList = createRandomPaper(trainingBasicInfo.getTrainingTopic(),trainingTrainersInfoList,organizationId);
            String idStr = randomExamPaperList.stream().map(r -> String.valueOf(r.getId())).collect(Collectors.joining(StrPool.COMMA));
            saveRecord.setRandomExamPaperId(idStr);
            saveRecord.setExamPaperId(DefValConstants.RANDOM_EXAM_PAPER_ID);
            String examNameList = randomExamPaperList.stream().map(ExamPaper::getExamPaperName).collect(Collectors.joining(StrPool.COMMA));
            saveRecord.setExamPaperName(examNameList);
        }else {
            saveRecord.setExamPaperId(examPaperId);
            saveRecord.setExamPaperName(examPaperName);
        }
        return saveRecord;
    }
    private List<ExamPaper> createRandomPaper(String trainTopic, List<TrainingTrainersInfo> trainingTrainersInfoList, Long organizationId) {
        // 获取当前登录用户所在区域的考试试卷规则
        UserBasicInfoVO userFullInfo = userInfoHelper.getUserFullInfo();
        Long regionId = userFullInfo.getRegionId();
        ReqExamRulePO request = new ReqExamRulePO();
        request.setRegionId(regionId);
        ResExamRuleVO examRule = examinationPaperManage.getExamRule(request);

        // 对在当前培训组织内的每个培训人员
        // 根据试卷规则从题库中抽取对应的题目,组成一套试卷
        List<ExamPaper> resList = Lists.newArrayList();
        List<Long> driverUserIdList = trainingTrainersInfoList.stream().map(TrainingTrainersInfo::getDriverUserId).collect(Collectors.toList());
        // 如果培训组织ID不为null 说明为编辑,先删除选择的培训人员所在培训组织已经生成的随机的试卷
        if (Objects.nonNull(organizationId) && CollUtil.isNotEmpty(driverUserIdList)){
            iExamPaperService.remove(Wrappers.lambdaQuery(ExamPaper.class)
                    .eq(ExamPaper::getTrainingOrganizationId, organizationId)
                    .eq(ExamPaper::getIsRandom, IsRandomEnum.RANDOM_PAPER.getCode())
                    .in(ExamPaper::getDriverUserId, driverUserIdList));
        }

        for (TrainingTrainersInfo trainingTrainersInfo : trainingTrainersInfoList) {
            ExamRuleInfo singleChoiceQuestionRule = examRule.getSingleChoiceQuestionRule();
            ExamRuleInfo multipleChoiceQuestionRule = examRule.getMultipleChoiceQuestionRule();
            ExamRuleInfo judgmentalQuestionRule = examRule.getJudgmentalQuestionRule();
            // 单选题
            Integer singleChoiceQuestionCnt = singleChoiceQuestionRule.getQuestionCnt();
            // 从题库中获取所有在使用的单选题
            List<PaperQuestion> singleList = iPaperQuestionService.list(Wrappers.lambdaQuery(PaperQuestion.class)
                    .eq(PaperQuestion::getQuestionStatus, PaperQuestionStatusEnum.IN_USE.name())
                    .eq(PaperQuestion::getQuestionType, QuestionTypeEnum.SINGLE_CHOICE.name()));
            // 使用 Collections.synchronizedList() 方法确保多线程安全
            List<PaperQuestion> singleSynchronizedList = Collections.synchronizedList(singleList);
            // 从列表中随机抽取指定数量的单选题
            List<ReqSaveExamPaperPO.ExamPaperQuestionIdPO> singleElements = getRandomElements(singleSynchronizedList, singleChoiceQuestionCnt);

            // 多选题
            Integer multipleChoiceQuestionCnt = multipleChoiceQuestionRule.getQuestionCnt();
            // 从题库中获取所有在使用的多选题
            List<PaperQuestion> multipleList = iPaperQuestionService.list(Wrappers.lambdaQuery(PaperQuestion.class)
                    .eq(PaperQuestion::getQuestionStatus, PaperQuestionStatusEnum.IN_USE.name())
                    .eq(PaperQuestion::getQuestionType, QuestionTypeEnum.MULTIPLE_CHOICE.name()));
            // 使用 Collections.synchronizedList() 方法确保多线程安全
            List<PaperQuestion> multipleSynchronizedList = Collections.synchronizedList(multipleList);
            // 从列表中随机抽取指定数量的多选题
            List<ReqSaveExamPaperPO.ExamPaperQuestionIdPO> multipleElements = getRandomElements(multipleSynchronizedList, multipleChoiceQuestionCnt);

            // 判断题
            Integer judgmentalQuestionCnt = judgmentalQuestionRule.getQuestionCnt();
            // 从题库中获取所有在使用的判断题
            List<PaperQuestion> judgmentalList = iPaperQuestionService.list(Wrappers.lambdaQuery(PaperQuestion.class)
                    .eq(PaperQuestion::getQuestionStatus, PaperQuestionStatusEnum.IN_USE.name())
                    .eq(PaperQuestion::getQuestionType, QuestionTypeEnum.JUDGMENTAL.name()));
            // 使用 Collections.synchronizedList() 方法确保多线程安全
            List<PaperQuestion> judgementalSynchronizedList = Collections.synchronizedList(judgmentalList);
            // 从列表中随机抽取指定数量的单选题
            List<ReqSaveExamPaperPO.ExamPaperQuestionIdPO> judgmentalElements = getRandomElements(judgementalSynchronizedList, judgmentalQuestionCnt);


            List<ReqSaveExamPaperPO.ExamPaperQuestionIdPO> examPaperQuestionIdList = new ArrayList<>();
            examPaperQuestionIdList.addAll(singleElements);
            examPaperQuestionIdList.addAll(multipleElements);
            examPaperQuestionIdList.addAll(judgmentalElements);

            List<Long> questionIdList = examPaperQuestionIdList.stream()
                    .map(ReqSaveExamPaperPO.ExamPaperQuestionIdPO::getPaperQuestionId).collect(Collectors.toList());

            List<PaperQuestion> paperQuestionList = iPaperQuestionService
                    .list(Wrappers.lambdaQuery(PaperQuestion.class).in(PaperQuestion::getId, questionIdList));
            Map<Long, String> paperQuestionIdTypeMap = paperQuestionList.stream().collect(Collectors.toMap(PaperQuestion::getId, PaperQuestion::getQuestionType));
            // 保存随机试卷基本信息
            ExamPaper examPaper = new ExamPaper();
            examPaper.setCreateOn(LocalDateTime.now());
            examPaper.setUpdateOn(LocalDateTime.now());
            examPaper.setExamPaperName(trainTopic + StrPool.DASH + trainingTrainersInfo.getDriverName()
                    + StrPool.DASH + DefValConstants.RANDOM_PAPER);
            examPaper.setFullScore(ExamPaperQualifiedScoreConstant.EXAM_PAPER_FULL_SCORE);
            examPaper.setQualifiedScore(ExamPaperQualifiedScoreConstant.EXAM_PAPER_QUALIFIED_SCORE);
            examPaper.setRegionId(regionId);
            examPaper.setExamRuleJson(JSONObject.toJSONString(examRule));
            examPaper.setIsRandom(IsRandomEnum.RANDOM_PAPER.getCode());
            examPaper.setDriverUserId(trainingTrainersInfo.getDriverUserId());
            iExamPaperService.save(examPaper);

            // 保存试卷题目关联关系
            List<ExamPaperQuestion> saveRecordList = new ArrayList<>();
            for (ReqSaveExamPaperPO.ExamPaperQuestionIdPO idPo : examPaperQuestionIdList) {
                ExamPaperQuestion examPaperQuestion = new ExamPaperQuestion();
                examPaperQuestion.setExamPaperId(examPaper.getId());
                examPaperQuestion.setPaperQuestionId(idPo.getPaperQuestionId());
                examPaperQuestion.setCreateOn(LocalDateTime.now());
                examPaperQuestion.setUpdateOn(LocalDateTime.now());
                examPaperQuestion.setSortValue(idPo.getSortValue());
                examPaperQuestion.setQuestionType(paperQuestionIdTypeMap.get(idPo.getPaperQuestionId()));
                examPaperQuestion.setIsRandom(IsRandomEnum.RANDOM_PAPER.getCode());
                saveRecordList.add(examPaperQuestion);
            }
            iExamPaperQuestionService.saveBatch(saveRecordList);

            List<Long> questionUsageCntUpdateList = examPaperQuestionIdList.stream().map(ReqSaveExamPaperPO.ExamPaperQuestionIdPO::getPaperQuestionId).collect(Collectors.toList());
            for (Long questionId : questionUsageCntUpdateList) {
                questionUsageCntUpdate(questionId);
            }
            resList.add(examPaper);
        }
        return resList;
    }

    public void questionUsageCntUpdate(Long questionId) {
        PaperQuestion paperQuestion = iPaperQuestionService.getById(questionId);
        if (Objects.isNull(paperQuestion)) {
            return;
        } else {
            long usage = iExamPaperQuestionService.count(Wrappers.lambdaQuery(ExamPaperQuestion.class)
                    .eq(ExamPaperQuestion::getPaperQuestionId, questionId));
            paperQuestion.setUsageCnt(usage);
            iPaperQuestionService.updateById(paperQuestion);
        }
    }

    private List<ReqSaveExamPaperPO.ExamPaperQuestionIdPO> getRandomElements(List<PaperQuestion> list, Integer count) {
        // 使用 Random 对象生成随机索引
        Random random = new Random();
        List<PaperQuestion> randomList = Lists.newArrayList();
        for (int i = 0; i < count; i++) {
            int index = random.nextInt(list.size());
            randomList.add(list.get(index));
        }
        AtomicLong i = new AtomicLong(1L);
        return randomList.stream().map(r -> {
            ReqSaveExamPaperPO.ExamPaperQuestionIdPO po = new ReqSaveExamPaperPO.ExamPaperQuestionIdPO();
            po.setPaperQuestionId(r.getId());
            po.setSortValue(i.longValue());
            i.incrementAndGet();
            return po;
        }).collect(Collectors.toList());
    }

    private String generateExamRegistrationNo() {
        String examRegistrationNo = RandomUtil.randomString(16);
        return examRegistrationNo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void uploadExamResult(ReqUploadExamResultPO request) {
        // 获取入参参数
        Long driverId = request.getDriverId();
        Long trainingOrganizationId = request.getTrainingOrganizationId();
        Integer score = request.getScore();

        // 获取驾驶员详情
        DriverInfo driverInfo = driverInfoService.getById(driverId);
        // 获取培训组织详情
        TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(trainingOrganizationId);

        String isQualified = score >= ExamPaperQualifiedScoreConstant.EXAM_PAPER_QUALIFIED_SCORE ? ExamQualifiedEnum.qualified.name() : ExamQualifiedEnum.unqualified.name();
        if (!TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())) {
            // 获取培训驾驶员信息 更新驾驶员培训人员信息
            TrainingOrganizationTrainer trainingOrganizationTrainer = iTrainingOrganizationTrainerService.getOne(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getDriverId, driverId)
                    .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, trainingOrganizationId));
            trainingOrganizationTrainer.setScore(score);
            trainingOrganizationTrainer.setIsQualified(isQualified);
            trainingOrganizationTrainer.setExaminationStatus(ExaminationStatusEnum.examined.name());
            iTrainingOrganizationTrainerService.saveOrUpdate(trainingOrganizationTrainer);
        }

        ExamRecord examRecord = iExamRecordService.getOne(Wrappers.lambdaQuery(ExamRecord.class)
                .eq(ExamRecord::getTrainingOrganizationId, trainingOrganizationId));
        // 新建考试记录
        ExamRecordDetail examRecordDetailSaveRecord = new ExamRecordDetail();
        examRecordDetailSaveRecord.setExamRecordId(examRecord.getId());
        examRecordDetailSaveRecord.setCompanyId(driverInfo.getCompanyId());
        examRecordDetailSaveRecord.setCompanyName(driverInfo.getCompanyName());
        examRecordDetailSaveRecord.setCreateBy(LoginUserUtil.getUserId());
        examRecordDetailSaveRecord.setCreateOn(LocalDateTime.now());
        examRecordDetailSaveRecord.setUpdateOn(LocalDateTime.now());
        examRecordDetailSaveRecord.setDriverId(driverInfo.getId());
        examRecordDetailSaveRecord.setDriverUserId(driverInfo.getUserId());
        examRecordDetailSaveRecord.setDriverName(driverInfo.getDriverName());
        examRecordDetailSaveRecord.setExamResult(isQualified);
        examRecordDetailSaveRecord.setExamTime(LocalDateTime.now());
        examRecordDetailSaveRecord.setExamType(examRecord.getExamType());
        examRecordDetailSaveRecord.setScore(score);
        examRecordDetailSaveRecord.setRegionId(driverInfo.getRegionId());
        iExamRecordDetailService.save(examRecordDetailSaveRecord);

        // 如果为培训报名考试 并且考试成绩合格 进入下一阶段
        if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingOrganization.getTrainingType())
                && ExamQualifiedEnum.qualified.name().equals(isQualified)) {
            driverInfo.setDriverRegistrationStatus(DriverRegistrationStatusEnum.upload_training_certificate_stage.name());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
            // 更新驾驶员分数为 100分
            DriverScoreInfo driverScoreInfo = iDriverScoreInfoService.getOne(Wrappers.lambdaQuery(DriverScoreInfo.class).eq(DriverScoreInfo::getDriverId, driverInfo.getId()));
            if (driverScoreInfo == null) {
                driverScoreInfo = new DriverScoreInfo();
                driverScoreInfo.setDriverId(driverInfo.getId());
                driverScoreInfo.setUserId(driverInfo.getUserId());
                driverScoreInfo.setCreateOn(LocalDateTime.now());
            }
            driverScoreInfo.setDriverScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            driverScoreInfo.setUpdateOn(LocalDateTime.now());
            iDriverScoreInfoService.saveOrUpdate(driverScoreInfo);

            DriverScoreRecord saveRecord = new DriverScoreRecord();
            saveRecord.setCreateOn(LocalDateTime.now());
            saveRecord.setUpdateOn(LocalDateTime.now());
            saveRecord.setDriverId(driverInfo.getId());
            saveRecord.setOperateReason("通过驾驶员报名培训并完成了考试");
            saveRecord.setOperateScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            saveRecord.setOperateType(ScoreOperateType.add.name());
            saveRecord.setUserId(driverInfo.getUserId());
            iDriverScoreRecordService.save(saveRecord);

            //初始化驾驶员星级及分数
            StarManage starManage = new StarManage();
            starManage.setCurrentScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            starManage.setCurrentStar(TaskConstant.Star.FIVE_STAR);
            starManage.setRegionId(driverInfo.getRegionId());
            starManage.setCompanyId(driverInfo.getCompanyId());
            starManage.setCompanyName(driverInfo.getCompanyName());
            starManage.setDriverName(driverInfo.getDriverName());
            starManage.setDriverId(driverId);
            starManage.setCreateOn(LocalDateTime.now());
            starManage.setUpdateOn(LocalDateTime.now());
            starManageService.save(starManage);

        }
        // 如果为继续教育考试 并且考试成绩合格  更新阶段和可参加继续教育培训报名标志 加分
        if (TrainingTypeEnum.continuing_education_training.name().equals(trainingOrganization.getTrainingType())
                && ExamQualifiedEnum.qualified.name().equals(isQualified)) {
            driverInfo.setContinuingEducationStatus(DriverReplacementRegistrationStatusEnum.available.name());
            driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
        }
        // 如果为换证培训 并且考试成绩合格 加分
        if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingOrganization.getTrainingType())
                && ExamQualifiedEnum.qualified.name().equals(isQualified)) {
            driverInfo.setReplacementRegistrationStatus(DriverReplacementRegistrationStatusEnum.upload_training_certificate_stage.name());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
        }
        // 如果为转码培训 并且考试成绩合格 加40分
        if (TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())
                && ExamQualifiedEnum.qualified.name().equals(isQualified)) {
            DriverScoreInfo driverScoreInfo = iDriverScoreInfoService.getOne(Wrappers.lambdaQuery(DriverScoreInfo.class).eq(DriverScoreInfo::getDriverId, driverInfo.getId()));
            CodingRuleBO codingRuleBehaviorScore = driverBehaviorScoreHelper.getCodingRuleBehaviorScore(CodingRuleBehaviorTypeEnum.complete_the_exam,null);
            Integer operationScore = codingRuleBehaviorScore.getOperationScore();
            if (driverScoreInfo == null) {
                driverScoreInfo = new DriverScoreInfo();
                driverScoreInfo.setDriverId(driverInfo.getId());
                driverScoreInfo.setUserId(driverInfo.getUserId());
                driverScoreInfo.setCreateOn(LocalDateTime.now());
                driverScoreInfo.setDriverScore(operationScore);
            } else {
                driverScoreInfo.setDriverScore(driverScoreInfo.getDriverScore() + operationScore);
            }
            driverScoreInfo.setUpdateOn(LocalDateTime.now());
            iDriverScoreInfoService.saveOrUpdate(driverScoreInfo);

            DriverScoreRecord saveRecord = new DriverScoreRecord();
            saveRecord.setCreateOn(LocalDateTime.now());
            saveRecord.setUpdateOn(LocalDateTime.now());
            saveRecord.setDriverId(driverInfo.getId());
            saveRecord.setOperateReason("驾驶员参加转码培训并完成了考试");
            saveRecord.setOperateScore(operationScore);
            saveRecord.setOperateType(ScoreOperateType.add.name());
            saveRecord.setUserId(driverInfo.getUserId());
            iDriverScoreRecordService.save(saveRecord);
        }
    }

    public void deleteTrainingOrganization(Long organizationId) {
        // TODO  补充校验逻辑
        LambdaQueryWrapper<TrainingOrganization> wrapper = Wrappers.lambdaQuery(TrainingOrganization.class);
        TrainingOrganizationManage context = (TrainingOrganizationManage) AopContext.currentProxy();
        context.buildTrainingOrganizationQuery(wrapper, organizationId);
        TrainingOrganization organization = iTrainingOrganizationService.getOne(wrapper);
        if (Objects.isNull(organization)) {
            throw new BizException("培训组织不存在");
        }
        //TrainingOrganization organization = iTrainingOrganizationService.getById(organizationId);

        String trainingType = organization.getTrainingType();

        if(iTrainingOrganizationService.removeById(organizationId)) {
            // 删除培训组织中的培训人员
            // 移除掉该培训组织删除掉的培训人员
            List<TrainingOrganizationTrainer> removeList = iTrainingOrganizationTrainerService.list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, organizationId));
            iTrainingOrganizationTrainerService.removeBatchByIds(removeList);
            List<Long> driverIds = removeList.stream().map(TrainingOrganizationTrainer::getDriverId).collect(Collectors.toList());
            List<DriverInfo> removeDriverInfos = driverInfoService.list(Wrappers.lambdaQuery(DriverInfo.class).in(DriverInfo::getId, driverIds));
            for (DriverInfo driverInfo : removeDriverInfos) {
                if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingType)) {
                    driverInfo.setDriverRegistrationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                } else if (TrainingTypeEnum.continuing_education_training.name().equals(trainingType)) {
                    driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                } else if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingType)) {
                    driverInfo.setReplacementRegistrationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
                }
                // 更新驾驶员是否可以参见培训报名标志
                driverInfo.setUpdateOn(LocalDateTime.now());
                driverInfoService.updateById(driverInfo);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void trainingOrganizationStatusModify(ReqTrainingOrganizationStatusModifyPO request) {
        Long organizationId = request.getOrganizationId();
        TrainingStatusEnum trainingStatus = request.getTrainingStatus();


        TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(organizationId);
        if (Objects.isNull(trainingOrganization)) {
            throw new BizException("该培训组织不存在");
        }

        trainingOrganization.setTrainingStatus(trainingStatus.name());
        trainingOrganization.setUpdateOn(LocalDateTime.now());
        iTrainingOrganizationService.updateById(trainingOrganization);

        // 转码培训只能有一个是生效状态
        if (TrainingStatusEnum.enable.equals(trainingStatus)
                && TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())) {
            iTrainingOrganizationService.update(Wrappers.lambdaUpdate(TrainingOrganization.class)
                    .eq(TrainingOrganization::getTrainingType, TrainingTypeEnum.transcoding_training.name())
                    .ne(TrainingOrganization::getId, organizationId)
                    .set(TrainingOrganization::getTrainingStatus, TrainingStatusEnum.disable.name()));
        }

    }

    public ResCurrentUserOrganizationVO getCurrentUserOrganization(ReqCurrentUserOrganizationPO request) {
        Long userId = LoginUserUtil.getUserId();
        TrainingTypeEnum trainingType = request.getTrainingType();

        List<TrainingOrganization> trainingOrganizationList = iTrainingOrganizationService.list(Wrappers.lambdaQuery(TrainingOrganization.class)
                .eq(TrainingOrganization::getTrainingType, trainingType));

        ResCurrentUserOrganizationVO resVO = new ResCurrentUserOrganizationVO();
        resVO.setStatus(CurrentUserOrganizationEnum.uncommitted);

        if (TrainingTypeEnum.transcoding_training.equals(trainingType)) {
            if (driverInfoHelper.hasDriverAvailableCertificate(userId)) {
                trainingOrganizationList = trainingOrganizationList.stream()
                        .filter(r -> TrainingStatusEnum.enable.name().equals(r.getTrainingStatus())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(trainingOrganizationList)) {
                    resVO.setTrainingOrganizationId(trainingOrganizationList.get(0).getId());
                    resVO.setStatus(CurrentUserOrganizationEnum.incomplete);
                    return resVO;
                }
            } else {
                return resVO;
            }
        }
        List<Long> trainingOrganizationIdList = trainingOrganizationList.stream().map(TrainingOrganization::getId).collect(Collectors.toList());

        if (CollectionUtil.isNotEmpty(trainingOrganizationIdList)) {
            List<TrainingOrganizationTrainer> trainingOrganizationTrainers = iTrainingOrganizationTrainerService.list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getDriverUserId, userId)
                    .in(TrainingOrganizationTrainer::getTrainingOrganizationId, trainingOrganizationIdList)
                    .orderByDesc(TrainingOrganizationTrainer::getCreateOn)
                    .last("limit 1"));

            if (CollectionUtil.isNotEmpty(trainingOrganizationTrainers)) {
                TrainingOrganizationTrainer trainingOrganizationTrainer = trainingOrganizationTrainers.get(0);
                resVO.setTrainingOrganizationId(trainingOrganizationTrainer.getTrainingOrganizationId());
                if (ExaminationStatusEnum.examined.name().equals(trainingOrganizationTrainer.getExaminationStatus())) {
                    resVO.setStatus(CurrentUserOrganizationEnum.completed);
                } else {
                    resVO.setStatus(CurrentUserOrganizationEnum.incomplete);
                }
            }
        }
        return resVO;
    }

    public void organizationSign(Long organizationId) {
        Long userId = LoginUserUtil.getUserId();

        DriverInfo driverInfo = driverInfoService.getOne(Wrappers.lambdaQuery(DriverInfo.class).eq(DriverInfo::getUserId, userId));
        if (Objects.isNull(driverInfo)){
            throw new BizException("您未参加该培训!");
        }
        TrainingOrganizationTrainer one = iTrainingOrganizationTrainerService.getOne(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, organizationId)
                .eq(TrainingOrganizationTrainer::getDriverUserId, userId)
                .eq(TrainingOrganizationTrainer::getDriverId,driverInfo.getId()));
        if (Objects.isNull(one)){
            throw new BizException("您未参加该培训!");
        }
        one.setSignTime(LocalDateTime.now());
        iTrainingOrganizationTrainerService.updateById(one);
    }

    public Boolean organizationSignCheck(Long organizationId) {
        Long userId = LoginUserUtil.getUserId();
        TrainingOrganizationTrainer one = iTrainingOrganizationTrainerService.getOne(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, organizationId)
                .eq(TrainingOrganizationTrainer::getDriverUserId, userId));

        if (Objects.isNull(one)) {
            return false;
        }
        return Objects.nonNull(one.getSignTime());
    }

    public PageVo<ResDriverTrainingListVO> driverTrainingList(ReqDriverTrainingListPO request) {
        Integer pageNumber = request.getPageNumber();
        Integer pageSize = request.getPageSize();
        Long driverId = request.getDriverId();

        Page<TrainingOrganizationTrainer> page = iTrainingOrganizationTrainerService.page(new Page<>(pageNumber, pageSize)
                , Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                        .eq(TrainingOrganizationTrainer::getDriverId, driverId));

        PageVo<ResDriverTrainingListVO> pageVo = new PageVo<>();
        Long total = page.getTotal();
        if (total > 0) {
            List<TrainingOrganizationTrainer> records = page.getRecords();
            List<Long> trainingOrganizationIdList = records.stream().map(TrainingOrganizationTrainer::getTrainingOrganizationId).distinct().collect(Collectors.toList());
            // 过滤掉培训组织不存在的数据
            List<Long> traIds = iTrainingOrganizationService.list(Wrappers.lambdaQuery(TrainingOrganization.class).select(TrainingOrganization::getId)).stream()
                    .map(TrainingOrganization::getId).collect(Collectors.toList());
            List<Long> ids = trainingOrganizationIdList.stream().filter(traIds::contains).collect(Collectors.toList());
            List<TrainingOrganizationTrainer> results = records.stream().filter(r -> ids.contains(r.getTrainingOrganizationId())).collect(Collectors.toList());
            List<TrainingOrganization> trainingOrganizationList = iTrainingOrganizationService.list(Wrappers.lambdaQuery(TrainingOrganization.class)
                    .in(TrainingOrganization::getId, ids));

            Map<Long, TrainingOrganization> trainingOrganizationMap = trainingOrganizationList.stream().collect(Collectors.toMap(TrainingOrganization::getId, Function.identity()));

            List<ResDriverTrainingListVO> resVOList = results.stream().map(r -> {
                String isQualified = r.getIsQualified();
                Long trainingOrganizationId = r.getTrainingOrganizationId();
                TrainingOrganization trainingOrganization = trainingOrganizationMap.get(trainingOrganizationId);
                if (Objects.nonNull(trainingOrganization)) {
                    ResDriverTrainingListVO resVO = new ResDriverTrainingListVO();
                    resVO.setOrganizationId(trainingOrganization.getId());
                    resVO.setTrainingTopic(trainingOrganization.getTrainingTopic());
                    resVO.setTrainingType(TrainingTypeEnum.valueOf(trainingOrganization.getTrainingType()));
                    resVO.setTrainingMode(TrainingModeEnum.valueOf(trainingOrganization.getTrainingMode()));
                    resVO.setTrainingStartTime(trainingOrganization.getTrainingStartTime());
                    resVO.setTrainingEndTime(trainingOrganization.getTrainingEndTime());
                    resVO.setAddress(trainingOrganization.getAddress());
                    resVO.setIsQualified(isQualified);
                    return resVO;
                }else {
                    return null;
                }
            }).collect(Collectors.toList());
            pageVo.setRecords(resVOList);
            pageVo.setTotal((long) resVOList.size());
        }
        return pageVo;
    }

    /**
     * 补考
     *
     * @param request
     */
    public void makeUpExam(ReqMakeUpExamPO request) {
        // 获取入参参数
        Long driverId = request.getDriverId();
        Long trainingOrganizationId = request.getTrainingOrganizationId();

        // 获取驾驶员详情
        DriverInfo driverInfo = driverInfoService.getById(driverId);
        // 获取培训组织详情
        TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(trainingOrganizationId);

        if (!TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())) {
            // 获取培训驾驶员信息 更新驾驶员培训人员信息
            TrainingOrganizationTrainer trainingOrganizationTrainer = iTrainingOrganizationTrainerService.getOne(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getDriverId, driverId)
                    .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, trainingOrganizationId));
            trainingOrganizationTrainer.setScore(null);
            trainingOrganizationTrainer.setIsQualified("");
            trainingOrganizationTrainer.setExaminationStatus(ExaminationStatusEnum.waiting_examination.name());
            iTrainingOrganizationTrainerService.saveOrUpdate(trainingOrganizationTrainer);
        }

    }

    /**
     * 获取培训组织关联的培训人员信息
     * @param request
     * @return
     */
    public PageVo<ResTrainingOrgTrainerVO> trainerList(TrainingOrgTrainersQuery request) {

        List<TrainingOrganizationTrainer> organizationTrainers;
        List<ResTrainingOrgTrainerVO> resVos = Lists.newArrayList();
        // 如果培训组织ID不为空说明是编辑培训组织,要查询出相关联的培训人员
        if (Objects.nonNull(request.getTrainingOrganizationId())) {
            organizationTrainers = trainingOrganizationTrainerService.list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, request.getTrainingOrganizationId()));
            List<Long> driverIds = organizationTrainers.stream()
                    .map(TrainingOrganizationTrainer::getDriverId)
                    .collect(Collectors.toList());
            Map<Long, DriverInfo> driverInfoMap = iDriverInfoService.listByIds(driverIds).stream()
                    .collect(Collectors.toMap(DriverInfo::getId, v -> v));
            for (TrainingOrganizationTrainer trainer : organizationTrainers) {
                ResTrainingOrgTrainerVO vo = new ResTrainingOrgTrainerVO();
                BeanUtils.copyProperties(trainer, vo);
                DriverInfo driverInfo = driverInfoMap.get(trainer.getDriverId());
                LocalDateTime now = LocalDateTime.now();
                int compare = now.compareTo(driverInfo.getInitialLicenseTime());
                // 当前时间大于驾驶证初次申领时间 -> 驾驶证过期
                if (compare > 0 || compare == 0) {
                    vo.setCertificateExpireFlag(false);
                } else {
                    vo.setCertificateExpireFlag(true);
                }
                resVos.add(vo);
            }
        }

        ReqArchivalInfoSearchPO po = new ReqArchivalInfoSearchPO();
        BeanUtils.copyProperties(request, po);

        LambdaQueryWrapper<DriverInfo> wrapper = Wrappers.lambdaQuery(DriverInfo.class);
        wrapper.like(StringUtils.isNotBlank(request.getCompanyName()), DriverInfo::getCompanyName, request.getCompanyName())
                .like(StringUtils.isNotBlank(request.getDriverName()), DriverInfo::getDriverName, request.getDriverName())
                .orderByDesc(DriverInfo::getCreateOn);
        if (request.getTrainingRegistrationType().equals(TrainingRegistrationTypeEnum.FORENSIC_REGISTRATION.name())) {
            wrapper.eq(DriverInfo::getDriverRegistrationStatus,DriverRegistrationStatusEnum.exam_training_stage.name());
            wrapper.eq(DriverInfo::getDriverRegistrationFlag,TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
        } else if (request.getTrainingRegistrationType().equals(TrainingRegistrationTypeEnum.CONTINUING_EDUCATION.name())) {
            wrapper.eq(DriverInfo::getContinuingEducationStatus,DriverRegistrationStatusEnum.exam_training_stage.name());
            wrapper.eq(DriverInfo::getContinuingEducationFlag,TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
        } else if (request.getTrainingRegistrationType().equals(TrainingRegistrationTypeEnum.REPLACEMENT_REGISTRATION.name())) {
            wrapper.eq(DriverInfo::getReplacementRegistrationStatus,DriverRegistrationStatusEnum.exam_training_stage.name());
            wrapper.eq(DriverInfo::getReplacementRegistrationFlag,TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
        }
        List<DriverInfo> driverInfos = iDriverInfoService.list(wrapper);

        for (DriverInfo driverInfo : driverInfos) {
            ResTrainingOrgTrainerVO vo = new ResTrainingOrgTrainerVO();
            BeanUtils.copyProperties(driverInfo,vo);
            vo.setDriverUserId(driverInfo.getUserId());
            vo.setDriverId(driverInfo.getId());
            LocalDateTime now = LocalDateTime.now();
            int compare = now.compareTo(driverInfo.getInitialLicenseTime());
            // 当前时间大于驾驶证初次申领时间 -> 驾驶证到期
            if (compare > 0 || compare == 0 ){
                vo.setCertificateExpireFlag(false);
            } else {
                vo.setCertificateExpireFlag(true);
            }
            resVos.add(vo);
        }

        // 获取传入的分页参数
        Integer pageNumber = request.getPageNumber();
        Integer pageSize = request.getPageSize();
        List<ResTrainingOrgTrainerVO> records = resVos.stream()
                .skip((long) (pageNumber - 1) * pageSize)
                .limit(pageSize)
                .collect(Collectors.toList());

       return PageVo.of(records,resVos.size());
    }

    public String setExamTime(ReqSetExamTimePO request) {
        Long trainingOrganizationId = request.getTrainingOrganizationId();
        List<Long> randomExamPaperIdList = request.getRandomExamPaperId();
        LocalDateTime examStartTime = request.getExamStartTime();
        LocalDateTime examEndTime = request.getExamEndTime();
        if (CollUtil.isNotEmpty(randomExamPaperIdList)) {
            Map<Long, ExamPaper> examPaperMap = iExamPaperService.list(Wrappers.lambdaQuery(ExamPaper.class).in(ExamPaper::getId, randomExamPaperIdList))
                    .stream().collect(Collectors.toMap(ExamPaper::getId, e -> e));
            List<ExamPaper> examPapers = Lists.newArrayList();
            for (Long id : randomExamPaperIdList) {
                ExamPaper examPaper = examPaperMap.get(id);
                examPaper.setExamStartTime(examStartTime);
                examPaper.setExamEndTime(examEndTime);
                examPaper.setUpdateOn(LocalDateTime.now());
                examPapers.add(examPaper);
            }
            if(iExamPaperService.updateBatchById(examPapers)) {
                // 保存考试时间到培训组织ID
                TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(trainingOrganizationId);
                trainingOrganization.setExamStartTime(examStartTime);
                trainingOrganization.setExamEndTime(examEndTime);
                trainingOrganization.setUpdateOn(LocalDateTime.now());
                iTrainingOrganizationService.updateById(trainingOrganization);
                return "设置成功!";
            }else {
                return "设置失败!";
            }
        }else {
            throw new BizException("请传入随机试卷ID!");
        }
    }

    public void exportTrainer(HttpServletResponse response, ReqTrainerExportPO req) {
        Long trainingOrganizationId = req.getTrainingOrganizationId();
        // 获取培训人员信息
        List<TrainingOrganizationTrainer> trainerList = iTrainingOrganizationTrainerService
                .list(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                        .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, trainingOrganizationId));
        List<Long> driverIdList = trainerList.stream().map(TrainingOrganizationTrainer::getDriverId).collect(Collectors.toList());
        Map<Long, DriverInfo> driverInfoMap = driverInfoService.list(Wrappers.lambdaQuery(DriverInfo.class).in(DriverInfo::getId, driverIdList))
                .stream().collect(Collectors.toMap(DriverInfo::getId, d -> d));

        List<TrainerInfoExportDTO> collect = trainerList.stream().map(r -> {
            TrainerInfoExportDTO exportDTO = new TrainerInfoExportDTO();
            Long driverId = r.getDriverId();
            DriverInfo driverInfo = driverInfoMap.get(driverId);
            if (Objects.nonNull(driverInfo)){
                exportDTO.setAddress(driverInfo.getAddress());
                exportDTO.setBirth(NdDateUtils.format(driverInfo.getBirth(),NdDateUtils.DEFAULT_DATE_FORMAT));
                exportDTO.setDriverName(driverInfo.getDriverName());
                exportDTO.setDriverLicenseNo(driverInfo.getDriverLicenseNo());
                exportDTO.setDriverLicenseType(driverInfo.getDriverLicenseType());
                Integer gender = driverInfo.getGender();
                if (Objects.nonNull(gender)) {
                    exportDTO.setGender(GenderEnum.getDescByCode(gender));
                }
                exportDTO.setHealth(driverInfo.getHealth());
                exportDTO.setIdCard(driverInfo.getIdCard());
                exportDTO.setPhoneNo(driverInfo.getPhoneNo());
                exportDTO.setIllegalAndIllegalInfo(driverInfo.getIllegalAndIllegalInfo());
                exportDTO.setNativePlace(driverInfo.getNativePlace());
                exportDTO.setNativePlaceRegionCode(driverInfo.getNativePlaceRegionCode());
                String motorcycleTypeList = driverInfo.getMotorcycleTypeList();
                String substring = motorcycleTypeList.substring(1, motorcycleTypeList.length() - 1);
                String motorcycleTypeListStr = Arrays.stream(substring.split(StrPool.COMMA)).map(VehicleTypeEnum::getDescByEnum).collect(Collectors.joining(StrPool.COMMA));
                exportDTO.setMotorcycleTypeList(motorcycleTypeListStr);
                exportDTO.setInitialLicenseTime(NdDateUtils.format(driverInfo.getInitialLicenseTime(),NdDateUtils.DEFAULT_DATE_FORMAT));
                // 获取培训组织信息
                TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(trainingOrganizationId);
                exportDTO.setTrainingTopic(trainingOrganization.getTrainingTopic());
                String trainingType = trainingOrganization.getTrainingType();
                if (StringUtils.isNotBlank(trainingType)) {
                    if (TrainingTypeEnum.forensic_registration_training.name().equals(trainingType)){
                        exportDTO.setTrainingType(DefValConstants.FORENSIC_REGISTRATION_TRAINING);
                    } else if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingType)) {
                        exportDTO.setTrainingType(DefValConstants.REPLACEMENT_REGISTRATION_TRAINING);
                    } else if (TrainingTypeEnum.continuing_education_training.name().equals(trainingType)) {
                        exportDTO.setTrainingType(DefValConstants.CONTINUING_EDUCATION_TRAINING);
                    }else if (TrainingTypeEnum.transcoding_training.name().equals(trainingType)){
                        exportDTO.setTrainingType(DefValConstants.TRANSCODING_TRAINING);
                    }
                }
                exportDTO.setTrainAddress(trainingOrganization.getAddress());
                String trainingMode = trainingOrganization.getTrainingMode();
                if (StringUtils.isNotBlank(trainingMode) && TrainingModeEnum.online.name().equals(trainingMode)){
                    exportDTO.setTrainingMode(DefValConstants.ONLINE);
                } else if (StringUtils.isNotBlank(trainingMode) && TrainingModeEnum.offline.name().equals(trainingMode)) {
                    exportDTO.setTrainingMode(DefValConstants.OFFLINE);
                }
                exportDTO.setTrainingStartTime(NdDateUtils.format(trainingOrganization.getTrainingStartTime(),NdDateUtils.DEFAULT_DATE_FORMAT));
                exportDTO.setTrainingEndTime(NdDateUtils.format(trainingOrganization.getTrainingEndTime(),NdDateUtils.DEFAULT_DATE_FORMAT));
                exportDTO.setRemark(trainingOrganization.getRemark());
                exportDTO.setExamStartTime(NdDateUtils.format(trainingOrganization.getExamStartTime(),NdDateUtils.DEFAULT_DATE_FORMAT));
                exportDTO.setExamEndTime(NdDateUtils.format(trainingOrganization.getExamEndTime(),NdDateUtils.DEFAULT_DATE_FORMAT));
                // 获取培训合格证信息
                Certificate certificate = certificateService.getOne(Wrappers.lambdaQuery(Certificate.class)
                        .eq(Certificate::getDriverId, r.getDriverId())
                        .last(DefValConstants.LIMIT_1));
                if (Objects.nonNull(certificate)) {
                    exportDTO.setCertificateNo(certificate.getCertificateNo());
                    exportDTO.setIssuedBy(certificate.getIssuedBy());
                    exportDTO.setIssueDate(NdDateUtils.format(certificate.getIssueDate(), NdDateUtils.DEFAULT_DATE_FORMAT));
                    exportDTO.setExpiryDate(NdDateUtils.format(certificate.getExpiryDate(), NdDateUtils.DEFAULT_DATE_FORMAT));
                }
            }
            return exportDTO;
        }).collect(Collectors.toList());
        String fileName = "培训人员信息列表";
        ExcelDownUtil.setFileName(fileName, response);
        // 数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), TrainerInfoExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void uploadExamResultAndGenerateCertificate(ReqUploadExamResultPO request) {

        UserInfoDetails user = LoginUserUtil.loginUserDetail();
        // 获取登录用户所在区域编码
        Long regionId = user.getRegionId();
        RegionDTO regionDto = regionsCacheHelper.getByRegionId(regionId);
        // 获取入参参数
        Long driverId = request.getDriverId();
        Long trainingOrganizationId = request.getTrainingOrganizationId();
        Integer score = request.getScore();

        // 获取驾驶员详情
        DriverInfo driverInfo = driverInfoService.getById(driverId);
        // 获取培训组织详情
        TrainingOrganization trainingOrganization = iTrainingOrganizationService.getById(trainingOrganizationId);

        String isQualified = score >= ExamPaperQualifiedScoreConstant.EXAM_PAPER_QUALIFIED_SCORE ? ExamQualifiedEnum.qualified.name() : ExamQualifiedEnum.unqualified.name();
        if (!TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())) {
            // 获取培训驾驶员信息 更新驾驶员培训人员信息
            TrainingOrganizationTrainer trainingOrganizationTrainer = iTrainingOrganizationTrainerService.getOne(Wrappers.lambdaQuery(TrainingOrganizationTrainer.class)
                    .eq(TrainingOrganizationTrainer::getDriverId, driverId)
                    .eq(TrainingOrganizationTrainer::getTrainingOrganizationId, trainingOrganizationId));
            trainingOrganizationTrainer.setScore(score);
            trainingOrganizationTrainer.setIsQualified(isQualified);
            trainingOrganizationTrainer.setExaminationStatus(ExaminationStatusEnum.examined.name());
            iTrainingOrganizationTrainerService.saveOrUpdate(trainingOrganizationTrainer);
        }

        ExamRecord examRecord = iExamRecordService.getOne(Wrappers.lambdaQuery(ExamRecord.class)
                .eq(ExamRecord::getTrainingOrganizationId, trainingOrganizationId));
        // 新建考试记录
        ExamRecordDetail examRecordDetailSaveRecord = new ExamRecordDetail();
        examRecordDetailSaveRecord.setExamRecordId(examRecord.getId());
        examRecordDetailSaveRecord.setCompanyId(driverInfo.getCompanyId());
        examRecordDetailSaveRecord.setCompanyName(driverInfo.getCompanyName());
        examRecordDetailSaveRecord.setCreateBy(LoginUserUtil.getUserId());
        examRecordDetailSaveRecord.setCreateOn(LocalDateTime.now());
        examRecordDetailSaveRecord.setUpdateOn(LocalDateTime.now());
        examRecordDetailSaveRecord.setDriverId(driverInfo.getId());
        examRecordDetailSaveRecord.setDriverUserId(driverInfo.getUserId());
        examRecordDetailSaveRecord.setDriverName(driverInfo.getDriverName());
        examRecordDetailSaveRecord.setExamResult(isQualified);
        examRecordDetailSaveRecord.setExamTime(LocalDateTime.now());
        examRecordDetailSaveRecord.setExamType(examRecord.getExamType());
        examRecordDetailSaveRecord.setScore(score);
        examRecordDetailSaveRecord.setRegionId(driverInfo.getRegionId());
        iExamRecordDetailService.save(examRecordDetailSaveRecord);

        String examType = trainingOrganization.getTrainingType();
        // 获取已经存在的所有合格证信息
        Long max;
        OptionalLong maxId = certificateService.list().stream().mapToLong(Certificate::getId).max();
        if (maxId.isPresent()){
            max = maxId.getAsLong();
        }else {
            max = 0L;
        }
        // 如果考试成绩合格，生成证书信息
        if (ExamQualifiedEnum.qualified.name().equals(isQualified)) {
            // 保存合格证信息
            Certificate saveRecord = new Certificate();
            // 生成证书编号
            String certificateNo = generateCertificateNo(regionDto,max);
            saveRecord.setCertificateNo(certificateNo);
            saveRecord.setDriverId(driverId);
            saveRecord.setDriverName(driverInfo.getDriverName());
            // 发证日期为当前时间、有效期加3年
            saveRecord.setIssueDate(LocalDateTime.now());
            saveRecord.setExpiryDate(LocalDateTime.now().plusYears(DriverConstant.YEAR_3));
            // 发证单位为所属区域散装中心
            String regionName = regionDto.getRegionName();
            saveRecord.setIssuedBy(regionName + DriverConstant.MANAGE_UNIT);
            saveRecord.setCreateOn(LocalDateTime.now());
            saveRecord.setUpdateOn(LocalDateTime.now());
            saveRecord.setCreateBy(user.getUserId());
            saveRecord.setUserId(driverInfo.getUserId());
            saveRecord.setBirth(driverInfo.getBirth());
            saveRecord.setGender(driverInfo.getGender());
            saveRecord.setNativePlace(driverInfo.getNativePlace());
            saveRecord.setHealth(driverInfo.getHealth());
            saveRecord.setPhoneNo(driverInfo.getPhoneNo());
            saveRecord.setAddress(driverInfo.getAddress());
            saveRecord.setDriverLicenseType(driverInfo.getDriverLicenseType());
            saveRecord.setDriverLicenseNo(driverInfo.getDriverLicenseNo());
            saveRecord.setRegionId(user.getRegionId());
            saveRecord.setRegionName(user.getRegionName());
            saveRecord.setCompanyId(user.getCompanyId());
            saveRecord.setCompanyName(user.getCompanyName());
            // 如果是换证培训,替换原来过期的取证培训合格证信息
            if (TrainingTypeEnum.replacement_registration_training.name().equals(examType)) {
                Certificate certificate = certificateService.getOne(Wrappers.lambdaQuery(Certificate.class)
                        .eq(Certificate::getUserId, driverInfo.getUserId()).last(DefValConstants.LIMIT_1));
                saveRecord.setId(certificate.getId());
            }
            certificateService.saveOrUpdate(saveRecord);
            // 生成电子合格证图片，并上传到OSS上


        }else {
            throw new BizException("考试成绩不满60分!");
        }

        // 如果为培训报名考试 并且考试成绩合格 更新阶段和可参加取证培训报名标志
        if (TrainingTypeEnum.forensic_registration_training.name().equals(examType)) {
            driverInfo.setDriverRegistrationStatus(DriverRegistrationStatusEnum.available.name());
            driverInfo.setDriverRegistrationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
            // 更新驾驶员分数为 100分
            DriverScoreInfo driverScoreInfo = iDriverScoreInfoService.getOne(Wrappers.lambdaQuery(DriverScoreInfo.class).eq(DriverScoreInfo::getDriverId, driverInfo.getId()));
            if (driverScoreInfo == null) {
                driverScoreInfo = new DriverScoreInfo();
                driverScoreInfo.setDriverId(driverInfo.getId());
                driverScoreInfo.setUserId(driverInfo.getUserId());
                driverScoreInfo.setCreateOn(LocalDateTime.now());
            }
            driverScoreInfo.setDriverScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            driverScoreInfo.setUpdateOn(LocalDateTime.now());
            iDriverScoreInfoService.saveOrUpdate(driverScoreInfo);

            DriverScoreRecord saveRecord = new DriverScoreRecord();
            saveRecord.setCreateOn(LocalDateTime.now());
            saveRecord.setUpdateOn(LocalDateTime.now());
            saveRecord.setDriverId(driverInfo.getId());
            saveRecord.setOperateReason("通过驾驶员报名培训并完成了考试");
            saveRecord.setOperateScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            saveRecord.setOperateType(ScoreOperateType.add.name());
            saveRecord.setUserId(driverInfo.getUserId());
            iDriverScoreRecordService.save(saveRecord);

            //初始化驾驶员星级及分数
            StarManage starManage = new StarManage();
            starManage.setCurrentScore(DriverRelationConstant.DRIVER_FULL_SCORE);
            starManage.setCurrentStar(TaskConstant.Star.FIVE_STAR);
            starManage.setRegionId(driverInfo.getRegionId());
            starManage.setCompanyId(driverInfo.getCompanyId());
            starManage.setCompanyName(driverInfo.getCompanyName());
            starManage.setDriverName(driverInfo.getDriverName());
            starManage.setDriverId(driverId);
            starManage.setCreateOn(LocalDateTime.now());
            starManage.setUpdateOn(LocalDateTime.now());
            starManageService.save(starManage);

        }
        // 如果为继续教育考试 并且考试成绩合格  更新阶段和可参加继续教育培训报名标志 加分
        if (TrainingTypeEnum.continuing_education_training.name().equals(trainingOrganization.getTrainingType())) {
            driverInfo.setContinuingEducationStatus(DriverReplacementRegistrationStatusEnum.available.name());
            driverInfo.setContinuingEducationFlag(TrainingRegistrationFlagEnum.CAN_TAKE.getCode());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
        }
        // 如果为换证培训 并且考试成绩合格 加分
        if (TrainingTypeEnum.replacement_registration_training.name().equals(trainingOrganization.getTrainingType())) {
            driverInfo.setReplacementRegistrationStatus(DriverReplacementRegistrationStatusEnum.upload_training_certificate_stage.name());
            driverInfo.setUpdateOn(LocalDateTime.now());
            driverInfoService.updateById(driverInfo);
        }
        // 如果为转码培训 并且考试成绩合格 加40分
        if (TrainingTypeEnum.transcoding_training.name().equals(trainingOrganization.getTrainingType())) {
            DriverScoreInfo driverScoreInfo = iDriverScoreInfoService.getOne(Wrappers.lambdaQuery(DriverScoreInfo.class).eq(DriverScoreInfo::getDriverId, driverInfo.getId()));
            CodingRuleBO codingRuleBehaviorScore = driverBehaviorScoreHelper.getCodingRuleBehaviorScore(CodingRuleBehaviorTypeEnum.complete_the_exam,null);
            Integer operationScore = codingRuleBehaviorScore.getOperationScore();
            if (driverScoreInfo == null) {
                driverScoreInfo = new DriverScoreInfo();
                driverScoreInfo.setDriverId(driverInfo.getId());
                driverScoreInfo.setUserId(driverInfo.getUserId());
                driverScoreInfo.setCreateOn(LocalDateTime.now());
                driverScoreInfo.setDriverScore(operationScore);
            } else {
                driverScoreInfo.setDriverScore(driverScoreInfo.getDriverScore() + operationScore);
            }
            driverScoreInfo.setUpdateOn(LocalDateTime.now());
            iDriverScoreInfoService.saveOrUpdate(driverScoreInfo);

            DriverScoreRecord saveRecord = new DriverScoreRecord();
            saveRecord.setCreateOn(LocalDateTime.now());
            saveRecord.setUpdateOn(LocalDateTime.now());
            saveRecord.setDriverId(driverInfo.getId());
            saveRecord.setOperateReason("驾驶员参加转码培训并完成了考试");
            saveRecord.setOperateScore(operationScore);
            saveRecord.setOperateType(ScoreOperateType.add.name());
            saveRecord.setUserId(driverInfo.getUserId());
            iDriverScoreRecordService.save(saveRecord);
        }
    }

    private String generateCertificateNo(RegionDTO regionDto, Long max) {
        String regionCode = regionDto.getRegionCode();
        // 获取当前时间所在年份
        LocalDateTime now = LocalDateTime.now();
        int year = now.getYear();
        // 获取当前时间所在月份
        Month month = now.getMonth();
        // 获取当前时间所在天数
        int day = now.getDayOfMonth();
        // 证书序号加一
        Long maxCurrent = max + 1;
        String serialNumber = String.format(DriverConstant.NO_5,maxCurrent);
        return regionCode + year + month + day + serialNumber;
    }
}
