package com.pcloud.book.es.biz.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.google.common.collect.Lists;
import com.pcloud.analysisengine.browse.dto.BrowseCacheRecordDto;
import com.pcloud.book.applet.biz.AppletUserBookcaseBiz;
import com.pcloud.book.book.biz.BookBiz;
import com.pcloud.book.book.constant.BookConstant;
import com.pcloud.book.book.dao.BookAdviserDao;
import com.pcloud.book.book.dao.BookDao;
import com.pcloud.book.book.dto.ErpBookApprovalDTO;
import com.pcloud.book.consumer.analysisengine.BrowseRecordConsr;
import com.pcloud.book.consumer.channel.QrcodeSceneConsr;
import com.pcloud.book.consumer.erp.ErpConsr;
import com.pcloud.book.consumer.trade.TradeConsr;
import com.pcloud.book.es.biz.ESBookAndAdviserBiz;
import com.pcloud.book.es.entity.ESBookAndAdviser;
import com.pcloud.book.es.repository.BookAndAdviserRepository;
import com.pcloud.book.group.biz.BookGroupBiz;
import com.pcloud.book.group.dto.BookServeDTO;
import com.pcloud.book.group.enums.JoinGroupTypeEnum;
import com.pcloud.book.rightsSetting.biz.RightsSettingBiz;
import com.pcloud.book.rightsSetting.constants.RightsSettingConstant;
import com.pcloud.book.rightsSetting.dto.RightsSettingDto;
import com.pcloud.book.util.common.ThreadPoolUtils;
import com.pcloud.book.util.common.YesOrNoEnums;
import com.pcloud.book.util.properties.BookProps;
import com.pcloud.common.constant.CacheConstant;
import com.pcloud.common.core.aspect.ParamLog;
import com.pcloud.common.enums.AppTypeEnum;
import com.pcloud.common.exceptions.BizException;
import com.pcloud.common.utils.ListUtils;
import com.pcloud.common.utils.NumberUtil;
import com.pcloud.common.utils.cache.redis.JedisClusterUtils;
import com.pcloud.common.utils.robot.WeWorkWebHookRobotUtils;
import com.pcloud.common.utils.string.StringUtil;
import com.pcloud.facade.tradecenter.dto.BookIncomeQueryVo;
import com.pcloud.facade.tradecenter.dto.IncomeBackDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.JedisCluster;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author ruansiyuan
 * @Date 2020/4/13 12:21
 **/
@Component("esBookAndAdviserBiz")
@Slf4j
public class ESBookAndAdviserBizImpl implements ESBookAndAdviserBiz {

    private static final Logger LOGGER = LoggerFactory.getLogger(ESBookAndAdviserBizImpl.class);

    private static final String NOT_FOUND_BOOK_ID_SELECT_KEY = "BOOK:ESBookAndAdviserBizImpl:getNotFoundBookId";
    private static final String NOT_FOUND_BOOK_ID_STATUS_SELECT_KEY = "BOOK:ESBookAndAdviserBizImpl:runStatus";

    @Autowired
    private BookDao bookDao;
    @Autowired
    private BookAndAdviserRepository bookAndAdviserRepository;
    @Autowired
    private AppletUserBookcaseBiz appletUserBookcaseBiz;
    @Autowired
    private QrcodeSceneConsr qrcodeSceneConsr;
    @Autowired
    private BookGroupBiz bookGroupBiz;
    @Autowired
    private BrowseRecordConsr browseRecordConsr;
    @Autowired
    private TradeConsr tradeConsr;
    @Autowired
    private ErpConsr erpConsr;
    @Autowired
    private RightsSettingBiz rightsSettingBiz;
    @Autowired
    private BookBiz bookBiz;
    @Autowired
    private BookAdviserDao bookAdviserDao;

    @ParamLog("导入全部book和bookAdviser")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addAllBookAndAdviserToES() {
        ThreadPoolUtils.OTHER_THREAD_POOL.execute(() -> {
            Integer count = bookDao.count();
            LOGGER.info("总数:" + count);
            if (null == count || count <= 0) {
                return;
            }
            Long maxId = 0L;
            Integer index = 0;
            Integer offset = 10000;
            List<ESBookAndAdviser> list;
            while (index * offset < count) {
                list = bookDao.findAllBookAndAdviser(maxId, offset);
                if (ListUtils.isEmpty(list)) {
                    break;
                }
                fillInfo(list,null);
                bookAndAdviserRepository.save(list);
                maxId = Long.valueOf(list.get(list.size() - 1).getBookId());
                index += 1;
                list.clear();
            }
        });
    }

