package com.ruoyi.web.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.enums.UserRegStatus;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.dz.domain.DzCards;
import com.ruoyi.dz.service.IDzCardsService;
import com.ruoyi.enums.CardType;
import com.ruoyi.mxjb.domain.BBusiWishUniversityMarjorsCollege;
import com.ruoyi.mxjb.domain.BBusiWishUniversityMarjorsGroup;
import com.ruoyi.mxjb.domain.BBusiWishUniversityMarjorsPlanSubmit;
import com.ruoyi.mxjb.domain.SubmitYearsStats;
import com.ruoyi.mxjb.mapper.BBusiWishUniversityMarjorsCollegeMapper;
import com.ruoyi.mxjb.mapper.BBusiWishUniversityMarjorsGroupMapper;
import com.ruoyi.mxjb.mapper.BBusiWishUniversityMarjorsPlanSubmitMapper;
import com.ruoyi.mxjb.service.IMxjbWishScoreRateService;
import com.ruoyi.mxjb.service.impl.MxjbWishScoreRateServiceImpl;
import com.ruoyi.sy.domain.SyMajor;
import com.ruoyi.sy.service.ISyMajorService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.syzy.criteria.ZytbMarjorCriteriaNew;
import com.ruoyi.syzy.domain.*;
import com.ruoyi.syzy.dto.SubmitWishRecordDTO;
import com.ruoyi.syzy.dto.UniversitiesCondDTO;
import com.ruoyi.common.enums.NewgaokaoType;
import com.ruoyi.syzy.mapper.*;
import com.ruoyi.syzy.service.IBBusiWishLocationSubmitsService;
import com.ruoyi.syzy.utils.BatchUtil;
import com.ruoyi.web.domain.Constant;
import com.ruoyi.web.domain.ZytbDto;
import lombok.extern.slf4j.Slf4j;
import com.ruoyi.web.util.PageUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ZyService {
    private final BBusiWishUniversitySubmitMarjorsMapper busiWishUniversitySubmitMarjorsMapper;
    private final BBusiWishUniversitySubmitRecruitPlanMapper busiWishUniversitySubmitRecruitPlanMapper;
    private final BBusiWishScoreGroupsMapper busiWishScoreGroupsMapper;
    private final BBusiWishUniversitiesMapper bBusiWishUniversitiesMapper;
    private final BBusiWishRecordsMapper busiWishRecordsMapper;
    private final BBusiWishUniversitySubmitsMapper busiWishUniversitySubmitsMapper;
    private final BBusiWishUniversityMarjorsCollegeMapper busiWishUniversityMarjorsCollegeMapper;
    private final BBusiWishUniversityMarjorsPlanSubmitMapper busiWishUniversityMarjorsPlanSubmitMapper;
    private final BBusiWishUniversityMarjorsGroupMapper busiWishUniversityMarjorsGroupMapper;
    private final IBBusiWishLocationSubmitsService busiWishLocationSubmitsService;
    private final BBusiWishUniversitiesSubjectevaluateMapper busiWishUniversitiesSubjectevaluateMapper;
    private final BBusiWishUniversitiesProfessionMapper busiWishUniversitiesProfessionMapper;
    private final BBusiWishUniversitiesSubjectMapper busiWishUniversitiesSubjectMapper;
    private final ISysConfigService configService;
    @Autowired
    private IDzCardsService cardsService;
    private final IMxjbWishScoreRateService mxjbWishScoreRateService;
    private final CacheService cacheService;
    private final ISyMajorService syMajorService;
    private final MajorFeatureCache majorFeatureCache;
//    private final IBusiScoreLockService scoreLockService;
    private final ScoreRankService scoreRankService;

    private Map<String, Map<String, List<String>>> typeMajorChildrenMap;
    Integer[] PickWeight = new Integer[] {30, 0, -14}; // ZytbUniversityCriteria 0冲刺型 1稳妥型 2保守型


    public ZyService(BBusiWishUniversitySubmitMarjorsMapper busiWishUniversitySubmitMarjorsMapper, BBusiWishUniversitySubmitRecruitPlanMapper busiWishUniversitySubmitRecruitPlanMapper,
                     BBusiWishScoreGroupsMapper busiWishScoreGroupsMapper, BBusiWishUniversitiesMapper bBusiWishUniversitiesMapper, BBusiWishRecordsMapper busiWishRecordsMapper,
                     BBusiWishUniversitySubmitsMapper busiWishUniversitySubmitsMapper, BBusiWishUniversityMarjorsCollegeMapper busiWishUniversityMarjorsCollegeMapper,
                     BBusiWishUniversityMarjorsPlanSubmitMapper busiWishUniversityMarjorsPlanSubmitMapper, BBusiWishUniversityMarjorsGroupMapper busiWishUniversityMarjorsGroupMapper,
                     IBBusiWishLocationSubmitsService busiWishLocationSubmitsService, BBusiWishUniversitiesSubjectevaluateMapper busiWishUniversitiesSubjectevaluateMapper, BBusiWishUniversitiesProfessionMapper busiWishUniversitiesProfessionMapper,
                     BBusiWishUniversitiesSubjectMapper busiWishUniversitiesSubjectMapper, ISysConfigService configService, IMxjbWishScoreRateService mxjbWishScoreRateService, CacheService cacheService, ISyMajorService syMajorService, ScoreRankService scoreRankService) {
        this.busiWishUniversitySubmitMarjorsMapper = busiWishUniversitySubmitMarjorsMapper;
        this.busiWishUniversitySubmitRecruitPlanMapper = busiWishUniversitySubmitRecruitPlanMapper;
        this.busiWishScoreGroupsMapper = busiWishScoreGroupsMapper;
        this.bBusiWishUniversitiesMapper = bBusiWishUniversitiesMapper;
        this.busiWishRecordsMapper = busiWishRecordsMapper;
        this.busiWishUniversitySubmitsMapper = busiWishUniversitySubmitsMapper;
        this.busiWishUniversityMarjorsCollegeMapper = busiWishUniversityMarjorsCollegeMapper;
        this.busiWishUniversityMarjorsPlanSubmitMapper = busiWishUniversityMarjorsPlanSubmitMapper;
        this.busiWishUniversityMarjorsGroupMapper = busiWishUniversityMarjorsGroupMapper;
        this.busiWishLocationSubmitsService = busiWishLocationSubmitsService;
        this.busiWishUniversitiesSubjectevaluateMapper = busiWishUniversitiesSubjectevaluateMapper;
        this.busiWishUniversitiesProfessionMapper = busiWishUniversitiesProfessionMapper;
        this.busiWishUniversitiesSubjectMapper = busiWishUniversitiesSubjectMapper;
        this.configService = configService;
        this.mxjbWishScoreRateService = mxjbWishScoreRateService;
        this.cacheService = cacheService;
        this.syMajorService = syMajorService;
//        this.scoreLockService = scoreLockService;
        this.scoreRankService = scoreRankService;
        this.majorFeatureCache = new MajorFeatureCache(busiWishUniversitiesSubjectevaluateMapper, busiWishUniversitiesProfessionMapper, busiWishUniversitiesSubjectMapper);
        this.typeMajorChildrenMap = buildMajorChildrenMap();
    }

    /**
     * 06-20后才算新的一年
     * @return
     */
    public Integer getYear() {
        String createtimeArrayStr = configService.selectConfigByKey("voluntary.newyear.createtime");
        if(!createtimeArrayStr.contains("{")){
            createtimeArrayStr= "[{\"location\":\"湖南\",\"time\":\"06-28\"},{\"location\":\"湖北\",\"time\":\"06-28\"},{\"location\":\"江西\",\"time\":\"01-24\"},{\"location\":\"安徽\",\"time\":\"01-28\"},{\"location\":\"广东\",\"time\":\"06-28\"},{\"location\":\"广西\",\"time\":\"06-28\"},{\"location\":\"河南\",\"time\":\"06-28\"},{\"location\":\"四川\",\"time\":\"06-28\"}]";
        }
        String voluntaryNewyearCreatetime= StringUtils.EMPTY;
        if(StringUtils.isNotEmpty(createtimeArrayStr)){
            JSONArray array = JSONArray.parseArray(createtimeArrayStr);
            for (Object item : array) {
                JSONObject jsonObject = (JSONObject) item;
                if (jsonObject.getString("location").trim().contains(SecurityUtils.getLoginUser().getUser().getLocation().trim())) {
                    voluntaryNewyearCreatetime = jsonObject.getString("time");
                    break;
                }
            }
        }

        Date registerTime = DateUtils.parseDate(DateUtils.getCurrentYear()+"-"+(StringUtils.isBlank(voluntaryNewyearCreatetime)?("06-22"):voluntaryNewyearCreatetime));

        Calendar cal = Calendar.getInstance();
        Integer year = cal.get(Calendar.YEAR);
        if(new Date().before(registerTime)){
            year= year -1 ;
        }
        return year;
    }

    /**
     * 查询最近2年的最新录取数据，以决定是用当年还是前一年匹配
     * @param location
     * @param type
     * @param level
     * @param currYear
     * @return
     */
    public Integer getSubmitYear(String location, String type, String level, Integer currYear) {
        //20231208 莫：院校投档线列表中status不限制，模拟志愿要限制status>0，解决院校线出了2023年的但是模拟志愿中只使用2022年的
        List<String> years = busiWishUniversitySubmitsMapper.yearsStatus(location, type, level);
        for (String y : years) { // years是按年度倒序的，取第一个最近年度的
            Integer currSubmitYear = Integer.parseInt(y);
            if (currSubmitYear <= currYear) {
                return currSubmitYear < currYear - 1 ? currYear : currSubmitYear;
            }
        }
        return currYear;
    }

    private Map<String, Map<Integer, String>> YearModeCourseMap = Maps.newHashMap();
    public void clearYearCourseMap() {
        YearModeCourseMap.clear();
    }
    public Map<Integer, String> getYearCourseMap(Integer currYear, String mode) {
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        Integer liberalScience = NewgaokaoType.isNewgaokao33Province(location) ? 2 : (("物理".equals(mode) || "理科".equals(mode)) ? 1 : 0);
        String key = location + liberalScience + currYear + mode;
        Map<Integer, String> yearCourseMap = YearModeCourseMap.get(key);
        if (null == yearCourseMap) {
            synchronized (YearModeCourseMap) {
                yearCourseMap = YearModeCourseMap.get(key);
                if (null == yearCourseMap) {
                    yearCourseMap = getYearCourseMapInner(location, liberalScience, currYear, mode);
                    YearModeCourseMap.put(key, yearCourseMap);
                }
            }
        }
        return yearCourseMap;
    }

    public Map<Integer, String> getYearCourseMapInner(String location, Integer liberalScience, Integer currYear, String mode) {
        Map cond = Maps.newHashMap();
        cond.put("location", location);
        cond.put("year", currYear - 3);
        cond.put("currYear", currYear);
        cond.put("liberalScience", liberalScience);
        Map<Integer, String> yearTypeMap = busiWishUniversitySubmitMarjorsMapper.selectYearTypes(cond).stream().collect(Collectors.toMap(t -> Integer.parseInt(t.getYear()), t -> t.getType()));
        String last = mode;
        Map<Integer, String> yearCourseMap = Maps.newHashMap();
        for (int i = currYear; i >= currYear - 3; i--) {
            String type = yearTypeMap.get(i);
            if (null != type) {
                last = type;
            }
            yearCourseMap.put(i, last);
        }
        return yearCourseMap;
    }

    public List<String> getVoluntaryHeaders(Boolean isMock,Integer currYear, String mode) {
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        currYear = getYear(); // || location.equals("江西") || location.equals("湖北") || location.equals("湖南")
        Integer currYearTitle=currYear;
        if(isMock){
            //同年时才升年
            if(currYear<Calendar.getInstance().get(Calendar.YEAR)){
                currYearTitle=currYear+1;
            }
        }
        Map<Integer, String> yearCourseMap = getYearCourseMap(currYear, mode);

        List<String> headerNameList = Lists.newArrayList("推荐类型", "院校", currYearTitle + "年招生计划", "投档线年份","最低分","位次","录取人数");
        String forceLocation = configService.selectConfigByKey("voluntary.force.locations");
        if (null != forceLocation && forceLocation.contains(location)||isMock) {
            currYear = 2023;
        }
        for (int i = currYear; i > currYear - 3; i--) {
            headerNameList.add(i + "&" + yearCourseMap.get(i));
        }
        headerNameList.add("填报");
        return headerNameList;
    }

    private Integer getScoreOfLastYear(String location, Integer currYear, String mode, Integer score) {
        BBusiWishScoreGroups currScoreRange = scoreRankService.getScoreRangeByScore(location, currYear, mode, currYear, score);
        Integer eqScore = null;
        if(null != currScoreRange) {
            Integer eqYear = currYear -1;
            BBusiWishScoreGroups eqScoreRange = scoreRankService.getScoreRangeByBatch(location, currYear, currScoreRange.getMode(), eqYear, currScoreRange.getType());
            if (null != eqScoreRange) {
                Integer eqRank = scoreRankService.getEqRank(currScoreRange, eqScoreRange, location, currYear, currYear, score, null, eqYear);
                eqScore = scoreRankService.getScore(location, eqYear, eqScoreRange.getMode(), eqRank);
            }
        }
        return eqScore;
    }
    private Integer getScoreOfLastYear2(String location, Integer currYear, String mode, Integer score) {
        Map cond = Maps.newHashMap();
        cond.put("location", location);
        cond.put("year", currYear);
        cond.put("mode", mode);
        cond.put("score", score);
        List<BBusiWishScoreGroups> groupList = busiWishScoreGroupsMapper.selectBusiWishScoreGroupsByScore(cond);
        if(CollectionUtils.isNotEmpty(groupList)) {
            BBusiWishScoreGroups currScoreGroup = groupList.get(0);
            cond.remove("score");
            Integer[] ranks = getMappingRank2(new Integer[] {currScoreGroup.getLowestRank()}, location, currYear, currYear - 1);
            cond.put("year", currYear - 1);
            cond.put("rank", ranks[0]);
            groupList = busiWishScoreGroupsMapper.selectBusiWishScoreGroupsByScore(cond);
        } else {
            cond.remove("year");
            groupList = busiWishScoreGroupsMapper.selectBusiWishScoreGroupsByScore(cond);
        }
        if (CollectionUtils.isEmpty(groupList)) {
            return score;
        }
        BBusiWishScoreGroups lastScoreGroup = groupList.get(0);
        return lastScoreGroup.getScore().intValue();
    }

    private Integer getLowestRank(Map cond, Integer score) {
        cond.put("score", score);
        List<BBusiWishScoreGroups> groupList = busiWishScoreGroupsMapper.selectBusiWishScoreGroupsByScore(cond);
        if (CollectionUtils.isEmpty(groupList)) {
            return null;
        }
        Integer lowestRank = groupList.get(0).getLowestRank();
        return lowestRank;
    }

    private String getMajorName(String majorName, String majorDirection) {
        if(StringUtils.isNotBlank(majorDirection)) {
            if (majorDirection.contains("国家专项")) {
                return majorName + "(国家专项)";
            } else if (majorDirection.contains("地方专项")) {
                return majorName + "(地方专项)";
            }
        }
        return majorName;
    }
    private boolean getScoreLock(String location) {
        boolean scoreLock = false;
        return false;
        /** TODO莫
        BusiScoreLock scoreLockCond = new BusiScoreLock();
        scoreLockCond.setIsValid(BoolValues.yes.getValue());
        scoreLockCond.setProvinceName(location);
        List<BusiScoreLock> busiScoreLockList = scoreLockService.selectBusiScoreLockList(scoreLockCond);
        if (CollectionUtils.isEmpty(busiScoreLockList)) {
            return scoreLock;
        }
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date start = sdf.parse(Calendar.getInstance().get(Calendar.YEAR) + "-" + busiScoreLockList.get(0).getStartDate());
            Date end = sdf.parse(Calendar.getInstance().get(Calendar.YEAR) + "-" + busiScoreLockList.get(0).getEndDate());
            Date now = new Date();
            scoreLock = !(now.before(start) || now.after(end));
        } catch (ParseException e) {
            log.error("ScoreLock", e);
        }
        return scoreLock;**/
    }
    private List<String> split2list(String value) {
        return StringUtils.isNotBlank(value) ? Arrays.asList(value.split(",")) : null;
    }

    public Map appendScoreCond(ZytbMarjorCriteriaNew query, Integer currYear, String location, String mode) {
        // 查询最低分数
        Integer lowestScore = null, highestScore = null;
        // [Level=冲的最大值, 冲的最低值, 稳的最低值, 保的最低值, Rate>=99的最高值, Rate<=1的最小值]
        Integer[] scoreRateRanges = mxjbWishScoreRateService.getWishScoreRatioRanges(currYear, query.getScore());

        // MaxScore + 1 是为了让lowRank匹配时包含输入的分数, 现在都是整数，可以+1分变包含
        Integer[] queryScoreRanges = null != query.getMinScore() && null != query.getMaxScore() ? new Integer[] { query.getMaxScore() + 1, query.getMinScore()} : null;
        boolean hasScoreRangeBegin = null != queryScoreRanges && null != queryScoreRanges[1];
        boolean hasScoreRangeEnd = null != queryScoreRanges && null != queryScoreRanges[0];

        Integer pickType = query.getPickType();
        if (null == pickType) {
            lowestScore = null; // hasScoreRangeBegin ? queryScoreRanges[1] : scoreRateRanges[3];
            highestScore = null; // hasScoreRangeEnd ? queryScoreRanges[0] : scoreRateRanges[0];
        } else {
            switch (pickType) {
                case 0: // 冲
                    lowestScore = hasScoreRangeBegin ? Math.max(queryScoreRanges[1], scoreRateRanges[1]) : scoreRateRanges[1];
                    // highestScore = hasScoreRangeEnd ? Math.min(queryScoreRanges[0], scoreRateRanges[0]) : scoreRateRanges[0];
                    break;
                case 1: // 稳
                    lowestScore = hasScoreRangeBegin ? Math.max(queryScoreRanges[1], scoreRateRanges[2]) : scoreRateRanges[2];
                    highestScore = hasScoreRangeEnd ? Math.min(queryScoreRanges[0], scoreRateRanges[1]) : scoreRateRanges[1];
                    break;
                default: // 保
                    // lowestScore = hasScoreRangeBegin ? Math.max(queryScoreRanges[1], scoreRateRanges[3]) : scoreRateRanges[3];
                    highestScore = hasScoreRangeEnd ? Math.min(queryScoreRanges[0], scoreRateRanges[2]) : scoreRateRanges[2];
                    break;
            }
        }
        UniversitiesCondDTO uc = query.getUniversity();
        //处理level（本科/专科）
        if (null == uc) {
            uc = new UniversitiesCondDTO();
            query.setUniversity(uc);
        }
        Map cond = Maps.newHashMap();
        if (StringUtils.isBlank(uc.getName())) {
            cond.put("lowestScore", lowestScore);
            cond.put("highestScore", highestScore);
        }
        /*if(null != scoreRateRanges) {   // TODO 引入的 99%, 1%时，是想排序时按一个级别，后面已不使用，暂注释，稍后可去掉
            cond.put("minScore", scoreRateRanges[4]);
            cond.put("maxScore", scoreRateRanges[5]);
        } else {
            cond.put("minScore", query.getScore() - 14);
            cond.put("maxScore", query.getScore() + 15);
        }*/
        return cond;
    }

    public Map appendSeatCond(ZytbMarjorCriteriaNew query, Integer currPlanYear, Integer currSubmitYear, String location, String mode) {
        // 查询最低名次 23位次/23年全局最低位次 * 22年全局最低位次=22位次
        Integer lowestRank = null, highestRank = null;
        // +30 -15 -60 600分 历史 => 冲：(600-630] 稳：(585-600] 保：(414,585]
        Integer score = query.getScore().intValue();
        Integer[] segments = getMappingRank(getPickTypeSegments(query.getScore(), query.getBatchMinScore(), location, currPlanYear, mode), location, currPlanYear, mode, score, currSubmitYear);
        // MaxScore + 1 是为了让lowRank匹配时包含输入的分数, 现在都是整数，可以+1分变包含
        Integer[] scoreRanges = null != query.getMinScore() && null != query.getMaxScore() ? getScoreRange(query.getMaxScore() + 1, query.getMinScore(), location, currPlanYear, mode) : null;
        scoreRanges = getMappingRank(scoreRanges, location, currPlanYear, mode, score, currSubmitYear);
        boolean hasScoreRangeBegin = null != scoreRanges && null != scoreRanges[0];
        boolean hasScoreRangeEnd = null != scoreRanges && null != scoreRanges[1];

        Integer pickType = query.getPickType();
        if (null == pickType) {
            // lowestRank = hasScoreRangeBegin ? scoreRanges[0] : segments[0];
            // if (null != segments[3]) {
            //     highestRank = hasScoreRangeEnd ? scoreRanges[1] : segments[3];
            // }
        } else {
            switch (pickType) {
                case 0:
                    // lowestRank = hasScoreRangeBegin ? Math.max(scoreRanges[0], segments[0]) : segments[0];
                    highestRank = hasScoreRangeEnd ? Math.min(scoreRanges[1], segments[1]) : segments[1];
                    break;
                case 1:
                    lowestRank = hasScoreRangeBegin ? Math.max(scoreRanges[0], segments[1]) : segments[1];
                    if(null != segments[2]) {
                        highestRank = hasScoreRangeEnd ? Math.min(scoreRanges[1], segments[2]) : segments[2];
                    }
                    break;
                default: // 保
                    lowestRank = hasScoreRangeBegin ? Math.max(scoreRanges[0], segments[2]) : segments[2];
                    // if(null != segments[3]) {
                    //     highestRank = hasScoreRangeEnd ? Math.min(scoreRanges[1], segments[3]) : segments[3];
                    // }
                    break;
            }
        }
        UniversitiesCondDTO uc = query.getUniversity();
        //处理level（本科/专科）
        if (null == uc) {
            uc = new UniversitiesCondDTO();
            query.setUniversity(uc);
        }
        Map cond = Maps.newHashMap();
        if (StringUtils.isBlank(uc.getName())) {
            cond.put("lowestRank", lowestRank);
            cond.put("highestRank", highestRank);
        }
        // [Level=冲的最大值, 冲的最低值, 稳的最低值, 保的最低值, Rate>=99的最高值, Rate<=1的最小值]
        /*Integer[] scoreRateRanges = mxjbWishScoreRateService.getWishScoreRatioRanges(currYear, query.getScore());
        if(null != scoreRateRanges) { // TODO 引入的 99%, 1%时，是想排序时按一个级别，后面已不使用，暂注释，稍后可去掉
            cond.put("minScore", scoreRateRanges[4]);
            cond.put("maxScore", scoreRateRanges[5]);
        } else {
            cond.put("minScore", query.getScore() - 14);
            cond.put("maxScore", query.getScore() + 15);
        }*/
        return cond;
    }

    private Integer getBatchline(String location, Integer year, Integer type, String examMajor) {
        BBusiWishLocationSubmits lsCond = new BBusiWishLocationSubmits();
        lsCond.setLocation(location);
        lsCond.setYear(String.valueOf(year));
        lsCond.setType(type);
        lsCond.setCourse(examMajor);
        List<BBusiWishLocationSubmits> list = busiWishLocationSubmitsService.selectBBusiWishLocationSubmitsList(lsCond);
        return CollectionUtils.isNotEmpty(list) ? list.get(0).getScore() : null;
    }

    public List<ZytbDto.ZytbVolunteerRes> getRecommendVoluntary(ZytbMarjorCriteriaNew query, Integer pageNo, Integer pageSize) {
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        // 计划年度
        Integer currYear = getYear();
        // 录取年度
        Integer currSubmitYear = getSubmitYear(location, null, query.getBatchName(), currYear);
        Integer pickType = query.getPickType();

        List<ZytbDto.ZytbVolunteerRes> volunteerResList = Lists.newArrayList();

        Map cond = currYear.equals(currSubmitYear) ? appendScoreCond(query, currYear, location, query.getMode()) : appendSeatCond(query, currYear, currSubmitYear, location, query.getMode());
        if (null == cond) {
            return volunteerResList;
        }
        // 查询去年录取的高于排名的学校专业
        cond.put("location", location);
        cond.put("planYear", currYear);
        cond.put("year", currSubmitYear);
        cond.put("level", query.getBatchName().replaceAll("批", "")); // 本科，专科
        cond.put("liberalScience", NumberUtils.toInt(query.getMode(), 0));
        String batchLevel = BatchUtil.getBatchById(query.getBatch());
        cond.put("majors", CollectionUtils.isNotEmpty(query.getMajors()) ? expandMarjors(batchLevel, query.getMajors()) : null);
            cond.put("sinoForeign", query.getSinoforeign());
        cond.put("specialProjectNation", query.getSpecialProjectNation());
        cond.put("specialProjectLocal", query.getSpecialProjectLocal());
        if (null != query.getCollect()) {
            cond.put("collect", query.getCollect()); //  ? "有征集" : ""
        }
        UniversitiesCondDTO uc = query.getUniversity();
        // uc.setLevel(BatchUtil.getBatchById(query.getBatch()));
        cond.put("uName", uc.getName());
        cond.put("uCode", uc.getCode());
        cond.put("uManagerType", uc.getManagerType());
        cond.put("uLocations", split2list(uc.getLocation()));
        cond.put("uFeatureses", split2list(uc.getFeatures()));
        cond.put("uNatureTypeCNs", split2list(uc.getNatureTypeCN()));
        //20240625莫，此处type对u.`type` like优化为u.`type` in(enum)会提升性能
        cond.put("uTypes", split2list(uc.getType()));
//        List<String> uTypes = bBusiWishUniversitiesMapper.listDistinctAttributes("type");
//        List<String> uTypeQueryList=new ArrayList<>();
//        if(StringUtils.isNotBlank(uc.getType())){
//            for (String dbUtype : uTypes) {
//                for (String queryUtype : split2list(uc.getType())) {
//                    if (StringUtils.isNotBlank(queryUtype) && (dbUtype.contains(queryUtype) || queryUtype.contains(dbUtype))) {
//                        uTypeQueryList.add(dbUtype);
//                    }
//                }
//            }
//        }
//        uTypeQueryList=uTypeQueryList.stream().filter(t->t != null && !t.isEmpty()).distinct().collect(Collectors.toList());
//        if (CollectionUtils.isNotEmpty(uTypeQueryList)){
//            cond.put("uTypes", uTypeQueryList);
//        }

        cond.put("uLevels", split2list(uc.getLevel()));
        cond.put("sameYear", currSubmitYear.equals(currYear));
        // cond.put("newSpecialGroup", "安徽".contains(location)); // 安徽 江西 2024 不在使用uCode
        PageHelper.startPage(pageNo, pageSize);
        List<BBusiWishUniversitySubmitRecruitPlan> submitPlanList = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitPlanGroups(cond);
        if(CollectionUtils.isEmpty(submitPlanList)) {
            return volunteerResList;
        }
        Set<Long> universityIdSet = submitPlanList.stream().map(t -> t.getUniversityId()).collect(Collectors.toSet());

        Map uCond = Maps.newHashMap();
        uCond.put("ids", universityIdSet);
        Map<Long, BBusiWishUniversities> wishUniversityMap = universityIdSet.size() > 0 ? bBusiWishUniversitiesMapper.selectBBusiWishUniversitiesListSimpleByIds(uCond).stream().collect(Collectors.toMap(BBusiWishUniversities::getId, Function.identity())) : Maps.newHashMap();

//        Map<Long, BBusiWishUniversitySubmitMarjors> universityMarjorMap = Maps.newHashMap();
//        Set<Long> matchedUniversityIdSet = submitPlanList.stream().filter(
//                t -> null != t.getUniversityId() && t.getStats() == null).map(t -> Long.parseLong(t.getUniversityId())).collect(Collectors.toSet());
//        if (matchedUniversityIdSet.size() > 0) {
//            cond.clear();
//            cond.put("location", location);
//            cond.put("year", currYear - 1);
//            cond.put("universityIds", matchedUniversityIdSet);
//            cond.put("level", query.getBatchName().replaceAll("批", "")); // 本科，专科
//            cond.put("liberalScience", NewgaokaoType.isNewgaokao33Province(location) ? 2 : (("物理".equals(subjects[0]) || "理科".equals(subjects[0])) ? 1 : 0));
//            for(BBusiWishUniversitySubmits us : busiWishUniversitySubmitsMapper.selectBBusiWishUniversitySubmitsByCond(cond)) {
//                BBusiWishUniversitySubmitMarjors submitMarjors = new BBusiWishUniversitySubmitMarjors();
//                submitMarjors.setScore(us.getScore());
//                submitMarjors.setSeat(us.getSeat());
//                submitMarjors.setYear(us.getYear());
//                submitMarjors.setType(us.getType());
//                universityMarjorMap.put(Long.parseLong(us.getUniversityId()), submitMarjors);
//            }
//        }
        Integer baseYearScore = query.getScore();
        if(!currSubmitYear.equals(currYear) && null == (baseYearScore = getScoreOfLastYear(location, currYear, query.getMode(), baseYearScore))) {
            throw new RuntimeException("分数不在范围");
        }
        Integer historyIndex = currSubmitYear.equals(currYear) ? 0 : 1;

        boolean scoreLock = getScoreLock(location);
        boolean showInheritScore = Boolean.valueOf(configService.selectConfigByKey("voluntary.showInheritScore"));

        String planYear = String.valueOf(currYear);
        // Map<Integer, String> yearCourseMap = getYearCourseMap(currYear, mode);
        for(BBusiWishUniversitySubmitRecruitPlan submitPlan : submitPlanList) {
            ZytbDto.ZytbVolunteerRes res = new ZytbDto.ZytbVolunteerRes();
            BBusiWishUniversities wishUniversities = wishUniversityMap.get(submitPlan.getUniversityId());
            if (null != wishUniversities && StringUtils.isNotBlank(submitPlan.getUniversityName())
                    && 0 != StringUtils.compare(submitPlan.getUniversityName(), wishUniversities.getName())) {
                BBusiWishUniversities newWishUniversities = new BBusiWishUniversities();
                BeanUtils.copyProperties(wishUniversities, newWishUniversities);
                newWishUniversities.setName(submitPlan.getUniversityName());
                res.setUniversity(newWishUniversities);
            } else {
                res.setUniversity(wishUniversities);
            }
            BBusiWishUniversitySubmitRecruitPlan plan = new BBusiWishUniversitySubmitRecruitPlan();
            plan.setPlanCount(submitPlan.getPlanCount());
            plan.setMajorCount(submitPlan.getMajorCount());
            plan.setCollegeCode(submitPlan.getCollegeCode());
            plan.setUniversityId(submitPlan.getUniversityId());
            plan.setType(submitPlan.getType());
            plan.setCourse(submitPlan.getCourse());
            plan.setYear(planYear);
            plan.setGroupsName(submitPlan.getGroupsName());
            res.setRecruitPlan(plan);
            res.setJCode(submitPlan.getUniversityCode());

            BBusiWishUniversitySubmitMarjors[] histories = new BBusiWishUniversitySubmitMarjors[4];
            if (null != submitPlan.getStats() && ArrayUtils.isNotEmpty(submitPlan.getStats().getStats())) {
                SubmitYearsStats.YearStat[] stats = submitPlan.getStats().getStats();
                for (int i = 0; i < Math.min(4, stats.length); i++) {
                    if (stats[i] != null) {
                        BBusiWishUniversitySubmitMarjors submitMarjor = new BBusiWishUniversitySubmitMarjors();
                        submitMarjor.setNumReal(stats[i].getNumReal());
                        submitMarjor.setScore(scoreLock ? stats[i].getScore() : stats[i].getCorrScore());
                        submitMarjor.setSeat(scoreLock ? stats[i].getSeat() : stats[i].getCorrSeat());
                        submitMarjor.setInheritScore(showInheritScore && null != stats[i].getScore() && null != submitMarjor.getScore() && stats[i].getScore() == 0 && submitMarjor.getScore() > 0);
                        submitMarjor.setCollect(stats[i].getCollect());
                        submitMarjor.setCollectDesc(stats[i].getCollectDesc());
                        submitMarjor.setSinoForeign(stats[i].getSinoForeign());
                        submitMarjor.setSinoForeignDesc(stats[i].getSinoForeignDesc());
                        submitMarjor.setType(submitPlan.getType());
                        Integer year = stats[i].getYear();
                        submitMarjor.setYear(String.valueOf(year));
                        // submitMarjor.setType(yearCourseMap.get(year));
                        histories[i] = submitMarjor;
                    }
                }
            }
            BBusiWishUniversitySubmitMarjors empty = new BBusiWishUniversitySubmitMarjors();
            for (int i = 0; i < histories.length; i++) {
                if (null == histories[i]) {
                    histories[i] = empty;
                }
            }
            Integer batchLine = getBatchline(location, currYear, query.getBatchName().contains("本科") ? 1 : 5, getExamMajor());
            MxjbWishScoreRateServiceImpl.ScoreRatioResult scoreRatioResult = getScoreRatioResult(histories, currYear, currSubmitYear, baseYearScore, location, query.getMode(), query.getScore().intValue(), batchLine);
            if(null != scoreRatioResult) {
                res.setPickType(scoreRatioResult.getEnrollLevel());
                res.setEnrollOver(scoreRatioResult.getOverflow());
                res.setEnrollRatio(String.valueOf(scoreRatioResult.getRatio()));
                res.setEnrollRatioText(scoreRatioResult.getRiskLevel());
            } else {
                res.setPickType(pickType);
                res.setEnrollRatio("");
                res.setEnrollRatioText("");
            }
//            if (null == histories[0]) {
//                histories[0] = universityMarjorMap.get(submitPlan.getUniversityId());
//            }
            if (histories != null && histories.length > 1) {
                histories = historyIndex > 0 ? ArrayUtils.subarray(histories, 1, histories.length) : ArrayUtils.subarray(histories, 0, histories.length - 1);
            }
            res.setHistories(histories);
            res.setHistory(res.getHistories()[0]);
            res.setSpecialProject(submitPlan.getSpecialProject());
            volunteerResList.add(res);
        }
        /*Collections.sort(volunteerResList, new Comparator<ZytbDto.ZytbVolunteerRes>() {
            @Override
            public int compare(ZytbDto.ZytbVolunteerRes o1, ZytbDto.ZytbVolunteerRes o2) {
                Integer v1 = StringUtils.isNotBlank(o1.getEnrollRatio()) ? Integer.parseInt(o1.getEnrollRatio()) : 0;
                Integer v2 = StringUtils.isNotBlank(o2.getEnrollRatio()) ? Integer.parseInt(o2.getEnrollRatio()) : 0;
                return v1.compareTo(v2);
            }
        });*/
        return PageUtil.getDtoListWithPageable(submitPlanList, volunteerResList);
    }


    private MxjbWishScoreRateServiceImpl.ScoreRatioResult getScoreRatioResult(BBusiWishUniversitySubmitMarjors[] histories, Integer currYear, Integer currSubmitYear, Integer baseYearScore, String location, String mode, Integer score, Integer batchLine
    ) {
        if(null != batchLine && baseYearScore < batchLine) {
            return null;
        }
        Integer historyYear;
        for (int i = 0; i < histories.length; i++) {
            if (null != histories[i].getYear() && null != histories[i].getScore() && histories[i].getScore() > 0 && (historyYear = Integer.parseInt(histories[i].getYear())) <= currSubmitYear) {
                Integer historyScore = histories[i].getScore().intValue();
                if(!currYear.equals(currSubmitYear) || !historyYear.equals(currSubmitYear)) {
                    BBusiWishScoreGroups currScoreRange = scoreRankService.getScoreRangeByScore(location, getYear(), mode, currYear, score);
                    if(null != currScoreRange) {
                        BBusiWishScoreGroups submitScoreRange = scoreRankService.getScoreRangeByBatch(location, currYear, currScoreRange.getMode(), currSubmitYear, currScoreRange.getType());
                        BBusiWishScoreGroups historyScoreRange = scoreRankService.getScoreRangeByBatch(location, currYear, currScoreRange.getMode(), historyYear, currScoreRange.getType());
                        Integer eqRank = scoreRankService.getEqRank(historyScoreRange, submitScoreRange, location, currYear, historyYear, historyScore, null, currSubmitYear);
                        historyScore = scoreRankService.getScore(location, currSubmitYear, submitScoreRange.getMode(), eqRank);
                    }
                }
                if(null == historyScore) {
                    return null;
                }
                return mxjbWishScoreRateService.getWishScoreRatioResult(currYear, baseYearScore, historyScore);
            }
        }
        return null;
    }

    public List<String> expandMarjors(String type, List<String> marjors) {
        Map<String, List<String>> majorChildrenMap = typeMajorChildrenMap.get(type);
        if (null == majorChildrenMap) {
            return marjors;
        }
        List<String> marjorsList = Lists.newArrayList();
        for (String marjor : marjors) {
            marjorsList.add(marjor);
            List<String> children = majorChildrenMap.get(marjor);
            if (null != children) {
                marjorsList.addAll(children);
            }
        }
        return marjorsList;
    }

    private Map<String, Map<String, List<String>>> buildMajorChildrenMap() {
        Map<String, String> majorCodeNameMap = Maps.newHashMap();
        List<SyMajor> majorList = Lists.newArrayList();
        syMajorService.selectSyMajorList(new SyMajor()).stream().forEach(m -> {
            if (m.getLevel() == 2) {
                majorCodeNameMap.put(m.getType() + m.getCode(), m.getName());
            } else if (m.getLevel() == 3) {
                majorList.add(m);
            }
        });
        Map<String, Map<String, List<String>>> typeMajorChildrenMap = Maps.newHashMap();
        majorList.stream().forEach(m -> {
            String parent = majorCodeNameMap.get(m.getType() + m.getParentCode());
            if (StringUtils.isNotBlank(parent)) {
                Map<String, List<String>> majorChildrenMap = typeMajorChildrenMap.get(m.getType());
                if (null == majorChildrenMap) {
                    majorChildrenMap = Maps.newHashMap();
                    typeMajorChildrenMap.put(m.getType(), majorChildrenMap);
                }
                List<String> childrenList = majorChildrenMap.get(parent);
                if (null == childrenList) {
                    childrenList = Lists.newArrayList();
                    majorChildrenMap.put(parent, childrenList);
                }
                childrenList.add(m.getName());
            }
        });
        return typeMajorChildrenMap;
    }

    public List<ZytbDto.ZytbVolunteerRes> filterResEmptyHistory(List<ZytbDto.ZytbVolunteerRes> resList) {
        for (ZytbDto.ZytbVolunteerRes res : resList) {
            BBusiWishUniversitySubmitMarjors[] histories = res.getHistories();
            for (int i = 0; i < histories.length; i++) {
                if (null != histories[i] && histories[i].getYear() == null) {
                    histories[i] = null;
                }
            }
        }
        return resList;
    }

    public List<ZytbDto.ZytbVoluntaryMarjorDetail> filterEmptyHistory(List<ZytbDto.ZytbVoluntaryMarjorDetail> detailList) {
        for (ZytbDto.ZytbVoluntaryMarjorDetail d : detailList) {
            BBusiWishUniversitySubmitMarjors[] histories = d.getHistories();
            for (int i = 0; i < histories.length; i++) {
                if (null != histories[i] && histories[i].getYear() == null) {
                    histories[i] = null;
                }
            }
        }
        return detailList;
    }

    public List<ZytbDto.ZytbVoluntaryMarjorDetail> getVoluntaryMarjors(ZytbDto.ZytbVoluntaryMarjorCond query) {
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        if (null == query.getYear()) {
            query.setYear(getYear());
        }
        query.setMode(getExamMajor());
        Integer currYear = query.getYear();
        Integer currSubmitYear = getSubmitYear(location, null, query.getBatchName(), currYear);
        Map cond = Maps.newHashMap();
        cond.put("location", location);
        cond.put("universityId", query.getUniversityId());
        cond.put("level", query.getBatchName().replaceAll("批", "")); // 本科，专科
        cond.put("liberalScience", NumberUtils.toInt(query.getMode(), 0));
        cond.put("collegeCode", query.getCollegeCode());
        cond.put("year", currYear); // 查询当前年的计划
        // cond.put("uCode", query.getJCode());
        List<BBusiWishUniversitySubmitRecruitPlan> planGroupDetailList = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitPlanGroupDetails(cond);

        Integer baseYearScore = currSubmitYear.equals(currYear) ? query.getScore() : getScoreOfLastYear(location, currYear, query.getMode(), query.getScore());
        Integer historyIndex = currSubmitYear.equals(currYear) ? 0 : 1;
//        Map<String, BBusiWishUniversitySubmitMarjors> universityMarjorMap = Maps.newHashMap();
//        Set<Long> matchedUniversityIdSet = planGroupDetailList.stream().filter(
//                t -> null != t.getUniversityId() && (t.getStats() == null || t.getStats().getStats()[0] == null)).map(t -> Long.parseLong(t.getUniversityId())).collect(Collectors.toSet());
//        if (matchedUniversityIdSet.size() > 0) {
//            cond.clear();
//            cond.put("location", location);
//            cond.put("year", currYear - 1);
//            cond.put("universityIds", matchedUniversityIdSet);
//            cond.put("level", query.getBatchName()); // 本科，专科
//            cond.put("liberalScience", NewgaokaoType.isNewgaokao33Province(location) ? 2 : (("物理".equals(subjects[0]) || "理科".equals(subjects[0])) ? 1 : 0));
//            for (BBusiWishUniversitySubmits us : busiWishUniversitySubmitsMapper.selectBBusiWishUniversitySubmitsByCond(cond)) {
//                BBusiWishUniversitySubmitMarjors submitMarjors = new BBusiWishUniversitySubmitMarjors();
//                submitMarjors.setScore(us.getScore());
//                submitMarjors.setSeat(us.getSeat());
//                submitMarjors.setYear(us.getYear());
//                submitMarjors.setType(us.getType());
//                universityMarjorMap.put(us.getUniversityId(), submitMarjors);
//            }
//        }

        boolean scoreLock = getScoreLock(location);
        boolean showInheritScore = Boolean.valueOf(configService.selectConfigByKey("voluntary.showInheritScore"));
        Integer batchLine = getBatchline(location, currYear, query.getBatchName().contains("本科") ? 1 : 5, getExamMajor());

        List<ZytbDto.ZytbVoluntaryMarjorDetail> detailList = Lists.newArrayList();
        for (BBusiWishUniversitySubmitRecruitPlan planGroupDetail : planGroupDetailList) {
            ZytbDto.ZytbVoluntaryMarjorDetail d = new ZytbDto.ZytbVoluntaryMarjorDetail();
            BeanUtils.copyProperties(planGroupDetail, d);
            majorFeatureCache.setMajorFeature(planGroupDetail, d);

            BBusiWishUniversitySubmitMarjors[] histories = new BBusiWishUniversitySubmitMarjors[4];
            if (null != planGroupDetail.getStats() && ArrayUtils.isNotEmpty(planGroupDetail.getStats().getStats())) {
                SubmitYearsStats.YearStat[] stats = planGroupDetail.getStats().getStats();
                for (int i = 0; i < Math.min(4, stats.length); i++) {
                    if (stats[i] != null) {
                        BBusiWishUniversitySubmitMarjors submitMarjor = new BBusiWishUniversitySubmitMarjors();
                        submitMarjor.setId(stats[i].getSubmitId());
                        submitMarjor.setYear(String.valueOf(stats[i].getYear()));
                        submitMarjor.setNumReal(stats[i].getNumReal());
                        submitMarjor.setScore(scoreLock ? stats[i].getScore() : stats[i].getCorrScore());
                        submitMarjor.setSeat(scoreLock ? stats[i].getSeat() : stats[i].getCorrSeat());
                        submitMarjor.setInheritScore(showInheritScore && null != stats[i].getScore() && null != submitMarjor.getScore() && stats[i].getScore() == 0 && submitMarjor.getScore() > 0);
                        submitMarjor.setCollect(stats[i].getCollect());
                        submitMarjor.setCollectDesc(stats[i].getCollectDesc());
                        submitMarjor.setSinoForeign(stats[i].getSinoForeign());
                        submitMarjor.setSinoForeignDesc(stats[i].getSinoForeignDesc());
                        Integer year = stats[i].getYear();
                        submitMarjor.setYear(String.valueOf(year));
                        // submitMarjor.setType(planGroupDetail.getType());
                        histories[i] = submitMarjor;
                    }
                }
            }
            BBusiWishUniversitySubmitMarjors empty = new BBusiWishUniversitySubmitMarjors();
            for (int i = 0; i < histories.length; i++) {
                if (null == histories[i]) {
                    histories[i] = empty;
                }
            }
            MxjbWishScoreRateServiceImpl.ScoreRatioResult scoreRatioResult = getScoreRatioResult(histories, currYear, currSubmitYear, baseYearScore, location, query.getMode(), query.getScore().intValue(), batchLine);;
            if(null != scoreRatioResult) {
                d.setPickType(scoreRatioResult.getEnrollLevel());
                d.setEnrollOver(scoreRatioResult.getOverflow());
                d.setEnrollRatio(scoreRatioResult.getRatio());
                d.setEnrollRatioText(scoreRatioResult.getRiskLevel());
            } else {
                d.setPickType(null);
                d.setEnrollRatio(null);
                d.setEnrollRatioText("");
            }
//            if (null == histories[0]) {
//                histories[0] = universityMarjorMap.get(planGroupDetail.getUniversityId());
//            }
            if (histories!= null && histories.length > 1) {
                histories = historyIndex > 0 ? ArrayUtils.subarray(histories, 1, histories.length) : ArrayUtils.subarray(histories, 0, histories.length - 1);
            }
            d.setHistories(histories);
            d.setHistory(d.getHistories()[0]);
            detailList.add(d);
        }

        Collections.sort(detailList, new Comparator<ZytbDto.ZytbVoluntaryMarjorDetail>() {
            @Override
            public int compare(ZytbDto.ZytbVoluntaryMarjorDetail o1, ZytbDto.ZytbVoluntaryMarjorDetail o2) {
                Integer iRet;
                Integer h1 = null != o1.getHistory() && null != o1.getHistory().getScore() ? 0 : 1;
                Integer h2 = null != o2.getHistory() && null != o2.getHistory().getScore() ? 0 : 1;
                if((iRet = h1.compareTo(h2)) != 0) {
                    return iRet;
                }
                Integer v1 = null != o1.getEnrollRatio() ? o1.getEnrollRatio() : 0;
                Integer v2 = null != o2.getEnrollRatio() ? o2.getEnrollRatio() : 0;
                if((iRet = v1.compareTo(v2)) != 0) {
                    return iRet;
                }
                String m1 = null != o1.getMarjorBelongs() ? o1.getMarjorBelongs() : "";
                String m2 = null != o2.getMarjorBelongs() ? o2.getMarjorBelongs() : "";
                return m1.compareTo(m2);
            }
        });
        return detailList;
    }

    private static class MajorFeatureCache {
        Map<String, List<String>> typeNameMap = Maps.newHashMap();
        Map<String, String> professionNameMap = Maps.newHashMap();
        Map<String, String> levelMap = Maps.newHashMap();


        public MajorFeatureCache(BBusiWishUniversitiesSubjectevaluateMapper busiWishUniversitiesSubjectevaluateMapper,
                                 BBusiWishUniversitiesProfessionMapper busiWishUniversitiesProfessionMapper,
                                 BBusiWishUniversitiesSubjectMapper busiWishUniversitiesSubjectMapper) {
            busiWishUniversitiesSubjectevaluateMapper.selectBBusiWishUniversitiesSubjectevaluateList(new BBusiWishUniversitiesSubjectevaluate()).stream().forEach(t -> {
                String key = t.getCollegeCode() + "_" + t.getName();
                levelMap.put(key, t.getLevel());
            });
            BBusiWishUniversitiesProfession upCond = new BBusiWishUniversitiesProfession();
            upCond.setExamType(Constant.EXAM_TYPE_PG);
            busiWishUniversitiesProfessionMapper.selectBBusiWishUniversitiesProfessionList(new BBusiWishUniversitiesProfession()).stream().forEach(t -> {
                String key = t.getCollegeCode() + "_" + t.getName();
                professionNameMap.put(key, t.getProfessionType());
            });
            busiWishUniversitiesSubjectMapper.selectBBusiWishUniversitiesSubjectList(new BBusiWishUniversitiesSubject()).stream().forEach(t -> {
                if(null == t.getSubjects()) {
                    return;
                }
                t.getSubjects().forEach(s -> {
                    String key = t.getCollegeCode() + "_" + s;
                    List<String> typeNames = typeNameMap.get(key);
                    if (null == typeNames) {
                        typeNames = Lists.newArrayList();
                        typeNameMap.put(key, typeNames);
                    }
                    typeNames.add(t.getTypeName());
                });
            });
        }

        public void setMajorFeature(BBusiWishUniversitySubmitRecruitPlan rp, ZytbDto.ZytbVoluntaryMarjorDetail d) {
            // String idKey = rp.getUniversityCode() + "_" + rp.getMarjorId();
            String nameKey = rp.getUniversityCode() + "_" + rp.getMarjorName();
            d.setLevel(levelMap.get(nameKey));
            d.setProfessionType(professionNameMap.get(nameKey));
            d.setTypeNames(typeNameMap.get(nameKey));
        }
    };

    /**
     * 保存专业组
     * @param wishResId
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<ZytbDto.ZytbVolunteerRes> getRecommendVoluntaryRes(Long wishResId, Integer pageNo, Integer pageSize) {
        return getRecommendVoluntaryRes(wishResId, pageNo, pageSize, null);
    }
    public List<ZytbDto.ZytbVolunteerRes> getRecommendVoluntaryRes(Long wishResId, Integer pageNo, Integer pageSize, Map dataMap) {
        List<ZytbDto.ZytbVolunteerRes> volunteerResList = Lists.newArrayList();

        BBusiWishRecords busiWishrecords = busiWishRecordsMapper.selectBBusiWishRecordsById(wishResId);
        if (null == busiWishrecords) {
            return volunteerResList;
        }
        SubmitWishRecordDTO.User us = JSON.parseObject(busiWishrecords.getUserSnapshot(), SubmitWishRecordDTO.User.class);
        // 初始化院校，计划，录取
        SubmitWishRecordDTO.SubmitWishRecordDetail detail = JSON.parseObject(busiWishrecords.getDetail(), SubmitWishRecordDTO.SubmitWishRecordDetail.class);
        if (null == detail.getYear()) {
            Integer year = NumberUtils.toInt(detail.getBatch().getScores().get(0).split("年")[0], 0);
            detail.setYear(year > 0 ? year : DateUtils.getCurrentYear());
            detail.getBatch().setName(StringUtils.replace(detail.getBatch().getName(), "普通类院校", ""));
        }
        Set<Long> allUniversityIdSet = Sets.newHashSet();
        Map<Long, Long> planUniversityMap = Maps.newHashMap();
        for (SubmitWishRecordDTO.SubmitWishRecordBatchWishes wish : detail.getBatch().getWishes()) {
            allUniversityIdSet.add(wish.getUniversityId());
            for (SubmitWishRecordDTO.SubmitWishRecordBatchMarjors marjor : wish.getMarjors()) {
                planUniversityMap.put(marjor.getId(), wish.getUniversityId());
            }
        }
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        Integer currYear = detail.getYear();
        String mode = us.getExamMajor();
        // 录取年度
        Integer currSubmitYear = getSubmitYear(location, null, detail.getBatch().getName(), currYear);
        if (null != dataMap) {
            dataMap.put("record", busiWishrecords);
            dataMap.put("detail", detail);
            dataMap.put("location", location);
            dataMap.put("currYear", currYear);
            dataMap.put("currSubmitYear", currSubmitYear);

            Map cond = Maps.newHashMap();
            cond.put("location", location);
            cond.put("year", currYear);
            cond.put("mode", mode);
            dataMap.put("seat", getLowestRank(cond, detail.getScore().intValue()));
        }
        if (planUniversityMap.size() == 0) {
            return volunteerResList;
        }
        Map cond = Maps.newHashMap();
        cond.put("ids", Lists.newArrayList(planUniversityMap.keySet()));
        Map<Long, BBusiWishUniversitySubmitRecruitPlan> planMap = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitRecruitGroups(cond)
                .stream().collect(Collectors.toMap(BBusiWishUniversitySubmitRecruitPlan::getId, Function.identity()));
        busiWishUniversityMarjorsPlanSubmitMapper.selectBBusiWishUniversityMarjorsPlanSubmitByIds(planUniversityMap.keySet()).stream().forEach(t -> {
            if (null != t.getStats()) {
                planMap.get(t.getPlanId()).setStats(t.getStats());
                planUniversityMap.remove(t.getPlanId());
            }
        });
//        Map<Long, BBusiWishUniversitySubmitMarjors> universityMarjorMap = Maps.newHashMap();
//        if (planUniversityMap.size() > 0) {
//            cond.clear();
//            cond.put("location", location);
//            cond.put("year", detail.getYear() - 1);
//            cond.put("universityIds", Sets.newHashSet(planUniversityMap.values()));
//            cond.put("level", detail.getBatch().getName()); // 本科，专科
//            cond.put("liberalScience", NewgaokaoType.isNewgaokao33Province(location) ? 2 : (("物理".equals(subjects[0]) || "理科".equals(subjects[0])) ? 1 : 0));
//            for (BBusiWishUniversitySubmits us : busiWishUniversitySubmitsMapper.selectBBusiWishUniversitySubmitsByCond(cond)) {
//                BBusiWishUniversitySubmitMarjors submitMarjors = new BBusiWishUniversitySubmitMarjors();
//                submitMarjors.setScore(us.getScore());
//                submitMarjors.setSeat(us.getSeat());
//                submitMarjors.setYear(us.getYear());
//                submitMarjors.setType(us.getType());
//                universityMarjorMap.put(Long.parseLong(us.getUniversityId()), submitMarjors);
//            }
//        }
        cond.clear();
        cond.put("ids", allUniversityIdSet);
        Map<Long, BBusiWishUniversities> wishUniversityMap = bBusiWishUniversitiesMapper.selectBBusiWishUniversitiesListSimpleByIds(cond).stream().collect(Collectors.toMap(BBusiWishUniversities::getId, Function.identity()));

        boolean scoreLock = getScoreLock(location);
        boolean showInheritScore = Boolean.valueOf(configService.selectConfigByKey("voluntary.showInheritScore"));

        // Map<Integer, String> yearCourseMap = getYearCourseMap(detail.getYear(), detail.getMode().split(",")[0]);
        Integer baseYearScore = currSubmitYear.equals(currYear) ? detail.getScore().intValue() : getScoreOfLastYear(location, currYear, mode, detail.getScore().intValue());
        Integer historyIndex = currSubmitYear.equals(currYear) ? 0 : 1;
        Integer batchLine = getBatchline(location, NumberUtils.toInt(busiWishrecords.getBatch(), 5), currYear, getExamMajor());
        BBusiWishUniversitySubmitMarjors empty = new BBusiWishUniversitySubmitMarjors();
        List<ZytbDto.ZytbVoluntaryMarjorDetail> marjorList;
        for (SubmitWishRecordDTO.SubmitWishRecordBatchWishes wish : detail.getBatch().getWishes()) {
            ZytbDto.ZytbVolunteerRes res = new ZytbDto.ZytbVolunteerRes();
            BBusiWishUniversities wishUniversities = wishUniversityMap.get(wish.getUniversityId());
            if (null != wishUniversities && StringUtils.isNotBlank(wish.getName())
                    && 0 != StringUtils.compare(wish.getName(), wishUniversities.getName())) {
                BBusiWishUniversities newWishUniversities = new BBusiWishUniversities();
                BeanUtils.copyProperties(wishUniversities, newWishUniversities);
                newWishUniversities.setName(wish.getName());
                res.setUniversity(newWishUniversities);
            } else {
                res.setUniversity(wishUniversities);
            }
            res.setUniversityId(wish.getUniversityId());
            res.setJCode(wish.getJCode());
            res.setPickType(wish.getPickType());
            volunteerResList.add(res);
            for (SubmitWishRecordDTO.SubmitWishRecordBatchMarjors marjor : wish.getMarjors()) {
                BBusiWishUniversitySubmitRecruitPlan plan = planMap.get(marjor.getId());
                if (null == plan) {
                    continue;
                }
                if (null != dataMap) {
                    if(null == (marjorList = res.getMarjorList())) {
                        marjorList = Lists.newArrayList();
                        res.setMarjorList(marjorList);
                    }
                    marjorList.add(toMarjorDetail(mode, batchLine, plan, marjor, null, empty, currYear, baseYearScore, historyIndex, scoreLock, showInheritScore, currSubmitYear, detail.getScore().intValue()));
                }
                if (null == res.getRecruitPlan()) {
                    plan.setId(null);
                    plan.setMarjorName(null);
                    plan.setMarjorDirection(null);
                    plan.setMarjorBelongs(null);
                    plan.setXuezhi(null);
                    plan.setXuefei(null);
                    plan.setMajorCount(1);
                    res.setRecruitPlan(plan);
                    res.setSpecialProject(plan.getSpecialProject());
                } else {
                    BBusiWishUniversitySubmitRecruitPlan merge = res.getRecruitPlan();
                    merge.setMajorCount(merge.getMajorCount() + 1);
                    merge.setPlanCount(merge.getPlanCount() + plan.getPlanCount());
                }
                if(null == plan.getStats()) {
                    continue;
                }
                SubmitYearsStats.YearStat[] statsList = plan.getStats().getStats();
                plan.setStats(null);
                BBusiWishUniversitySubmitMarjors[] submitMarjorList = res.getHistories();
                if (null == submitMarjorList) {
                    submitMarjorList = new BBusiWishUniversitySubmitMarjors[4];
                    res.setHistories(submitMarjorList);
                }

                for (int i = 0; i < Math.min(4, statsList.length); i++) {
                    if (null == statsList[i]) {
                        continue;
                    }
                    BBusiWishUniversitySubmitMarjors merge = submitMarjorList[i];
                    if (null == merge) {
                        submitMarjorList[i] = new BBusiWishUniversitySubmitMarjors();
                        submitMarjorList[i].setScore(scoreLock ? statsList[i].getScore() : statsList[i].getCorrScore());
                        submitMarjorList[i].setSeat(scoreLock ? statsList[i].getSeat() : statsList[i].getCorrSeat());
                        submitMarjorList[i].setNumReal(statsList[i].getNumReal());
                        submitMarjorList[i].setCollect(statsList[i].getCollect());
                        submitMarjorList[i].setCollectDesc(statsList[i].getCollectDesc());
                        submitMarjorList[i].setSinoForeign(statsList[i].getSinoForeign());
                        submitMarjorList[i].setSinoForeignDesc(statsList[i].getSinoForeignDesc());
                        Integer year = statsList[i].getYear();
                        submitMarjorList[i].setYear(String.valueOf(year));
                        // submitMarjorList[i].setType(yearCourseMap.get(year));
                        continue;
                    } else {
                        if (null != merge.getNumReal() && null != statsList[i].getNumReal()) {
                            merge.setNumReal(merge.getNumReal() + statsList[i].getNumReal());
                        } else if (null != merge.getNumReal()) {
                            merge.setNumReal(merge.getNumReal());
                        } else if (null != statsList[i].getNumReal()) {
                            merge.setNumReal(statsList[i].getNumReal());
                        }
                        if (null == merge.getCollect() && null != statsList[i].getCollect() && statsList[i].getCollect()) {
                            merge.setCollect(statsList[i].getCollect());
                            merge.setCollectDesc(statsList[i].getCollectDesc());
                        }
                        if (null == merge.getSinoForeign() && null != statsList[i].getSinoForeign() && statsList[i].getSinoForeign()) {
                            merge.setSinoForeign(statsList[i].getSinoForeign());
                            merge.setSinoForeignDesc(statsList[i].getSinoForeignDesc());
                        }
                        merge.setScore(Math.min(merge.getScore(), scoreLock ? statsList[i].getScore() : statsList[i].getCorrScore()));
                        merge.setSeat(Math.max(merge.getSeat(), scoreLock ? statsList[i].getSeat() : statsList[i].getCorrSeat()));
                    }
                }
            }
            res.setEnrollRatio(wish.getEnrollRatio());
            res.setEnrollRatioText(wish.getEnrollRatioText());
        }
        volunteerResList.forEach(t -> {
            BBusiWishUniversitySubmitMarjors[] submitMarjorsList = t.getHistories();
            if (null == submitMarjorsList) {
                submitMarjorsList = new BBusiWishUniversitySubmitMarjors[4];
                t.setHistories(submitMarjorsList);
            }
            for (int i = 0; i < submitMarjorsList.length; i++) {
                if (null == submitMarjorsList[i]) {
                    submitMarjorsList[i] = empty;
                }
            }
            MxjbWishScoreRateServiceImpl.ScoreRatioResult scoreRatioResult = getScoreRatioResult(submitMarjorsList, currYear, currSubmitYear, baseYearScore, location, mode, detail.getScore().intValue(), batchLine);
            if (null != scoreRatioResult) {
                // t.setPickType(scoreRatioResult.getEnrollLevel());
                t.setEnrollOver(scoreRatioResult.getOverflow());
                // t.setEnrollRatio(String.valueOf(scoreRatioResult.getRatio()));
                // t.setEnrollRatioText(scoreRatioResult.getRiskLevel());
            } else {
                // t.setPickType(null);
                // t.setEnrollRatio("");
                // t.setEnrollRatioText("");
            }
//            if (null == submitMarjorsList[0]) {
//                submitMarjorsList[0] = universityMarjorMap.get(t.getUniversityId());
//            }
            t.setUniversityId(null);
            if (null != submitMarjorsList && submitMarjorsList.length > 1) {
                submitMarjorsList = historyIndex > 0 ? ArrayUtils.subarray(submitMarjorsList, 1, submitMarjorsList.length) : ArrayUtils.subarray(submitMarjorsList, 0, submitMarjorsList.length - 1);
            }
            t.setHistories(submitMarjorsList);
            t.setHistory(t.getHistories()[0]);
        });
        /*Collections.sort(volunteerResList, new Comparator<ZytbDto.ZytbVolunteerRes>() {
            @Override
            public int compare(ZytbDto.ZytbVolunteerRes o1, ZytbDto.ZytbVolunteerRes o2) {
                Double v1 = null != o1.getHistory() && null != o1.getHistory().getScore() ? o1.getHistory().getScore() : 0.0;
                Double v2 = null != o2.getHistory() && null != o2.getHistory().getScore() ? o2.getHistory().getScore() : 0.0;
                return -v1.compareTo(v2);
            }
        });*/
        return PageUtil.buildListForPage(volunteerResList, pageNo, pageSize);
    }
    public List<ZytbDto.ZytbVoluntaryMarjorDetail> getVoluntaryMarjorsRes(Long wishResId, String collegeCode, String jCode) {
        List<ZytbDto.ZytbVoluntaryMarjorDetail> detailList = Lists.newArrayList();

        BBusiWishRecords busiWishrecords = busiWishRecordsMapper.selectBBusiWishRecordsById(wishResId);
        if (null == busiWishrecords) {
            return detailList;
        }
        SubmitWishRecordDTO.User us = JSON.parseObject(busiWishrecords.getUserSnapshot(), SubmitWishRecordDTO.User.class);
        String mode = us.getExamMajor();
        // 初始化院校，计划，录取
        SubmitWishRecordDTO.SubmitWishRecordDetail detail = JSON.parseObject(busiWishrecords.getDetail(), SubmitWishRecordDTO.SubmitWishRecordDetail.class);
        Map<Long, Long> planUniversityMap = Maps.newHashMap();
        for (SubmitWishRecordDTO.SubmitWishRecordBatchWishes wish : detail.getBatch().getWishes()) {
            if (!wish.getCollegeCode().equals(collegeCode) || StringUtils.isNotBlank(jCode) && !jCode.equals(wish.getJCode())) {
                continue;
            }
            for (SubmitWishRecordDTO.SubmitWishRecordBatchMarjors marjor : wish.getMarjors()) {
                planUniversityMap.put(marjor.getId(), wish.getUniversityId());
            }
        }
        if (planUniversityMap.size() == 0) {
            return detailList;
        }
        Map cond = Maps.newHashMap();
        cond.put("ids", Lists.newArrayList(planUniversityMap.keySet()));
        Map<Long, BBusiWishUniversitySubmitRecruitPlan> planMap = busiWishUniversitySubmitRecruitPlanMapper.selectWishUniversitySubmitRecruitGroups(cond)
                .stream().collect(Collectors.toMap(BBusiWishUniversitySubmitRecruitPlan::getId, Function.identity()));
        busiWishUniversityMarjorsPlanSubmitMapper.selectBBusiWishUniversityMarjorsPlanSubmitByIds(planUniversityMap.keySet()).stream().forEach(t -> {
            planMap.get(t.getPlanId()).setStats(t.getStats());
            planUniversityMap.remove(t.getPlanId());
        });

        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        Integer currYear = detail.getYear();
        Integer currSubmitYear = getSubmitYear(location, null, detail.getBatch().getName(), currYear);
//        Map<Long, BBusiWishUniversitySubmitMarjors> universityMarjorMap = Maps.newHashMap();
//        if (planUniversityMap.size() > 0) {
//            cond.clear();
//            cond.put("location", location);
//            cond.put("year", detail.getYear() - 1);
//            cond.put("universityIds", Sets.newHashSet(planUniversityMap.values()));
//            cond.put("level", detail.getBatch().getName()); // 本科，专科
//            cond.put("liberalScience", NewgaokaoType.isNewgaokao33Province(location) ? 2 : (("物理".equals(subjects[0]) || "理科".equals(subjects[0])) ? 1 : 0));
//            for (BBusiWishUniversitySubmits us : busiWishUniversitySubmitsMapper.selectBBusiWishUniversitySubmitsByCond(cond)) {
//                BBusiWishUniversitySubmitMarjors submitMarjors = new BBusiWishUniversitySubmitMarjors();
//                submitMarjors.setScore(us.getScore());
//                submitMarjors.setSeat(us.getSeat());
//                submitMarjors.setYear(us.getYear());
//                submitMarjors.setType(us.getType());
//                universityMarjorMap.put(Long.parseLong(us.getUniversityId()), submitMarjors);
//            }
//        }
        cond.clear();

        boolean scoreLock = getScoreLock(location);
        boolean showInheritScore = Boolean.valueOf(configService.selectConfigByKey("voluntary.showInheritScore"));

        Integer baseYearScore = currSubmitYear.equals(currYear) ? detail.getScore().intValue() : getScoreOfLastYear(location, currYear, mode, detail.getScore().intValue());
        Integer historyIndex = currSubmitYear.equals(currYear) ? 0 : 1;
        BBusiWishUniversitySubmitMarjors empty = new BBusiWishUniversitySubmitMarjors();
        Integer batchLine = getBatchline(location, currYear, NumberUtils.toInt(busiWishrecords.getBatch(), 5), getExamMajor());
        for (SubmitWishRecordDTO.SubmitWishRecordBatchWishes wish : detail.getBatch().getWishes()) {
            if (!wish.getCollegeCode().equals(collegeCode) || StringUtils.isNotBlank(jCode) && !jCode.equals(wish.getJCode())) {
                continue;
            }
            for (SubmitWishRecordDTO.SubmitWishRecordBatchMarjors marjor : wish.getMarjors()) {
                BBusiWishUniversitySubmitRecruitPlan plan = planMap.get(marjor.getId());
                if (null == plan) {
                    continue;
                }
                ZytbDto.ZytbVoluntaryMarjorDetail d = toMarjorDetail(mode, batchLine, plan, marjor, null,
                        empty, currYear, baseYearScore, historyIndex, scoreLock, showInheritScore,
                        currSubmitYear, detail.getScore().intValue());
                detailList.add(d);
            }
        }
        return detailList;
    }

    private ZytbDto.ZytbVoluntaryMarjorDetail toMarjorDetail(String mode, Integer batchLine, BBusiWishUniversitySubmitRecruitPlan plan, SubmitWishRecordDTO.SubmitWishRecordBatchMarjors marjor, Map<Integer, String> yearCourseMap,
                                                             BBusiWishUniversitySubmitMarjors empty, Integer currYear, Integer baseYearScore, Integer historyIndex, boolean scoreLock, boolean showInheritScore,
                                                             Integer currSubmitYear, Integer score) {
        ZytbDto.ZytbVoluntaryMarjorDetail d = new ZytbDto.ZytbVoluntaryMarjorDetail();
        BeanUtils.copyProperties(plan, d);
        d.setMarjorName(marjor.getName());
        BBusiWishUniversitySubmitMarjors[] submitMarjorList = d.getHistories();
        String location = SecurityUtils.getLoginUser().getUser().getLocation();
        if(null != plan.getStats()) {
            SubmitYearsStats.YearStat[] statsList = plan.getStats().getStats();
            for (int i = 0; i < Math.min(4, statsList.length); i++) {
                if (null == statsList[i]) {
                    continue;
                }
                submitMarjorList[i] = new BBusiWishUniversitySubmitMarjors();
                submitMarjorList[i].setYear(String.valueOf(statsList[i].getYear()));
                submitMarjorList[i].setScore(scoreLock ? statsList[i].getScore() : statsList[i].getCorrScore());
                submitMarjorList[i].setSeat(scoreLock ? statsList[i].getSeat() : statsList[i].getCorrSeat());
                submitMarjorList[i].setInheritScore(showInheritScore && null != statsList[i].getScore() && null != submitMarjorList[i].getScore() && statsList[i].getScore() == 0 && submitMarjorList[i].getScore() > 0);
                submitMarjorList[i].setNumReal(statsList[i].getNumReal());
                submitMarjorList[i].setCollect(statsList[i].getCollect());
                submitMarjorList[i].setCollectDesc(statsList[i].getCollectDesc());
                submitMarjorList[i].setSinoForeign(statsList[i].getSinoForeign());
                submitMarjorList[i].setSinoForeignDesc(statsList[i].getSinoForeignDesc());
            }
        }
        for (int i = 0; i < submitMarjorList.length; i++) {
            if (null == submitMarjorList[i]) {
                submitMarjorList[i] = empty;
            }
        }
        d.setPickType(marjor.getPickType());
        d.setEnrollRatio(NumberUtils.toInt(marjor.getEnrollRatio(), 0));
        d.setEnrollRatioText(marjor.getEnrollRatioText());
        d.setEnrollFluctuate(marjor.getEnrollFluctuate());
        MxjbWishScoreRateServiceImpl.ScoreRatioResult scoreRatioResult = getScoreRatioResult(submitMarjorList, currYear, currSubmitYear, baseYearScore, location, mode, score, batchLine);
        if (null != scoreRatioResult) {
            // d.setPickType(scoreRatioResult.getEnrollLevel());
            d.setEnrollOver(scoreRatioResult.getOverflow());
            // d.setEnrollRatio(scoreRatioResult.getRatio());
            // d.setEnrollRatioText(scoreRatioResult.getRiskLevel());
        } else {
            // d.setPickType(null);
            // d.setEnrollRatio(null);
            // d.setEnrollRatioText("");
        }
//                if(null == submitMarjorList[0]) {
//                    submitMarjorList[0] = universityMarjorMap.get(wish.getUniversityId());
//                }
        if (submitMarjorList!= null && submitMarjorList.length > 1) {
            submitMarjorList = historyIndex > 0 ? ArrayUtils.subarray(submitMarjorList, 1, submitMarjorList.length) : ArrayUtils.subarray(submitMarjorList, 0, submitMarjorList.length - 1);
        }
        d.setHistories(submitMarjorList);
        d.setHistory(d.getHistories()[0]);
        return d;
    }

    private Integer[] getScoreRange(Integer maxScore, Integer minScore, String location, Integer currYear, String mode) {
        Map cond = Maps.newHashMap();
        cond.put("location", location);
        cond.put("year", currYear);
        cond.put("mode", mode);
        Integer minLevel = getLowestRank(cond, maxScore);
        Integer maxLevel = getLowestRank(cond, minScore);
        return new Integer[] {minLevel, maxLevel};
    }

    /**
     * 查询
     */
    private Integer[] getPickTypeSegments(Integer score, Integer batchMinScore, String location, Integer currYear, String mode) {
        // +30 -15 -60 => 600分 历史  冲：(600-630] 稳：(585-600] 保：(414,585]  // 600 => [630=70 冲 600=91 稳 585=200 保 414=300)
        if (null == score) {
            log.error("Score is null for {} at {} {}", location, currYear, mode);
            return null;
        }
        Map cond = Maps.newHashMap();
        cond.put("location", location);
        cond.put("year", currYear);
        cond.put("mode", mode);

        // [Level=冲的最大值, 冲的最低值, 稳的最低值, 保的最低值, Rate>=99的最高值, Rate<=1的最小值]
        Integer[] levelRanges = mxjbWishScoreRateService.getWishScoreRatioRanges(currYear, score);
        if (null == levelRanges) {
            log.error("getLevelRanges for {} at {} {}", location, currYear, cond.get("mode"));
            return null;
        }

        Integer rank1, rank2, rank3, rank4;
        if (null == (rank2 = getLowestRank(cond, levelRanges[1]))) {
            log.error("getLowestRank for {} at {} {}", location, currYear, cond.get("mode"));
            return null;
        }

        Integer[] segments = new Integer[4];
        segments[0] = null == (rank1 = getLowestRank(cond, levelRanges[0])) ? rank2 - 1 : Math.min(rank1, rank2 - 1);
        segments[1] = rank2;
        segments[2] = null == (rank3 = getLowestRank(cond, levelRanges[2])) ? null : Math.max(rank3, rank2 + 1);
        segments[3] = null == (rank4 = getLowestRank(cond, Math.max(batchMinScore, levelRanges[3]))) ? null : null == rank3 ? rank4 : Math.max(rank4, rank3 + 1);
        return segments;
    }

    // 生成缓存数据表
    public void calculateSubmitPlanData(String location, Integer year) {
        Integer[] batchs = new Integer[] {0, 1, 2, 3, 4}; // 0 提前批 1 本科一批 2 本科二批 3 本科三批 4 高职专科批
        Integer[] liberalSciences = new Integer[] {0, 1, 2}; // 1理科(物理)，0文科(历史), 2文理不分
        StopWatch stopWatch = new StopWatch();
        for (Integer batch : batchs) {
            for (Integer liberalScience : liberalSciences) {
                stopWatch.reset();
                stopWatch.start();
                calculateSubmitPlanData(location, year, batch, liberalScience);
                stopWatch.stop();
                log.info("process {} {} Batch:{} {} used: {}", location, year, batch, liberalScience, stopWatch.getTime(TimeUnit.SECONDS));
            }
        }
        log.info("process Completed");
    }

    private void calculateSubmitPlanData(String location, Integer year, Integer batch, Integer liberalScience) {
        // 查询今年的计划
        BBusiWishUniversitySubmitRecruitPlan planCond = new BBusiWishUniversitySubmitRecruitPlan();
        planCond.setLocation(location);
        planCond.setYear(year.toString());
        planCond.setBatch(batch.longValue());
        planCond.setLiberalScience(liberalScience);
        List<BBusiWishUniversitySubmitRecruitPlan> recruitPlanList = busiWishUniversitySubmitRecruitPlanMapper.selectBBusiWishUniversitySubmitRecruitPlanList(planCond)
                .stream().filter(t -> StringUtils.isNotBlank(t.getCollegeCode())).collect(Collectors.toList());  // TODO 没有招生代码的如何处理？
        if (CollectionUtils.isEmpty(recruitPlanList)) {
            return;
        }
        // 检查 Group
        Map<String, BBusiWishUniversityMarjorsGroup> universityMarjorGroupMap = Maps.newHashMap();
        BBusiWishUniversityMarjorsGroup groupCond = new BBusiWishUniversityMarjorsGroup();
        groupCond.setLocation(location);
        String planLevel = recruitPlanList.get(0).getLevel();
        groupCond.setLevel(StringUtils.isNotBlank(planLevel) ? planLevel.substring(0, 2) : "");
        groupCond.setLiberalScience(liberalScience);
        for (BBusiWishUniversityMarjorsGroup group : busiWishUniversityMarjorsGroupMapper.selectBBusiWishUniversityMarjorsGroupList(groupCond)) {
            String majorName = getMajorName(group.getMarjorName(), group.getMarjorSpecial());
            String universityMajorName = group.getUniversityId() + ":" + majorName;
            universityMarjorGroupMap.put(universityMajorName, group);
        }

        List<BBusiWishUniversityMarjorsGroup> newGroupList = Lists.newArrayList();
        // key is universityCollege, value is recruitPlans
        Map<String, List<BBusiWishUniversitySubmitRecruitPlan>> universityCollegePlansMap = Maps.newHashMap();
        // key is batch, value is level
        Map<Long, String> levelNameMap = Maps.newHashMap();
        // key is universityMarjorName, value is recruitPlan
        Map<String, BBusiWishUniversitySubmitRecruitPlan> universityMarjorPlanMap = Maps.newHashMap();
        BBusiWishUniversitySubmitRecruitPlan tmpPlan;
        Map<Long, BBusiWishUniversityMarjorsGroup> planMarjorsGroupMap = Maps.newHashMap();
        for (BBusiWishUniversitySubmitRecruitPlan plan : recruitPlanList) {
            String majorName = getMajorName(plan.getMarjorName(), plan.getMarjorDirection());
            String universityMajorName = plan.getUniversityId() + ":" + majorName;
            if (null != (tmpPlan = universityMarjorPlanMap.putIfAbsent(universityMajorName, plan))) {
                log.warn("Plan {} Dup with {} ", plan.getId(), tmpPlan.getId()); // TODO 计划只取一个，重复的合并还是忽略？
                continue;
            }
            BBusiWishUniversityMarjorsGroup marjorsGroup = universityMarjorGroupMap.get(universityMajorName);
            if(null == marjorsGroup) {
                marjorsGroup = new BBusiWishUniversityMarjorsGroup();
                marjorsGroup.setLocation(location);
                marjorsGroup.setLevel(plan.getLevel());
                marjorsGroup.setLiberalScience(liberalScience);
                marjorsGroup.setUniversityId(plan.getUniversityId());
                marjorsGroup.setMarjorName(plan.getMarjorName());
                String special = majorName.substring(plan.getMarjorName().length());
                if (special.length() > 0) {
                    marjorsGroup.setMarjorSpecial(special.replace("(|)", ""));
                } else {
                    marjorsGroup.setMarjorSpecial(special);
                }
                marjorsGroup.setStatus(1L);
                newGroupList.add(marjorsGroup);
                universityMarjorGroupMap.put(universityMajorName, marjorsGroup);
            }
            planMarjorsGroupMap.put(plan.getId(), marjorsGroup);
            levelNameMap.putIfAbsent(plan.getBatch(), plan.getLevel());
            String universityCollege =  plan.getUniversityId() + ":" + plan.getCollegeCode();
            List<BBusiWishUniversitySubmitRecruitPlan> groupPlanList =  universityCollegePlansMap.get(universityCollege);
            if (null == groupPlanList) {
                groupPlanList = Lists.newArrayList();
                universityCollegePlansMap.put(universityCollege, groupPlanList);
            }
            groupPlanList.add(plan);
        }

        // 查询去年的报名, 然后映射计划与报名
        BBusiWishUniversitySubmitMarjors majorCond = new BBusiWishUniversitySubmitMarjors();
        majorCond.setLocation(planCond.getLocation());
        majorCond.setYear(String.valueOf(year - 1));
        majorCond.setLevel(levelNameMap.get(planCond.getBatch()));
        majorCond.setLiberalScience(planCond.getLiberalScience().intValue());

        // key is planId, value is Submit for last year need 3 years
        Map<Long, BBusiWishUniversitySubmitMarjors[]> planSubmitMarjorsMap = Maps.newHashMap();
        BBusiWishUniversitySubmitMarjors[] tmpSubmits;
        for (int yearIdx = 2, yr = year - 3; yr < year; yr++, yearIdx--) {
            majorCond.setYear(String.valueOf(yr));
            for (BBusiWishUniversitySubmitMarjors marjor : busiWishUniversitySubmitMarjorsMapper.selectBBusiWishUniversitySubmitMarjorsList(majorCond)) {
                String majorName = getMajorName(marjor.getMarjorName(), marjor.getMarjorDirection());
                String universityMajorName = marjor.getUniversityId() + ":" + majorName;
                BBusiWishUniversitySubmitRecruitPlan plan = universityMarjorPlanMap.get(universityMajorName);
                if (null == plan) { // 未找到计划的忽略
                    continue;
                }
                planSubmitMarjorsMap.get(plan.getId());
                if (null == (tmpSubmits = planSubmitMarjorsMap.get(plan.getId()))) {
                    tmpSubmits = new BBusiWishUniversitySubmitMarjors[4];
                    planSubmitMarjorsMap.put(plan.getId(), tmpSubmits);
                } else if (null != tmpSubmits[yearIdx]) {
                    log.warn("Submit {} Dup with {} ", marjor.getId(), tmpSubmits[yearIdx].getId()); // TODO 报名只取一个，重复的合并还是忽略？
                    continue;
                }
                tmpSubmits[yearIdx] = marjor;
            }
        }

        // 计算专业组
        BBusiWishUniversityMarjorsCollege collegeCond = new BBusiWishUniversityMarjorsCollege();
        collegeCond.setLocation(planCond.getLocation());
        collegeCond.setYear(planCond.getYear());
        collegeCond.setLevel(levelNameMap.get(planCond.getBatch()));
        collegeCond.setLiberalScience(planCond.getLiberalScience().intValue());
        Map<String, BBusiWishUniversityMarjorsCollege> universityMarjorsCollegeMap = busiWishUniversityMarjorsCollegeMapper.selectBBusiWishUniversityMarjorsCollegeList(collegeCond)
                .stream().collect(Collectors.toMap(t -> t.getUniversityId() + ":" + t.getCollegeCode(), Function.identity()));

        BBusiWishUniversityMarjorsPlanSubmit planSubmitCond = new BBusiWishUniversityMarjorsPlanSubmit();
        planSubmitCond.setLocation(planCond.getLocation());
        planSubmitCond.setYear(planCond.getYear());
        planSubmitCond.setLevel(levelNameMap.get(planCond.getBatch()));
        planSubmitCond.setLiberalScience(planCond.getLiberalScience().intValue());
        Set<Long> universityMarjorsSubmitIdSet = busiWishUniversityMarjorsPlanSubmitMapper.selectBBusiWishUniversityMarjorsPlanSubmitList(planSubmitCond)
                .stream().map(BBusiWishUniversityMarjorsPlanSubmit::getPlanId).collect(Collectors.toSet());

        BBusiWishUniversityMarjorsCollege existCollege;
        for (String universityCollege : universityCollegePlansMap.keySet()) {
            List<BBusiWishUniversitySubmitRecruitPlan> planList = universityCollegePlansMap.get(universityCollege);
            // 创建专业组
            BBusiWishUniversityMarjorsCollege college = new BBusiWishUniversityMarjorsCollege();
            BeanUtils.copyProperties(collegeCond, college);
            college.setPlanCount(0L);
            college.setMarjorCount((long) planList.size());
            tmpPlan = planList.get(0);
            college.setUniversityId(tmpPlan.getUniversityId());
            college.setCollegeCode(tmpPlan.getCollegeCode());
            // 汇总 MarjorCount, planCount(sum), score(min), seat(max), realNum(sum)
            for (BBusiWishUniversitySubmitRecruitPlan plan : planList) {
                college.setPlanCount(college.getPlanCount() + plan.getPlanCount());
                BBusiWishUniversitySubmitMarjors[] submitMarjorsList = planSubmitMarjorsMap.get(plan.getId());
                if (null != submitMarjorsList) {
                    SubmitYearsStats submitYearsStats = new SubmitYearsStats();
                    // submitYearsStats.setYear(year);
                    SubmitYearsStats.YearStat yearStats[] = new SubmitYearsStats.YearStat[3];
                    submitYearsStats.setStats(yearStats);
                    for (int i = 0; i < submitMarjorsList.length; i++) {
                        BBusiWishUniversitySubmitMarjors submitMarjors = submitMarjorsList[i];
                        if (null == submitMarjors) {
                            continue;
                        }
                        Long numReal = null != submitMarjors.getNumReal() ? submitMarjors.getNumReal() : 0;
                        if (null == yearStats[i]) {
                            yearStats[i] = new SubmitYearsStats.YearStat();
                            yearStats[i].setScore(submitMarjors.getScore());
                            yearStats[i].setSeat(submitMarjors.getSeat());
                            yearStats[i].setNumReal(numReal);
                        } else {
                            yearStats[i].setScore(Math.min(yearStats[i].getScore(), submitMarjors.getScore()));
                            yearStats[i].setSeat(Math.max(yearStats[i].getSeat(), submitMarjors.getSeat()));
                            yearStats[i].setNumReal(yearStats[i].getNumReal() + numReal);
                        }
                    }
                    college.setStats(submitYearsStats);
                }
            }
            if (null != (existCollege = universityMarjorsCollegeMap.remove(universityCollege))) {
                college.setId(existCollege.getId());
                busiWishUniversityMarjorsCollegeMapper.updateBBusiWishUniversityMarjorsCollege(college);
            } else {
                busiWishUniversityMarjorsCollegeMapper.insertBBusiWishUniversityMarjorsCollege(college);
            }

            // 新增group
            if (newGroupList.size() > 0) {
                for (BBusiWishUniversityMarjorsGroup newGroup : newGroupList) {
                    busiWishUniversityMarjorsGroupMapper.insertBBusiWishUniversityMarjorsGroup(newGroup);
                }
            }

            // 保存映射关系及专业组id
            for (BBusiWishUniversitySubmitRecruitPlan plan : planList) {
                BBusiWishUniversityMarjorsPlanSubmit planSubmit = new BBusiWishUniversityMarjorsPlanSubmit();
                BeanUtils.copyProperties(planSubmitCond, planSubmit);

                String course = plan.getCourse();
                planSubmit.setCourse(plan.getCourse());
                if (NewgaokaoType.isNewgaokao33Province(location)) {
                    planSubmit.setCourse0("");
                    if (course.contains("不限")) {
                        planSubmit.setCourse1("");
                        planSubmit.setCourse2("");
                    } else if (course.contains("+") || course.contains("和")) {
                        planSubmit.setCourse1("");
                        planSubmit.setCourse2(course.replaceAll("\\+|和", ","));
                    } else {
                        planSubmit.setCourse1(course.replaceAll("/|或", ","));
                        planSubmit.setCourse2("");
                    }
                } else {
                    course = StringUtils.trimToEmpty(course);
                    String[] courses = course.split("\\+");
                    planSubmit.setCourse0(courses[0]);
                    if (courses.length == 1 || courses[1].contains("不限")) {
                        planSubmit.setCourse1("");
                        planSubmit.setCourse2("");
                    } else if (courses[1].contains("和")) {
                        planSubmit.setCourse1("");
                        planSubmit.setCourse2(courses[1].replaceAll("和", ","));
                    } else {
                        planSubmit.setCourse1(courses[1].replaceAll("或", ","));
                        planSubmit.setCourse2("");
                    }
                }
                planSubmit.setPlanId(plan.getId());
                BBusiWishUniversitySubmitMarjors[] submitMarjorsList = planSubmitMarjorsMap.get(plan.getId());
                if (null != submitMarjorsList) {
                    if (null != submitMarjorsList[0]) {
                        planSubmit.setSubmitId(submitMarjorsList[0].getId());
                    }
                    SubmitYearsStats submitYearsStats = new SubmitYearsStats();
                    // submitYearsStats.setYear(year);
                    SubmitYearsStats.YearStat yearStats[] = new SubmitYearsStats.YearStat[3];
                    submitYearsStats.setStats(yearStats);
                    BBusiWishUniversitySubmitMarjors submitMarjors;
                    for (int i = 0; i < submitMarjorsList.length; i++) {
                        if (null == (submitMarjors = submitMarjorsList[i])) {
                            continue;
                        }
                        yearStats[i] = new SubmitYearsStats.YearStat();
                        Long numReal = null != submitMarjors.getNumReal() ? submitMarjors.getNumReal() : 0;
                        yearStats[i].setScore(submitMarjors.getScore());
                        yearStats[i].setSeat(submitMarjors.getSeat());
                        yearStats[i].setNumReal(numReal);
                    }
                    planSubmit.setStats(submitYearsStats);
                }
                BBusiWishUniversityMarjorsGroup marjorsGroup = planMarjorsGroupMap.get(plan.getId());
                planSubmit.setGroupId(marjorsGroup.getGroupId());

                planSubmit.setUniversityId(plan.getUniversityId());
                planSubmit.setCollegeCode(plan.getCollegeCode());
                String majorName = getMajorName(plan.getMarjorName(), plan.getMarjorDirection());
                planSubmit.setMarjorName(majorName);
                planSubmit.setMarjorCollegeId(college.getId());
                if (universityMarjorsSubmitIdSet.remove(plan.getId())) {
                    busiWishUniversityMarjorsPlanSubmitMapper.updateBBusiWishUniversityMarjorsPlanSubmit(planSubmit);
                } else {
                    busiWishUniversityMarjorsPlanSubmitMapper.insertBBusiWishUniversityMarjorsPlanSubmit(planSubmit);
                }
            }
        }
        if (universityMarjorsSubmitIdSet.size() > 0) {
            busiWishUniversityMarjorsPlanSubmitMapper.deleteBBusiWishUniversityMarjorsPlanSubmitByIds(universityMarjorsSubmitIdSet.toArray(new Long[universityMarjorsSubmitIdSet.size()]));
        }
        if (universityMarjorsCollegeMap.size() > 0) {
            List<Long> idList = universityMarjorsCollegeMap.values().stream().map(t -> t.getId()).collect(Collectors.toList());
            busiWishUniversityMarjorsCollegeMapper.deleteBBusiWishUniversityMarjorsCollegeByIds(idList.toArray(new Long[idList.size()]));
        }
    }

    /**
     * 限制体验卡在06/15~08/31之间不能使用模拟志愿查看与保存的功能
     * @param user
     * @return
     */
    public String limitExperienceCard(SysUser user){
        if(StringUtils.isNull(user.getCardId())|| UserRegStatus.isNotVip()){
            //未绑定卡的用户不受限制（仅能查询一条记录）
            return null;
        }

        //06-15,08-31
        String limitVoluntaryDate= configService.selectConfigByKey("voluntary.limit.experienceCard");
        String startDateStr = StringUtils.EMPTY;
        String endDateStr = StringUtils.EMPTY;
        if(StringUtils.isEmpty(limitVoluntaryDate)){
            startDateStr = DateUtils.getCurrentYear()+"-06-15";
            endDateStr = DateUtils.getCurrentYear()+"-08-31";
        }else {
            String[] limitDateArray = Convert.toStrArray(limitVoluntaryDate);
            startDateStr = DateUtils.getCurrentYear()+"-"+(null==limitDateArray[0]?("06-15"):limitDateArray[0]);
            endDateStr = DateUtils.getCurrentYear()+"-"+(null==limitDateArray[1]?("08-31"):limitDateArray[1]);
        }

        Date startLimitDate = DateUtils.parseDate(startDateStr);
        //结束日期加一天
        Date endLimitDate = new Date(DateUtils.parseDate(endDateStr).getTime()+24*60*60*1000);

        /** TODO 莫
        if(new Date().before(endLimitDate)&&startLimitDate.before(new Date())){
            DzCards card= cardsService.selectDzCardsByCardId(user.getCardId());
            if(CardType.isExperienceCard(card.getType())){
                //体验卡在限制时间内
                return String.format(Constants.EXPERIENCE_CARD_INVALID_VOLUNTARY,user.getUserName(),DateUtils.replaceYear(startDateStr),DateUtils.replaceYear(endDateStr)) ;
            }
        }

        if(getScoreLock(user.getLocation())){
            //锁分：非高三卡不能使用
            int year = Calendar.getInstance().get(Calendar.YEAR);
            if(!(user.getYear()==(year-3)||user.getGradeId()==700L)){
                return String.format(Constants.SENIOR_THIRD_CARD_INVALID_VOLUNTARY,user.getUserName(),DateUtils.replaceYear(startDateStr),DateUtils.replaceYear(endDateStr)) ;
            }
        }
         **/

        return null;
    }

    private Map<String, BBusiWishScoreGroups> locationYearRangeMap = Maps.newHashMap();

    public void clearScoreGroupRange() {
        locationYearRangeMap.clear();
    }

    /**
     * 计算一分一段极值分及位次
     * @param location
     * @param year
     * @return
     */
    public BBusiWishScoreGroups getScoreGroupRange(String location, Integer year) {
        String key = location + year;
        BBusiWishScoreGroups range = locationYearRangeMap.get(key);
        if (null != range) {
            return range;
        }
        BBusiWishScoreGroups cond = new BBusiWishScoreGroups();
        cond.setLocation(location);
        cond.setYear(year);
        range = busiWishScoreGroupsMapper.selectRangeByLocation(cond);
        locationYearRangeMap.put(key, range);
        return range;
    }

    public Integer[] getMappingRank(Integer[] ranges, String location, Integer currYear, String mode, Integer score, Integer targetYear) {
        Integer planYear = getYear();
        if(null == ranges) {
            return ranges;
        }
        for (int i = 0; i < ranges.length; i++) {
            if(null != ranges[i]) {
                ranges[i] = scoreRankService.getRankEqRank(location, planYear, currYear, mode, score, ranges[i], targetYear);
            }
        }
        return ranges;

    }

    public Integer[] getMappingRank2(Integer[] ranges, String location, Integer currYear, Integer targetYear) {
        if (null == ranges) {
            return null;
        }
        // currRank / currMinRank * lastMinRank = lastRank
        BBusiWishScoreGroups currRange = getScoreGroupRange(location, currYear);
        BBusiWishScoreGroups targetRange = getScoreGroupRange(location, targetYear);
        if (null == currRange || null == targetRange) {
            return ranges;
        }
        Integer currRangeMinRank = currRange.getLowestRank();
        Integer targetRangeMinRank = targetRange.getLowestRank();
        for (int i = 0; i < ranges.length; i++) {
            if(null != ranges[i]) {
                Double rate = ranges[i] * 1.0 / currRangeMinRank;
                ranges[i] = (int) Math.ceil(rate * targetRangeMinRank);
            }
        }
        return ranges;
    }

    public String getExamMajor(){
        return String.valueOf(SecurityUtils.getLoginUser().getUser().getExamMajor());
    }
}