    private void fillInfo(List<ESBookAndAdviser> list, List<Long> bookIds){
        Set<Long> searchBookIds = new HashSet<>();
        Set<Long> channelIds = new HashSet<>();
        Set<Long> adviserIds = new HashSet<>();

        Set<Long> sourceBookIds = new HashSet<>();
        Set<Long> sourceChannelIds = new HashSet<>();
        Set<Long> sourceAdviserIds = new HashSet<>();
        List<BookIncomeQueryVo> bookIncomeQueryVos = new ArrayList<BookIncomeQueryVo>();
        for (ESBookAndAdviser esBookAndAdviser : list){
            if (esBookAndAdviser.getIsFundSupport()==1){//统计基金书数据
                searchBookIds.add(Long.valueOf(esBookAndAdviser.getBookId()));
                channelIds.add(Long.valueOf(esBookAndAdviser.getChannelId()));
                adviserIds.add(Long.valueOf(esBookAndAdviser.getAdviserId()));
                BookIncomeQueryVo bookIncomeQueryVo = new BookIncomeQueryVo();
                bookIncomeQueryVo.setBookId(Long.valueOf(esBookAndAdviser.getBookId()));
                bookIncomeQueryVo.setChannelId(Long.valueOf(esBookAndAdviser.getChannelId()));
                bookIncomeQueryVo.setAdviserId(Long.valueOf(esBookAndAdviser.getAdviserId()));
                bookIncomeQueryVos.add(bookIncomeQueryVo);
            }
            sourceBookIds.add(Long.valueOf(esBookAndAdviser.getBookId()));
            sourceAdviserIds.add(Long.valueOf(esBookAndAdviser.getAdviserId()));
            sourceChannelIds.add(Long.valueOf(esBookAndAdviser.getChannelId()));
        }
        //书刊资源数量(二维码)
        Map<String, Integer> resourceCountMap = qrcodeSceneConsr.mapServeCount4Applet(new ArrayList<>(sourceBookIds));
        //书刊资源数量(社群书)
        Map<String, Integer> bookGroupResourceCountMap = bookGroupBiz.mapServeCount4Applet(new ArrayList<>(sourceBookIds));
        //书刊扫码量浏览量
        Map<String, BrowseCacheRecordDto> bookSvUvPvMap = browseRecordConsr.getBookSvUvPv(new ArrayList<>(adviserIds),new ArrayList<>(channelIds),new ArrayList<>(searchBookIds));
        // 获取书刊累计收益(销售额)
        Map<String, IncomeBackDto> bookIncomeMap = tradeConsr.getBookIncome(bookIncomeQueryVos, null);
        for (ESBookAndAdviser bookAndAdviser : list){
            String key = bookAndAdviser.getBookId() + "-" + bookAndAdviser.getAdviserId() + "-" + bookAndAdviser.getChannelId();
            Integer resourceCount=0;
            if (!MapUtils.isEmpty(resourceCountMap) && resourceCountMap.containsKey(key)){
                resourceCount = resourceCountMap.get(key);
            }
            if (!MapUtils.isEmpty(bookGroupResourceCountMap) && bookGroupResourceCountMap.containsKey(key)) {
                resourceCount = resourceCount + bookGroupResourceCountMap.get(key);
            }
            bookAndAdviser.setResourceCount(resourceCount);
            //统计基金书数据
            BigDecimal fundBookValue = BigDecimal.ZERO;
            if (bookAndAdviser.getIsFundSupport()==1) {
                Long scanCount = 0L;
                Long browseCounts= 0L;
                if(!MapUtils.isEmpty(bookSvUvPvMap) && bookSvUvPvMap.containsKey(key)){
                    BrowseCacheRecordDto browseCacheRecordDto = bookSvUvPvMap.get(key);
                    scanCount = browseCacheRecordDto.getScanCount() == null?0L:browseCacheRecordDto.getScanCount();
                    browseCounts = browseCacheRecordDto.getBrowseCounts()==null?0L:browseCacheRecordDto.getBrowseCounts();
                }
                BigDecimal income = BigDecimal.ZERO;
                IncomeBackDto incomeBack = bookIncomeMap.get(bookAndAdviser.getBookId()+"_"+bookAndAdviser.getChannelId()+"_"+bookAndAdviser.getAdviserId());
                if (null!=incomeBack){
                    income=incomeBack.getIncome()==null?new BigDecimal(0):incomeBack.getIncome();
                }
                fundBookValue = income.multiply(new BigDecimal(10)).add(new BigDecimal(scanCount*3)).add(new BigDecimal(browseCounts));
            }
            bookAndAdviser.setFundBookValue(fundBookValue == null?BigDecimal.ZERO:fundBookValue);
            Long adviserId = Long.valueOf(bookAndAdviser.getAdviserId());
            Long bookId = Long.valueOf(bookAndAdviser.getBookId());
            Long channelId = Long.valueOf(bookAndAdviser.getChannelId());
            //权益
            Long rightsSettingId = 0L;
            Integer joinGroupType = bookAndAdviser.getJoinGroupType();
            if (null != joinGroupType && JoinGroupTypeEnum.XIAORUI.getCode().equals(joinGroupType)){
                if (adviserId>0 && bookId>0 && channelId>0){
                    RightsSettingDto rightsSettingDto = rightsSettingBiz.getRightsSettingByBookId4AppletHome(bookId,adviserId,channelId);
                    if (null != rightsSettingDto && null != rightsSettingDto.getId()){
                        rightsSettingId = rightsSettingDto.getId();
                    } else {
                        rightsSettingId = bookBiz.getDefaultRightsSettingId();
                    }
                }
            }
            bookAndAdviser.setRightsSettingId(rightsSettingId);
            //标记是否有答案
            List<BookServeDTO> bookServeVOS = bookGroupBiz.getBookAndBookGroupServeIds(adviserId, bookId, channelId);
            bookGroupBiz.removeCanNotBuy(bookServeVOS);
            bookGroupBiz.fillBookServe(bookServeVOS);
            Integer hasAnswer = 0;
            if (!ListUtils.isEmpty(bookServeVOS)) {
                List<BookServeDTO> answerList = bookServeVOS.stream().
                        filter(s -> s.getServeName().contains("答案") || s.getFromType().equals(AppTypeEnum.ANSWER.value)).collect(Collectors.toList());
                if (!ListUtils.isEmpty(answerList)) {
                    hasAnswer = 1;
                }
            }
            bookAndAdviser.setHasAnswer(hasAnswer);
        }
    }

    @ParamLog("更新书和编辑书")
    @Override
    public void updateBookAndAdviserToES(List<Long> bookIds) {
        if (ListUtils.isEmpty(bookIds)) {
            return;
        }
        try {
            List<ESBookAndAdviser> list = bookDao.findBookAndAdviserByBookIds(bookIds);
            filterBooks(list);
            fillInfo(list,bookIds);
            if (ListUtils.isEmpty(list)){
                return;
            }
            bookAndAdviserRepository.save(list);
        }catch (Exception e){
            LOGGER.error("更新书和编辑书出错："+bookIds.toString());
        }

    }

    private void filterBooks(List<ESBookAndAdviser> books) {
        if ((ListUtils.isEmpty(books))){
            return;
        }
        Iterator<ESBookAndAdviser> iterator =  books.iterator();
        while (iterator.hasNext()) {
            ESBookAndAdviser esBookAndAdviser = iterator.next();
            String bookName = esBookAndAdviser.getBookName();
            if (StringUtil.isEmpty(bookName)){
                iterator.remove();
            }
            if (bookName.matches("^[`~!@#$%^&*()+=|{}:;\\[\\].<>/\\\\?~！@#￥%……（）——+|{}【】‘；：”“’。，,、？0123456789]+$") ||
                    bookName.matches("(.*)([哈]{4,})(.*)") ||
                    bookName.matches("(.*)([或]{4,})(.*)") ||
                    bookName.matches("(.*)(红榜)(.*)") ||
                    bookName.matches("(.*)\\$测试\\$(.*)")
            ){
                iterator.remove();
            }
        }
    }

    @Override
    public Page<ESBookAndAdviser> getAdviserBooks4ES(String grayStatus, String keyword, Long templetId, Long secondTempletId, Long thirdTempletId, Integer currentPage, Integer numPerPage) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0))
                .filter(Objects.isNull(templetId) ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("templetId", templetId))
                .filter(Objects.isNull(secondTempletId) ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("secondTempletId", secondTempletId))
                .filter(Objects.isNull(thirdTempletId) ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("thirdTempletId", thirdTempletId));

        BoolQueryBuilder should1 = QueryBuilders.boolQuery()
                .should(QueryBuilders.termQuery("isBookAdviserDelete", 0))
                .should(QueryBuilders.termQuery("isAdviserBook", 0));
        // 审核通过之后屏蔽掉测试编辑的书刊
        BoolQueryBuilder adviserIdBuilder = getAdviserIdBuilder(grayStatus);

        if (isIsbn(keyword)) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("isbn", keyword));
        } else if (StringUtils.isNotBlank(keyword)) {
            MatchQueryBuilder mqb = QueryBuilders.matchQuery("ikBookName", keyword).minimumShouldMatch("75%");

            boolQueryBuilder.must(mqb);
        }

        boolQueryBuilder.must(should1);
        boolQueryBuilder.must(adviserIdBuilder);
        Sort sort=new Sort(Sort.Direction.DESC, "isAdviserBook","lastModifiedDate", "bookId");
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage,sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
        return search;
    }

    BoolQueryBuilder getAdviserIdBuilder(String grayStatus){
        // 审核通过之后屏蔽掉测试编辑的书刊
        BoolQueryBuilder adviserIdBuilder = QueryBuilders.boolQuery();
        if (YesOrNoEnums.YES.getCode().equals(grayStatus)) {
            if ("test".equalsIgnoreCase(BookProps.getSystemEnv())) {
                adviserIdBuilder = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("adviserId", 1404));
            }
            if ("uat".equalsIgnoreCase(BookProps.getSystemEnv())) {
                adviserIdBuilder = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("adviserId", 12829));
            }
            if ("pro".equalsIgnoreCase(BookProps.getSystemEnv())) {
                adviserIdBuilder = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("adviserId", 1362));
            }
        }
        return adviserIdBuilder;
    }

    private boolean isIsbn(String keyword) {
        return StringUtils.isNumeric(keyword) && StringUtils.length(keyword) > 4;
    }

    @Override
    public Page<ESBookAndAdviser> getESAdviserBooks4Applet(String grayStatus, String keyword, List<Long> templetIds, List<Long> graLabelIds,
                                                           List<Long> subLabelIds, Integer currentPage, Integer numPerPage, List<Long> scecondTempletIds, Integer isAdviserBook, List<Long> adviserIds, Long agentId) {
        long start = System.currentTimeMillis();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0))
                .filter(QueryBuilders.termQuery("isBookAdviserDelete", 0));
        if (CollectionUtils.isNotEmpty(templetIds)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("templetId", templetIds));
        }
        if (agentId != null) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("adviserId", adviserIds));
        }

        //（年级&&科目）||二级分类
        BoolQueryBuilder must1 = QueryBuilders.boolQuery()
                .filter(ListUtils.isEmpty(graLabelIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("graLabelId", graLabelIds))
                .filter(ListUtils.isEmpty(subLabelIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("subLabelId", subLabelIds));
        BoolQueryBuilder must2 = QueryBuilders.boolQuery().filter(ListUtils.isEmpty(scecondTempletIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("secondTempletId", scecondTempletIds));
        BoolQueryBuilder should2 = QueryBuilders.boolQuery().should(must1).should(must2);

        // 审核通过之后屏蔽掉测试编辑的书刊
        BoolQueryBuilder adviserIdBuilder = getAdviserIdBuilder(grayStatus);

        boolQueryBuilder.must(adviserIdBuilder);

        //屏蔽指定书籍
        BoolQueryBuilder bookBuilder = getBookBuilder();
        boolQueryBuilder.must(bookBuilder);

        if (isIsbn(keyword)) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("isbn", keyword));
        } else if (StringUtils.isNotBlank(keyword)) {
            MatchQueryBuilder mqb = QueryBuilders.matchQuery("ikBookName", keyword).minimumShouldMatch("75%");

            boolQueryBuilder.must(mqb);
        }

        if (isAdviserBook != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("isAdviserBook",isAdviserBook));
        }
        if (!ListUtils.isEmpty(scecondTempletIds)) {
            if (ListUtils.isEmpty(graLabelIds) && ListUtils.isEmpty(subLabelIds)) {
                boolQueryBuilder.must(must2);
            } else {
                boolQueryBuilder.must(should2);
            }
        } else if (!ListUtils.isEmpty(graLabelIds) || !ListUtils.isEmpty(subLabelIds)) {
            boolQueryBuilder.must(must1);
        }
        // 排序：因为涉及分词，故而_score固定放在第一位
        Sort sort = new Sort(Sort.Direction.DESC, "_score", "isApproval", "isFundSupport", "fundBookValue", "resourceCount", "isAdviserBook", "lastModifiedDate", "bookId");
        if(StringUtil.isEmpty(keyword)){
            sort = new Sort(Sort.Direction.DESC, "isApproval", "_score", "isFundSupport", "fundBookValue", "resourceCount", "isAdviserBook", "lastModifiedDate", "bookId");
        }
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage, sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);

        log.info("es.search({})==>{},total={},cost={}",
                StringUtils.replaceEach(boolQueryBuilder.toString(), new String[]{"\n", "  ", " : "}, new String[]{"", "", "："}),
                search, search.getTotalElements(), (System.currentTimeMillis() - start));
        return search;
    }

    @Override
    public Page<ESBookAndAdviser> getESAdviserBooks4AppletV2(String grayStatus, String keyword, List<Long> templetIds, List<Long> graLabelIds,
                                                           List<Long> subLabelIds, Integer currentPage, Integer numPerPage, List<Long> scecondTempletIds, List<Long> adviserIds, Long agentId) {
        long start = System.currentTimeMillis();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0))
                .filter(QueryBuilders.termQuery("isBookAdviserDelete", 0))
                .filter(QueryBuilders.termQuery("isAdviserBook", 1))
                .filter(QueryBuilders.rangeQuery("resourceCount").gt(0));

        if (CollectionUtils.isNotEmpty(templetIds)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("templetId", templetIds));
        }
        if (agentId != null) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("adviserId", adviserIds));
        }

        //（年级&&科目）||二级分类
        BoolQueryBuilder must1 = QueryBuilders.boolQuery()
                .filter(ListUtils.isEmpty(graLabelIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("graLabelId", graLabelIds))
                .filter(ListUtils.isEmpty(subLabelIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("subLabelId", subLabelIds));
        BoolQueryBuilder must2 = QueryBuilders.boolQuery().filter(ListUtils.isEmpty(scecondTempletIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("secondTempletId", scecondTempletIds));
        BoolQueryBuilder should2 = QueryBuilders.boolQuery().should(must1).should(must2);

        // 审核通过之后屏蔽掉测试编辑的书刊
        BoolQueryBuilder adviserIdBuilder = getAdviserIdBuilder(grayStatus);
        //屏蔽指定书籍
        BoolQueryBuilder bookBuilder = getBookBuilder();
        boolQueryBuilder.filter(bookBuilder);
        boolQueryBuilder.filter(adviserIdBuilder);

        if (isIsbn(keyword)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("isbn", keyword));
        } else if (StringUtils.isNotBlank(keyword)) {
//            MatchQueryBuilder mqb = QueryBuilders.matchQuery("ikBookName", keyword).minimumShouldMatch("75%");
            boolQueryBuilder.must(QueryBuilders.queryStringQuery(keyword).field("ikBookName").minimumShouldMatch("75%"));
        }

        if (!ListUtils.isEmpty(scecondTempletIds)) {
            if (ListUtils.isEmpty(graLabelIds) && ListUtils.isEmpty(subLabelIds)) {
                boolQueryBuilder.filter(must2);
            } else {
                boolQueryBuilder.filter(should2);
            }
        } else if (!ListUtils.isEmpty(graLabelIds) || !ListUtils.isEmpty(subLabelIds)) {
            boolQueryBuilder.filter(must1);
        }
        // 排序：因为涉及分词，故而_score固定放在第一位
        Sort sort = new Sort(Sort.Direction.DESC, "_score", "isApproval", "isFundSupport", "fundBookValue", "resourceCount", "isAdviserBook", "lastModifiedDate", "bookId");
        if(StringUtil.isEmpty(keyword)){
            sort = new Sort(Sort.Direction.DESC, "isApproval", "_score", "isFundSupport", "fundBookValue", "resourceCount", "isAdviserBook", "lastModifiedDate", "bookId");
        }
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage, sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);

        log.info("es.search({})==>{},total={},cost={}",
                StringUtils.replaceEach(boolQueryBuilder.toString(), new String[]{"\n", "  ", " : "}, new String[]{"", "", "："}),
                search, search.getTotalElements(), (System.currentTimeMillis() - start));
        return search;
    }

    private BoolQueryBuilder getBookBuilder() {
        String ignorBookKey = CacheConstant.BOOK + "ignorBookKey";
        List<Long> bookIds = JedisClusterUtils.getJsonList(ignorBookKey, Long.class);
        if (ListUtils.isEmpty(bookIds)) {
            bookIds = Lists.newArrayList(5920573L,5934953L,5920574L,5934954L);
            JedisClusterUtils.setJsonList(ignorBookKey,bookIds,3600 * 24);
        }
        BoolQueryBuilder mustNot =  QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery("bookId", bookIds));
        return mustNot;

    }

    @Override
    public void deleteAdviserBooks4ES() {
        Page<ESBookAndAdviser> search = getEsBookAndAdvisers();
        while (search.getContent().size() > 0){
            search.getContent().stream().forEach(e -> log.info("从es中删除的书是" + e.getVerLabelId() + ":" + e.getBookName()) );
            bookAndAdviserRepository.delete(search.getContent());
            search = getEsBookAndAdvisers();
        }
    }

    @Override
    @ParamLog("删除选中的书籍")
    public void deleteChoosedAdviserBooks4ES(String keyword) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("bookName", keyword));
        PageRequest pageRequest = new PageRequest(0, 100);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
        if (search.getSize() > 0){
            search.getContent().stream().forEach(e -> log.info("从es中删除的书是" + e.getVerLabelId() + ":" + e.getBookName()) );
            bookAndAdviserRepository.delete(search.getContent());
        }
    }

    private Page<ESBookAndAdviser> getEsBookAndAdvisers() {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        BoolQueryBuilder should1 = QueryBuilders.boolQuery()
                .should(QueryBuilders.termQuery("isBookAdviserDelete", 0))
                .should(QueryBuilders.termQuery("isAdviserBook", 0));
        BoolQueryBuilder should2 = QueryBuilders.boolQuery()
                .should(QueryBuilders.regexpQuery("bookName","([`~!@#$%^&*()+=|{}:;\\[\\].<>/\\\\?~！@#￥%……（）——+|{}【】‘；：”“’。，,、？]+)"))
                .should(QueryBuilders.regexpQuery("bookName","([0123456789]+)"))
                .should(QueryBuilders.regexpQuery("bookName","((.*)(["+ BookConstant.HANZI+"]){4,}(.*))"))
                .should(QueryBuilders.regexpQuery("bookName","("+"(.*)(红榜)(.*)"+")"));
        boolQueryBuilder.must(should1);
        boolQueryBuilder.must(should2);
        PageRequest pageRequest = new PageRequest(0, 10);
        return bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
    }


    @Override
    public Page<ESBookAndAdviser> getESAdviserBooks4Answer(String grayStatus, String keyword, Long graLabelId, Long subLabelId, Long verLabelId, Integer currentPage, Integer numPerPage, List<Long> templetIds) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0))
                .filter(QueryBuilders.termQuery("isBookAdviserDelete", 0))
                .filter(ListUtils.isEmpty(templetIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("templetId", templetIds))
                .filter(null == graLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("graLabelId", graLabelId))
                .filter(null == subLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("subLabelId", subLabelId))
                .filter(null == verLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("verLabelId", verLabelId));
        BoolQueryBuilder should = QueryBuilders.boolQuery()
                .should(StringUtil.isEmpty(keyword) ? QueryBuilders.boolQuery() : QueryBuilders.wildcardQuery("bookName", "*" + keyword + "*"))
                .should(StringUtil.isEmpty(keyword) ? QueryBuilders.boolQuery() : QueryBuilders.wildcardQuery("isbn", "*" + keyword + "*"));
        // 审核通过之后屏蔽掉测试编辑的书刊
        BoolQueryBuilder adviserIdBuilder = getAdviserIdBuilder(grayStatus);
        boolQueryBuilder.must(adviserIdBuilder);
        boolQueryBuilder.must(should);
        Sort sort = new Sort(Sort.Direction.DESC, "isFundSupport", "fundBookValue", "resourceCount", "isAdviserBook", "lastModifiedDate", "bookId");
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage, sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
        return search;
    }

    @Override
    public Page<ESBookAndAdviser> getAdviserBooks4SeriesBook(List<String> keywords, Integer currentPage, Integer numPerPage, List<Long> adviserIds,
                                                             Long agentId, Long subLabelId, Long graLabelId, String serialNumber, Long verLabelId, Long volLabelId) {
        //k12教育分类校验
        List<Long> templetIds = Arrays.asList(RightsSettingConstant.K12_TEMPLET_ID_NEW);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0))
                .filter(QueryBuilders.termQuery("isBookAdviserDelete", 0))
                .filter(null == agentId   ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("adviserId", adviserIds))
                .filter(null == subLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("subLabelId", subLabelId))
                .filter(null == graLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("graLabelId", graLabelId))
                .filter(null == verLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("verLabelId", verLabelId))
                .filter(null == volLabelId ? QueryBuilders.boolQuery() : QueryBuilders.termQuery("volLabelId", volLabelId))
                .filter(ListUtils.isEmpty(templetIds) ? QueryBuilders.boolQuery() : QueryBuilders.termsQuery("templetId", templetIds));
        //关键词
        for (String keyword:keywords){
            BoolQueryBuilder should = QueryBuilders.boolQuery().should(StringUtil.isEmpty(keyword) ? QueryBuilders.boolQuery() : QueryBuilders.wildcardQuery("bookName", "*" + keyword + "*"));
            boolQueryBuilder.must(should);
        }
        //书刊序号
        if (!StringUtil.isEmpty(serialNumber)){
            BoolQueryBuilder should = QueryBuilders.boolQuery().should(QueryBuilders.termQuery("serialNumber", serialNumber.trim()));
            boolQueryBuilder.must(should);
        }
        //编辑书
        BoolQueryBuilder should1 = QueryBuilders.boolQuery().should(QueryBuilders.termQuery("isAdviserBook",1));
        boolQueryBuilder.must(should1);
        //权益
        // BoolQueryBuilder should2 = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("rightsSettingId",0));
        // boolQueryBuilder.must(should2);
        Sort sort = new Sort(Sort.Direction.DESC, "lastModifiedDate", "bookId");
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage, sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
        return search;
    }

    @Override
    public Map<String, Object> getNotFoundBookId(String lastBookId) throws Exception {
        String lock = JedisClusterUtils.get(NOT_FOUND_BOOK_ID_SELECT_KEY);
        if(!StringUtil.isEmpty(lock)){
            throw new BizException(BizException.DB_DML_FAIL.getCode(), "请不要重复执行");
        }
        JedisClusterUtils.set(NOT_FOUND_BOOK_ID_SELECT_KEY, "lock", 3600);
        // 解除停止状态
        JedisClusterUtils.set(NOT_FOUND_BOOK_ID_STATUS_SELECT_KEY, "running", 3600);
        List<Long> notFoundBookIds = Lists.newArrayList();
        Integer pageNumber = 100;
        List<Long> esBookIds = null;
        List<Long> existsBookIds = null;

        String runStatus = "running";
        try {
            if(StringUtil.isEmpty(lastBookId)){
                lastBookId = "0";
            }
            Sort sort = new Sort(Sort.Direction.ASC, "bookId");
            while ("running".equalsIgnoreCase(runStatus)) {
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                boolQueryBuilder.must(QueryBuilders.rangeQuery("bookId").gt(lastBookId));
                boolQueryBuilder.queryName("bookId");
                PageRequest pageRequest = new PageRequest(0, 5000, sort);
                Page<String> map = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest).map(x -> x.getBookId());
                List<String> esContent = map.getContent();
                if (esContent.size() > 0) {
                    // 循环每次查询100条
                    for (int i = 0; i < (esContent.size() % pageNumber == 0 ? esContent.size()/pageNumber : (esContent.size()/pageNumber+1)); i++) {
                        esBookIds = esContent.stream().skip(i * pageNumber).limit(pageNumber).map(Long::valueOf).collect(Collectors.toList());
                        if(ListUtils.isEmpty(esBookIds)){
                            continue;
                        }
                        // 查询，并筛选出不存在的数据
                        existsBookIds = bookDao.getByIds(esBookIds);
                        Thread.sleep(1);
                        if(ListUtils.isEmpty(existsBookIds)){
                            if(ListUtils.isEmpty(esBookIds)){
                                continue;
                            }
                            notFoundBookIds.addAll(esBookIds);
                        } else {
                            esBookIds.removeAll(existsBookIds);
                            if(ListUtils.isEmpty(esBookIds)){
                                continue;
                            }
                            notFoundBookIds.addAll(esBookIds);
                        }
                    }
                    lastBookId = esContent.get(esContent.size()-1);
                } else {
                    runStatus = "complete";
                    break;
                }
                // 判断是否强制终止
                runStatus = JedisClusterUtils.get(NOT_FOUND_BOOK_ID_STATUS_SELECT_KEY);
            }
            return MapUtil.<String, Object>builder()
                    .put("runStatus", runStatus)
                    .put("lastBookId", lastBookId)
                    .put("notFoundBookIds", notFoundBookIds)
                    .put("msg", "stop".equalsIgnoreCase(runStatus) ? "执行被终止" : "执行完成")
                    .build();
        } catch (Exception e){
            LOGGER.info("getNotFoundBookId 执行出现异常，notFoundBookIds：" + notFoundBookIds.toString(), e);
            return MapUtil.<String, Object>builder()
                    .put("runStatus", runStatus)
                    .put("lastBookId", lastBookId)
                    .put("notFoundBookIds", notFoundBookIds)
                    .put("msg", "执行出现异常")
                    .build();
        } finally {
            // 移除执行锁
            JedisClusterUtils.del(NOT_FOUND_BOOK_ID_SELECT_KEY);
        }
    }

    @Override
    public void stopNotFoundBookId() {
        JedisClusterUtils.set(NOT_FOUND_BOOK_ID_STATUS_SELECT_KEY, "stop", 3600);
    }

    @Override
    public void updateTempletId() {
        try {
            WeWorkWebHookRobotUtils.sendTextMsg("e99da8d0-62b9-4352-a196-d1f20b01960f",
                    "开始同步BookAdviser分类信息", null, CollUtil.toList("13035118598"));
            final List<ESBookAndAdviser> bookList = new ArrayList<>();
            bookAdviserDao.syncTempletId4ES(resultContext -> {
                ESBookAndAdviser esBookAndAdviser = resultContext.getResultObject();
                bookList.add(esBookAndAdviser);
                if (bookList.size() == 1000) {
                    saveToES(bookList, resultContext.getResultCount());
                    bookList.clear();
                }
            });
            saveToES(bookList, bookList.size());
        } catch (Exception e) {
            WeWorkWebHookRobotUtils.sendTextMsg("e99da8d0-62b9-4352-a196-d1f20b01960f",
                    String.format("ESBookAdviser同步失败\nERR：%s", Arrays.toString(e.getStackTrace())),
                    null, CollUtil.toList("13035118598"));
        }
    }

    private void saveToES(List<ESBookAndAdviser> bookList, int resultCount) {
        if (CollUtil.isEmpty(bookList)) {
            return;
        }
        List<String> idList = bookList.stream().map(ESBookAndAdviser::getBookId).collect(Collectors.toList());
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termsQuery("bookId", idList));
        Iterable<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder);
        List<ESBookAndAdviser> list = new ArrayList<>();
        Map<String, ESBookAndAdviser> map = bookList.stream().filter(Objects::nonNull).filter(x -> Objects.nonNull(x.getBookId())).collect(Collectors.toMap(ESBookAndAdviser::getBookId, x -> x));
        for (ESBookAndAdviser bookAndAdviser : search) {
            ESBookAndAdviser esBookAndAdviser = map.get(bookAndAdviser.getBookId());
            if(Objects.isNull(esBookAndAdviser)) continue;
            bookAndAdviser.setTempletId(esBookAndAdviser.getTempletId());
            bookAndAdviser.setSecondTempletId(esBookAndAdviser.getSecondTempletId());
            bookAndAdviser.setThirdTempletId(esBookAndAdviser.getThirdTempletId());
            list.add(bookAndAdviser);
        }
        if (CollUtil.isNotEmpty(list)) {
            bookAndAdviserRepository.save(list);
            WeWorkWebHookRobotUtils.sendTextMsg("e99da8d0-62b9-4352-a196-d1f20b01960f",
                    String.format("ESBookAdviser同步中\n当前同步数量: %d\n存入ES数量：%d", resultCount, list.size()),
                    null, CollUtil.toList("13035118598"));
        }
    }

    @Override
    public Page<ESBookAndAdviser> getESBooksOrderByChose(String keyword, List<Long> choseBookIdList, Integer currentPage, Integer numPerPage) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("isBookDelete", 0));

        String number = (!StringUtil.isEmpty(keyword) && keyword.startsWith("BK") && keyword.length()>2)?keyword.substring(2):null;
        if (!StringUtil.isEmpty(number) && NumberUtil.isNumber(number)) {
            //书刊编号搜索
            BoolQueryBuilder bookIdBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("bookId", Long.valueOf(number)));
            boolQueryBuilder.must(bookIdBuilder);
        } else  {
            BoolQueryBuilder should = QueryBuilders.boolQuery()
                    .should(StringUtil.isEmpty(keyword) ? QueryBuilders.boolQuery() : QueryBuilders.wildcardQuery("bookName", "*" + keyword + "*"))
                    .should(StringUtil.isEmpty(keyword) ? QueryBuilders.boolQuery() : QueryBuilders.wildcardQuery("isbn", "*" + keyword + "*"));
            boolQueryBuilder.must(should);
        }
        Sort sort = new Sort(Sort.Direction.DESC, "lastModifiedDate", "bookId");
        PageRequest pageRequest = new PageRequest(currentPage, numPerPage, sort);
        Page<ESBookAndAdviser> search = bookAndAdviserRepository.search(boolQueryBuilder, pageRequest);
        return search;
    }
}
