package com.slodon.b2b2c.model.goods;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.slodon.b2b2c.core.config.DomainUrlUtil;
import com.slodon.b2b2c.core.constant.GoodsCategoryConst;
import com.slodon.b2b2c.core.constant.GoodsConst;
import com.slodon.b2b2c.core.constant.ImageSizeEnum;
import com.slodon.b2b2c.core.constant.PromotionConst;
import com.slodon.b2b2c.core.exception.MallException;
import com.slodon.b2b2c.core.response.PagerInfo;
import com.slodon.b2b2c.core.util.AssertUtil;
import com.slodon.b2b2c.core.util.FileUrlUtil;
import com.slodon.b2b2c.core.util.StringUtil;
import com.slodon.b2b2c.core.util.TimeUtil;
import com.slodon.b2b2c.dao.read.goods.*;
import com.slodon.b2b2c.dao.read.member.MemberFollowProductReadMapper;
import com.slodon.b2b2c.dao.read.seller.StoreInnerLabelReadMapper;
import com.slodon.b2b2c.dao.read.seller.StoreLabelBindGoodsReadMapper;
import com.slodon.b2b2c.enums.WebSiteConstant;
import com.slodon.b2b2c.goods.dto.GoodsRankListDTO;
import com.slodon.b2b2c.goods.dto.SearchConditionDTO;
import com.slodon.b2b2c.goods.example.*;
import com.slodon.b2b2c.goods.pojo.*;
import com.slodon.b2b2c.member.example.MemberFollowProductExample;
import com.slodon.b2b2c.member.pojo.Member;
import com.slodon.b2b2c.member.pojo.MemberFollowProduct;
import com.slodon.b2b2c.model.promotion.PromotionCommonModel;
import com.slodon.b2b2c.model.system.SettingModel;
import com.slodon.b2b2c.seller.example.StoreInnerLabelExample;
import com.slodon.b2b2c.seller.example.StoreLabelBindGoodsExample;
import com.slodon.b2b2c.seller.pojo.StoreInnerLabel;
import com.slodon.b2b2c.seller.pojo.StoreLabelBindGoods;
import com.slodon.b2b2c.system.pojo.Setting;
import com.slodon.b2b2c.vo.goods.GoodsListVO;
import com.slodon.b2b2c.goods.dto.SearchProductDTO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.alibaba.fastjson.serializer.SerializerFeature.MapSortField;
import static com.alibaba.fastjson.serializer.SerializerFeature.SortField;

@Component
@Slf4j
public class ESGoodsModel {

    @Resource
    private GoodsReadMapper goodsReadMapper;
    @Resource
    private GoodsCategoryReadMapper goodsCategoryReadMapper;
    @Resource
    private GoodsPromotionReadMapper goodsPromotionReadMapper;
    @Resource
    private GoodsBindAttributeValueReadMapper goodsBindAttributeValueReadMapper;
    @Resource
    private GoodsBindRankReadMapper goodsBindRankReadMapper;

    @Resource
    private StoreInnerLabelReadMapper storeInnerLabelReadMapper;
    @Resource
    private StoreLabelBindGoodsReadMapper storeLabelBindGoodsReadMapper;
    @Resource
    private MemberFollowProductReadMapper memberFollowProductReadMapper;
    @Resource
    private GoodsExtendModel goodsExtendModel;
    @Resource
    private PromotionCommonModel promotionCommonModel;
    @Resource
    private SettingModel settingModel;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 创建索引
     *
     * @param url
     * @param port
     * @param type job-定时更新，其他-初始化索引
     * @return
     */
    @SneakyThrows
    public boolean jobCreateIndexesES(String url, Integer port, String type, String webSite) {
        //es索引上次更新的时间
        String esIndexUpdateTime = stringRedisTemplate.opsForValue().get("es_index_update_time");
        if (WebSiteConstant.MEMBER_DISTRIBUTOR.equals(webSite)) {
            esIndexUpdateTime = stringRedisTemplate.opsForValue().get("es_index_update_time" + "_" + webSite);
        }

        GoodsExample example = new GoodsExample();
        example.setWebSite(webSite);
        example.setState(GoodsConst.GOODS_STATE_UPPER);
        if ("job".equals(type)) {
            example.setUpdateTimeAfter(TimeUtil.strToDate(esIndexUpdateTime));
        }
        List<Goods> addList = goodsReadMapper.listByExample(example);

        if (!CollectionUtils.isEmpty(addList)) {
            List<SearchProductDTO> addGoodsList = this.getSearchProductVOs(addList);
            BulkResponse response = this.createIndex(url, port, addGoodsList, webSite);
            if (response.hasFailures()) {
                //索引插入数据失败
                log.error(response.buildFailureMessage());
            }
        }

        if ("job".equals(type)) {
            //下架状态的商品，执行删除索引记录操作
            example.setState(null);
            example.setWebSite(webSite);
            example.setStateNotEquals(GoodsConst.GOODS_STATE_UPPER);
            //需要删除索引记录的商品列表
            List<Goods> deleteList = goodsReadMapper.listByExample(example);
            if (!CollectionUtils.isEmpty(deleteList)) {
                this.deleteByQuery(url, port, deleteList, webSite);
            }
        }

        //更新es时间
        String updateTime = TimeUtil.getDateTimeString(new Date());
        Setting setting = new Setting();
        setting.setName("es_index_update_time");
        if (WebSiteConstant.MEMBER_DISTRIBUTOR.equals(webSite)) {
            setting.setName("es_index_update_time" + "_" + webSite);
        }
        setting.setValue(updateTime);
        settingModel.updateSetting(setting);
        //更新redis里的配置
        stringRedisTemplate.opsForValue().set("es_index_update_time", updateTime);
        if (WebSiteConstant.MEMBER_DISTRIBUTOR.equals(webSite)) {
            stringRedisTemplate.opsForValue().set("es_index_update_time" + "_" + webSite, updateTime);
        }
        return true;
    }

    /**
     * 组装SearchProductVO对象，用来创建索引
     *
     * @param goodsList
     * @return
     */
    private List<SearchProductDTO> getSearchProductVOs(List<Goods> goodsList) {
        List<SearchProductDTO> searchProductDTOS = new ArrayList<>();
        SearchProductDTO searchProductDTO;
        GoodsCategoryExample goodsCategoryExample = new GoodsCategoryExample();
        goodsCategoryExample.setGrade(GoodsCategoryConst.CATEGORY_GRADE_3);
        List<GoodsCategory> categoryList = goodsCategoryReadMapper.listByExample(goodsCategoryExample);
        List<GoodsBindAttributeValue> bindAttributeValueListAll = goodsBindAttributeValueReadMapper.listByExample(new GoodsBindAttributeValueExample());
        List<GoodsExtend> goodsExtendList = goodsExtendModel.listByExample(new GoodsExtendExample());
        List<StoreLabelBindGoods> labelBindGoodsListAll = storeLabelBindGoodsReadMapper.listByExample(new StoreLabelBindGoodsExample());
        List<StoreInnerLabel> storeInnerLabelList = storeInnerLabelReadMapper.listByExample(new StoreInnerLabelExample());
        List<GoodsRankListDTO> rankListAll = goodsBindRankReadMapper.getGoodsRankList(new GoodsBindRankExample());
        GoodsPromotionExample promotionExample = new GoodsPromotionExample();
        promotionExample.setIsEffective(PromotionConst.IS_EFFECTIVE_YES);
        promotionExample.setStartTimeBefore(new Date());
        promotionExample.setEndTimeAfter(new Date());
        //一级活动，绑定商品
        promotionExample.setBindType(PromotionConst.BIND_TYPE_1);
        List<GoodsPromotion> listAll = goodsPromotionReadMapper.listByExample(promotionExample);
        promotionExample.setBindType(PromotionConst.BIND_TYPE_2);
        List<GoodsPromotion> secondListAll = goodsPromotionReadMapper.listByExample(promotionExample);
        promotionExample.setBindType(PromotionConst.BIND_TYPE_3);
        List<GoodsPromotion> thirdListAll = goodsPromotionReadMapper.listByExample(promotionExample);
        for (Goods goods : goodsList) {
            searchProductDTO = new SearchProductDTO();
            searchProductDTO.setGoodsId(goods.getGoodsId().toString());
            searchProductDTO.setWebSite(goods.getWebSite());
            searchProductDTO.setGoodsNameCn(goods.getGoodsNameCn());
            searchProductDTO.setGoodsName(goods.getGoodsName());
            searchProductDTO.setBrandId(StringUtil.isNullOrZero(goods.getBrandId()) ? "" : goods.getBrandId().toString());
            searchProductDTO.setBrandName(goods.getBrandName());
            searchProductDTO.setStoreId(goods.getStoreId().toString());
            searchProductDTO.setStoreName(goods.getStoreName());
            searchProductDTO.setCategoryId1(goods.getCategoryId1().toString());
            searchProductDTO.setCategoryId2(goods.getCategoryId2().toString());
            searchProductDTO.setCategoryId3(goods.getCategoryId3().toString());
            searchProductDTO.setGoodsMoney(goods.getGoodsMoney());
            searchProductDTO.setGoodsSummary(goods.getGoodsSummary());
            GoodsCategory goodsCategory = categoryList.stream().filter(category -> category.getCategoryId().equals(goods.getCategoryId3())).findFirst().orElse(null);
            if (goodsCategory != null) {
                searchProductDTO.setCategoryName(goodsCategory.getCategoryName());
            }
            searchProductDTO.setContent(goods.getGoodsBrief());
            searchProductDTO.setDefaultProductId(goods.getDefaultProductId());

            searchProductDTO.setGoodsImage(goods.getMainImage());
            searchProductDTO.setGoodsPrice(goods.getGoodsPrice());
            searchProductDTO.setMarketPrice(goods.getMarketPrice());
            searchProductDTO.setGoodsStock(goods.getGoodsStock());
            searchProductDTO.setCommentsNumber(goods.getCommentNumber());

            //商品的系统属性
            List<GoodsBindAttributeValue> bindAttributeValueList = bindAttributeValueListAll.stream().filter(bindAttributeValue -> bindAttributeValue.getGoodsId().equals(goods.getGoodsId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(bindAttributeValueList)) {
                StringBuffer attributeInfo = new StringBuffer();
                bindAttributeValueList.forEach(bindAttributeValue -> {
                    attributeInfo.append(" ").append(bindAttributeValue.getAttributeValueId());
                });
                searchProductDTO.setAttributeInfo(attributeInfo.substring(1));
            }

            searchProductDTO.setSalesNum(goods.getVirtualSales() + goods.getActualSales());
            //商品扩展信息
            GoodsExtend goodsExtend = goodsExtendList.stream().filter(e -> e.getGoodsId().equals(goods.getGoodsId())).findFirst().orElse(null);
            if (goodsExtend != null) {
                searchProductDTO.setClickNum(goodsExtend.getClickNumber());
                searchProductDTO.setFollowNum(goodsExtend.getFollowNumber());
            }
            searchProductDTO.setStoreIsRecommend(goods.getStoreIsRecommend());
            searchProductDTO.setIsSelf(goods.getIsSelf());


            //查询店铺分类绑定商品表
            List<StoreLabelBindGoods> labelBindGoodsList = labelBindGoodsListAll.stream().filter(bindGoods -> bindGoods.getGoodsId().equals(goods.getGoodsId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(labelBindGoodsList)) {
                //商家内部一级分类ID
                StringBuilder storeInnerLabelId1 = new StringBuilder();
                //商家内部二级分类ID
                StringBuilder storeInnerLabelId2 = new StringBuilder();
                for (StoreLabelBindGoods labelBindGoods : labelBindGoodsList) {
                    StoreInnerLabel storeInnerLabel = storeInnerLabelList.stream().filter(innerLabel -> innerLabel.getInnerLabelId().equals(labelBindGoods.getInnerLabelId())).findFirst().orElse(null);
                    if (storeInnerLabel != null) {
                        if (storeInnerLabel.getParentInnerLabelId() == 0) {
                            //商品绑定的是一级内部分类
                            storeInnerLabelId1.append(" ").append(storeInnerLabel.getInnerLabelId());
                        } else {
                            //商品绑定的是二级内部分类
                            storeInnerLabelId2.append(" ").append(storeInnerLabel.getInnerLabelId());
                            storeInnerLabelId1.append(" ").append(storeInnerLabel.getParentInnerLabelId());
                        }
                    }
                }
                searchProductDTO.setStoreInnerLabelId1(storeInnerLabelId1.substring(1));
                if (!StringUtils.isEmpty(storeInnerLabelId2.toString())) {
                    searchProductDTO.setStoreInnerLabelId2(storeInnerLabelId2.substring(1));
                }
            }
            searchProductDTO.setOnlineTime(goods.getOnlineTime().getTime());

            //商品排行查询
            GoodsBindRankExample goodsBindRankExample = new GoodsBindRankExample();
            goodsBindRankExample.setGoodsId(goods.getGoodsId());
            List<GoodsRankListDTO> rankList = rankListAll.stream().filter(rank -> rank.getGoodsId().equals(goods.getGoodsId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(rankList)) {
                GoodsRankListDTO goodsRank = rankList.get(0);
                searchProductDTO.setRankId(goodsRank.getRankId().toString());
                searchProductDTO.setGoodsRank(goodsRank.getRankName() + "第" + goodsRank.getGoodsRank() + "名");
            } else {
                searchProductDTO.setRankId(null);
                searchProductDTO.setGoodsRank(null);
            }

            //商品参与的活动
            List<GoodsPromotion> list = listAll.stream().filter(goodsPromotion -> goodsPromotion.getGoodsId().equals(goods.getGoodsId())).collect(Collectors.toList());
            //二级活动，绑定店铺
            list.addAll(secondListAll.stream().filter(goodsPromotion -> goodsPromotion.getStoreId().equals(goods.getStoreId())).collect(Collectors.toList()));
            //三级活动，绑定三级分类
            list.addAll(thirdListAll.stream().filter(goodsPromotion -> goodsPromotion.getGoodsCategoryId3().equals(goods.getCategoryId3())).collect(Collectors.toList()));
            List<GoodsListVO.PromotionVO> vos = new ArrayList<>();
            //根据活动名称过滤重复
            Set<String> set = new HashSet<>();
            if (!CollectionUtils.isEmpty(list)) {
                for (GoodsPromotion goodsActivity : list) {
                    String promotionName = promotionCommonModel.getPromotionName(goodsActivity.getPromotionType());
                    if (!set.add(promotionName)) {
                        continue;
                    }
                    GoodsListVO.PromotionVO promotionVO = new GoodsListVO.PromotionVO();
                    promotionVO.setGoodsPromotionId(goodsActivity.getGoodsPromotionId());
                    promotionVO.setPromotionId(goodsActivity.getPromotionId());
                    promotionVO.setPromotionName(promotionName);
                    promotionVO.setPromotionType(goodsActivity.getPromotionType());
                    promotionVO.setPromotionGrade(goodsActivity.getPromotionGrade());
                    vos.add(promotionVO);
                    //如果是商品活动，查询活动价格
                    if (goodsActivity.getBindType() == PromotionConst.BIND_TYPE_1) {
                        BigDecimal promotionPrice = promotionCommonModel.getPromotionPrice(goodsActivity.getPromotionType(), goodsActivity.getPromotionId(), goodsActivity.getProductId());
                        if (promotionPrice.compareTo(BigDecimal.ZERO) > 0) {
                            searchProductDTO.setGoodsPrice(promotionPrice);
                        }
                    }
                }
            }
            if (!CollectionUtils.isEmpty(vos)) {
                searchProductDTO.setActivityInfo(JSON.toJSONString(vos, SortField, MapSortField));
            }
            //获取商品排序权重
            String saleWeightStr = stringRedisTemplate.opsForValue().get("goods_sort_weight_sale");
            String viewWeightStr = stringRedisTemplate.opsForValue().get("goods_sort_weight_view");
            BigDecimal saleWeight = StringUtils.isEmpty(saleWeightStr) ? new BigDecimal(70) : new BigDecimal(saleWeightStr);
            BigDecimal viewWeight = StringUtils.isEmpty(viewWeightStr) ? new BigDecimal(30) : new BigDecimal(viewWeightStr);
            searchProductDTO.setSortWeight(new BigDecimal(searchProductDTO.getSalesNum()).multiply(saleWeight).add(new BigDecimal(searchProductDTO.getClickNum()).multiply(viewWeight)));
            searchProductDTOS.add(searchProductDTO);
        }
        return searchProductDTOS;
    }

    /**
     * 创建es索引
     *
     * @param url
     * @param port
     * @param goodsList
     * @throws IOException
     */
    private BulkResponse createIndex(String url, Integer port, List<SearchProductDTO> goodsList, String webSite) throws IOException {
        RestHighLevelClient client = null;
        BulkResponse response = null;
        try {
            client = getESClient();
            boolean indexExist = client.indices().exists(new GetIndexRequest(DomainUrlUtil.ES_INDEX_NAME + "_" + webSite), RequestOptions.DEFAULT);
            if (!indexExist) {
                //索引不存在，创建索引，设置索引分词格式
                CreateIndexRequest createIndexRequest = new CreateIndexRequest(DomainUrlUtil.ES_INDEX_NAME + "_" + webSite);
                createIndexRequest.settings(Settings.builder()
                        .put("index.analysis.analyzer.default.type", "ik_max_word")
                        .put("index.analysis.analyzer.default_search.type", "ik_smart"));
                client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
            BulkRequest bulkRequest = new BulkRequest();
            //插入数据
            for (SearchProductDTO g : goodsList) {
                bulkRequest.add(new IndexRequest(DomainUrlUtil.ES_INDEX_NAME + "_" + webSite).id(g.getGoodsId()).source(JSONObject.toJSONString(g), XContentType.JSON));
            }
            response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            log.error("创建es索引异常", e);
        } finally {
            if (client != null) {
                client.close();
            }
        }
        return response;
    }

    /**
     * 删除es索引记录
     *
     * @param url
     * @param port
     * @param goodsList
     * @throws IOException
     */
    private void deleteByQuery(String url, Integer port, List<Goods> goodsList, String webSite) throws IOException {
        RestHighLevelClient client = null;
        try {
            client = getESClient();
            BulkRequest bulkRequest = new BulkRequest();
            for (Goods g : goodsList) {
                bulkRequest.add(new DeleteRequest(DomainUrlUtil.ES_INDEX_NAME + "_" + webSite, g.getGoodsId().toString()));
            }
            client.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            log.error("删除es索引异常", e);
        } finally {
            if (client != null) {
                client.close();
            }
        }
    }

    //region es搜索商品

    /**
     * 查询满足条件的商品对应的店铺id
     *
     * @param categoryId 分类id
     * @param keyword    关键词
     * @return 店铺id集合
     */
    public Set<Long> searchStoreIdsByEs(Integer categoryId, String keyword) {
        Set<Long> result = new HashSet<>();//返回数据
        RestHighLevelClient client = getESClient();//获取es客户端
        SearchRequest searchRequest = new SearchRequest(DomainUrlUtil.ES_INDEX_NAME);

        //构造查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        SearchConditionDTO qc = new SearchConditionDTO();
        if (!StringUtil.isNullOrZero(categoryId)) {
            qc.setCategoryIds(categoryId + "");
            boolQueryBuilder.must(this.searchIndexAssembling4cate(qc));
        }
        if (!StringUtils.isEmpty(keyword)) {
            qc.setKeyword(keyword);
            boolQueryBuilder.must(this.searchIndexAssembling4keyword(qc));
        }
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.size(0);
        //聚合查询，将所有符合条件的店铺id放入list
        searchSourceBuilder.aggregation(AggregationBuilders.filter("query", boolQueryBuilder)
                .subAggregation(AggregationBuilders.terms("list").field(SearchProductDTO.STORE_ID_ + ".keyword").size(1000000)));
        searchRequest.source(searchSourceBuilder);
        try {
            //解析查询结果，拿到店铺id list
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            ParsedFilter filter = (ParsedFilter) searchResponse.getAggregations().asList().get(0);//过滤后的结果集
            ParsedStringTerms terms = (ParsedStringTerms) filter.getAggregations().asList().get(0);//聚合属性值id的结果集
            terms.getBuckets().forEach(buckets -> {
                String key = (String) buckets.getKey();
                result.add(Long.valueOf(key));
            });
        } catch (Exception e) {
            log.error("es连接异常:", e);
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                log.error("es连接异常:", e);
            }
        }
        return result;
    }

    /**
     * 根据条件获取店铺内的商品
     *
     * @param storeIds   店铺ids
     * @param categoryId 分类id
     * @param keyword    关键词
     * @return 店铺商品
     */
    public List<SearchProductDTO> getStoreGoodsList(String storeIds, Integer categoryId, String keyword) {
        List<SearchProductDTO> result = new ArrayList<>();
        RestHighLevelClient client = getESClient();
        SearchRequest searchRequest = new SearchRequest(DomainUrlUtil.ES_INDEX_NAME);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        SearchConditionDTO qc = new SearchConditionDTO();

        //构造查询条件
        BoolQueryBuilder storeQuery = QueryBuilders.boolQuery();
        for (String storeId : storeIds.split(",")) {
            storeQuery.should(QueryBuilders.matchQuery(SearchProductDTO.STORE_ID_, storeId));
        }
        boolQueryBuilder.must(storeQuery);
        if (!StringUtil.isNullOrZero(categoryId)) {
            qc.setCategoryIds(categoryId + "");
            boolQueryBuilder.must(this.searchIndexAssembling4cate(qc));
        }
        if (!StringUtils.isEmpty(keyword)) {
            qc.setKeyword(keyword);
            boolQueryBuilder.must(this.searchIndexAssembling4keyword(qc));
        }
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.trackTotalHits(true);//解除es最大查询限制，默认限制是10000
        searchSourceBuilder.size(10000);//es默认最多查询10000条数据，如果要查更多，需要在创建索引时增加配置
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            for (SearchHit hit : hits.getHits()) {
                result.add(JSON.parseObject(hit.getSourceAsString(), SearchProductDTO.class));
            }
        } catch (Exception e) {
            log.error("es连接异常:", e);
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                log.error("es连接异常:", e);
            }
        }
        return result;
    }

    /**
     * 获取默认三级分类id
     * 获取规则：按条件查询前500条数据中，三级分类最多的即为默认分类
     *
     * @param qc 查询条件
     * @return 默认三级分类id
     */
    public Integer getDefaultCategoryId(SearchConditionDTO qc) {
        RestHighLevelClient client = getESClient();
        SearchRequest searchRequest = new SearchRequest(DomainUrlUtil.ES_INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = this.getCountSearchSourceBuilder(qc);
        //查询前500条符合条件的记录，聚合获取条数最多的三级分类id
        searchSourceBuilder.size(500);
        searchSourceBuilder.aggregation(AggregationBuilders.terms("list").field(SearchProductDTO.CATEGORY_ID3_ + ".keyword"));
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            ParsedStringTerms aggregation = searchResponse.getAggregations().get("list");
            List<? extends Terms.Bucket> buckets = aggregation.getBuckets();
            if (CollectionUtils.isEmpty(buckets)) {
                return null;
            }
            long key = Long.parseLong(buckets.get(0).getKeyAsString());
            return Math.toIntExact(key);
        } catch (Exception e) {
            log.error("es连接异常:", e);
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                log.error("es连接异常:", e);
            }
        }
        return null;
    }

    /**
     * es搜索商品
     *
     * @param qc
     * @param pager
     * @return
     */
    public List<GoodsListVO> searchGoodsByES(SearchConditionDTO qc, PagerInfo pager, Member member) {
        List<GoodsListVO> goodsList = new ArrayList<>();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //搜索条件构造
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (!StringUtil.isEmpty(qc.getGoodsIds())) {
            //商品id搜索
            boolQueryBuilder.must(this.searchIndexAssembling4goods(qc));
        }
        if (!StringUtil.isEmpty(qc.getCategoryIds())) {
            //分类id搜索
            boolQueryBuilder.must(this.searchIndexAssembling4cate(qc));
        }
        if (!StringUtils.isEmpty(qc.getAttributeInfo())) {
            //检索属性搜索
            boolQueryBuilder.must(this.searchIndexAssembling4Attribute(qc));
        }
        if (!StringUtil.isEmpty(qc.getBrandIds())) {
            //品牌
            boolQueryBuilder.must(this.searchIndexAssembling4brand(qc));
        }
        if (!StringUtil.isNullOrZero(qc.getStoreId())) {
            //店铺
            boolQueryBuilder.must(this.searchIndexAssembling4Store(qc));
        }
        if (!StringUtil.isNullOrZero(qc.getStoreInnerLabelId())) {
            //店铺内部分类
            boolQueryBuilder.must(this.searchIndexAssembling4StoreInnerLabel(qc));
        }
        if (!StringUtils.isEmpty(qc.getKeyword())) {
            boolQueryBuilder.must(this.searchIndexAssembling4keyword(qc));
            //搜索结果高亮设置
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field(SearchProductDTO.GOODS_NAME_);
            highlightTitle.preTags("<font color=\"red\">");
            highlightTitle.postTags("</font>");
            highlightTitle.highlighterType("unified");
            highlightBuilder.field(highlightTitle);
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        if (qc.getLowPrice() != null) {
            //价格最小值
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_PRICE_).gte(qc.getLowPrice()));
        }
        if (qc.getHighPrice() != null) {
            //价格最大值
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_PRICE_).lte(qc.getHighPrice()));
        }

        if (Optional.of(qc).map(SearchConditionDTO::getIsSelf).orElse(0) == 1) {
            //是否自营
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchProductDTO.IS_SELF_, qc.getIsSelf().toString()));
        }
        if (Optional.of(qc).map(SearchConditionDTO::getStore).orElse(0) == 1) {
            //是否有货
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_STOCK_).gte(1));
        }

        searchSourceBuilder.query(boolQueryBuilder);
        //分页
        if (pager != null) {
            searchSourceBuilder.from(pager.getStart());
            searchSourceBuilder.size(pager.getPageSize());
        }

        int sort = Optional.of(qc).map(SearchConditionDTO::getSort).orElse(0);
        // es 排序 0:默认排序(按照权重)；1销量；2评论；3价格从低到高；4、价格从高到低；5、人气从高到低；6、收藏数从高到低；7、店铺推荐
        if (sort == 0) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.SORT_WEIGHT_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 1) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.SALES_NUM_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 2) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.COMMENTS_NUMBER_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 3) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.GOODS_PRICE_).unmappedType("long").order(SortOrder.ASC));
        } else if (sort == 4) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.GOODS_PRICE_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 5) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.CLICK_NUM_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 6) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.FOLLOW_NUM_).unmappedType("long").order(SortOrder.DESC));
        } else if (sort == 7) {
            searchSourceBuilder.sort(new FieldSortBuilder(SearchProductDTO.STORE_IS_RECOMMEND_).unmappedType("long").order(SortOrder.DESC));
            searchSourceBuilder.query(boolQueryBuilder.must(QueryBuilders.termQuery(SearchProductDTO.STORE_IS_RECOMMEND_, 1)));
        }

        //查询商品
        RestHighLevelClient client = getESClient();
        try {
            SearchRequest searchRequest = new SearchRequest(DomainUrlUtil.ES_INDEX_NAME + "_" + qc.getWebSite());
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            if (pager != null) {
                pager.setRowsCount((int) hits.getTotalHits().value);
            }
            for (SearchHit hit : hits.getHits()) {
                JSONObject jsonObject = JSONObject.parseObject(hit.getSourceAsString());

                //取高亮结果
                Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
                HighlightField highlightField = highlightFieldMap.get(SearchProductDTO.GOODS_NAME_);
                String goodsName = !StringUtils.isEmpty(highlightField) && !StringUtils.isEmpty(highlightField.fragments())
                        ? highlightField.fragments()[0].string()
                        : jsonObject.getString(SearchProductDTO.GOODS_NAME_);
                Long goodsId = Long.valueOf(jsonObject.getString(SearchProductDTO.ID_));
                String goodsDefaultName = jsonObject.getString(SearchProductDTO.GOODS_NAME_);
                Long storeId = Long.valueOf(jsonObject.getString(SearchProductDTO.STORE_ID_));
                Integer categoryId3 = new Integer(jsonObject.getString(SearchProductDTO.CATEGORY_ID3_));
                Integer categoryId2 = new Integer(jsonObject.getString(SearchProductDTO.CATEGORY_ID2_));
                Integer categoryId1 = new Integer(jsonObject.getString(SearchProductDTO.CATEGORY_ID1_));
                Integer commentsNumber = new Integer(jsonObject.getString(SearchProductDTO.COMMENTS_NUMBER_));
                String goodsMoney = jsonObject.getString(SearchProductDTO.GOODS_MONEY_);
                GoodsListVO vo = new GoodsListVO();
                vo.setGoodsId(goodsId);
                vo.setGoodsName(goodsName);
                vo.setGoodsDefaultName(goodsDefaultName);
                vo.setGoodsImage(FileUrlUtil.getFileUrl(jsonObject.getString(SearchProductDTO.GOODS_IMAGE_), ImageSizeEnum._300x300));
                vo.setGoodsPrice(new BigDecimal(jsonObject.getString(SearchProductDTO.GOODS_PRICE_)));
                vo.setMarketPrice(jsonObject.getString(SearchProductDTO.MARKET_PRICE_) == null ? null : new BigDecimal(jsonObject.getString(SearchProductDTO.MARKET_PRICE_)));
                vo.setGoodsBrief(jsonObject.getString(SearchProductDTO.CONTENT_));
                vo.setCommentsNumber(commentsNumber);
                vo.setDefaultProductId(jsonObject.getLong(SearchProductDTO.DEFAULT_PRODUCT_ID_));
                vo.setCategoryId3(categoryId3);
                vo.setCategoryId2(categoryId2);
                vo.setCategoryId1(categoryId1);
                vo.setStoreId(storeId);
                vo.setGoodsMoney(goodsMoney);
                vo.setStoreName(jsonObject.getString(SearchProductDTO.STORE_NAME_));
                if (!StringUtil.isEmpty(jsonObject.getString(SearchProductDTO.STORE_INNER_LABEL_ID2_))) {
                    for (String storeInnerLabelId : jsonObject.getString(SearchProductDTO.STORE_INNER_LABEL_ID2_).split(" ")) {
                        vo.getStoreCategoryId().add(Integer.valueOf(storeInnerLabelId));
                    }
                }
                vo.setSaleNum(new Integer(jsonObject.getString(SearchProductDTO.SALES_NUM_)));
                vo.setIsOwnShop(new Integer(jsonObject.getString(SearchProductDTO.IS_SELF_)));
                //活动信息
                String activityInfo = jsonObject.getString(SearchProductDTO.ACTIVITY_INFO_);
                if (!StringUtil.isEmpty(activityInfo)) {
                    List<GoodsListVO.PromotionVO> activityList = JSONArray.parseArray(activityInfo, GoodsListVO.PromotionVO.class);
                    vo.setActivityList(activityList);
                }
                //是否收藏商品
                vo.setIsFollowGoods(false);
                if (member != null && !StringUtil.isNullOrZero(member.getMemberId())) {
                    MemberFollowProductExample followProductExample = new MemberFollowProductExample();
                    followProductExample.setGoodsId(goodsId);
                    followProductExample.setMemberId(member.getMemberId());
                    List<MemberFollowProduct> followGoodsList = memberFollowProductReadMapper.listByExample(followProductExample);
                    if (!CollectionUtils.isEmpty(followGoodsList)) {
                        vo.setIsFollowGoods(true);
                    }
                }
                vo.setRankId(jsonObject.getString(SearchProductDTO.RANK_ID_) == null ? null : new Integer(jsonObject.getString(SearchProductDTO.RANK_ID_)));
                vo.setGoodsRank(jsonObject.getString(SearchProductDTO.GOODS_RANK_));
                goodsList.add(vo);
            }
        } catch (Exception e) {
            log.error("es连接异常:", e);
        } finally {
            try {
                client.close();
            } catch (IOException e) {
                log.error("es连接异常:", e);
            }
        }
        return goodsList;
    }

    /**
     * 构造查询数量的搜索资源
     *
     * @param qc 搜索参数
     * @return 搜索资源对象
     */
    private SearchSourceBuilder getCountSearchSourceBuilder(SearchConditionDTO qc) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //搜索条件构造
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (!StringUtil.isEmpty(qc.getGoodsIds())) {
            //商品id搜索
            boolQueryBuilder.must(this.searchIndexAssembling4goods(qc));
        }
        if (!StringUtil.isEmpty(qc.getCategoryIds())) {
            //分类id搜索
            boolQueryBuilder.must(this.searchIndexAssembling4cate(qc));
        }
        if (!StringUtils.isEmpty(qc.getAttributeInfo())) {
            //检索属性搜索
            boolQueryBuilder.must(this.searchIndexAssembling4Attribute(qc));
        }
        if (!StringUtil.isEmpty(qc.getBrandIds())) {
            //品牌
            boolQueryBuilder.must(this.searchIndexAssembling4brand(qc));
        }
        if (!StringUtil.isNullOrZero(qc.getStoreId())) {
            //店铺
            boolQueryBuilder.must(this.searchIndexAssembling4Store(qc));
        }
        if (!StringUtil.isNullOrZero(qc.getStoreInnerLabelId())) {
            //店铺内部分类
            boolQueryBuilder.must(this.searchIndexAssembling4StoreInnerLabel(qc));
        }
        if (!StringUtils.isEmpty(qc.getKeyword())) {
            boolQueryBuilder.must(this.searchIndexAssembling4keyword(qc));
        }
        if (qc.getLowPrice() != null) {
            //价格最小值
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_PRICE_).gte(qc.getLowPrice()));
        }
        if (qc.getHighPrice() != null) {
            //价格最大值
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_PRICE_).lte(qc.getHighPrice()));
        }

        if (Optional.of(qc).map(SearchConditionDTO::getIsSelf).orElse(0) == 1) {
            //是否自营
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchProductDTO.IS_SELF_, qc.getIsSelf().toString()));
        }
        if (Optional.of(qc).map(SearchConditionDTO::getStore).orElse(0) == 1) {
            //是否有货
            boolQueryBuilder.must(QueryBuilders.rangeQuery(SearchProductDTO.GOODS_STOCK_).gte(1));
        }

        searchSourceBuilder.query(boolQueryBuilder);
        return searchSourceBuilder;
    }

    /**
     * 创建es连接
     *
     * @return
     */
    private RestHighLevelClient getESClient() {
        //阿里云Elasticsearch集群需要basic auth验证。
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        //访问用户名和密码为您创建阿里云Elasticsearch实例时设置的用户名和密码，也是Kibana控制台的登录用户名和密码。
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(DomainUrlUtil.SLD_ES_USERNAME, DomainUrlUtil.SLD_ES_PASSWORD));
        return new RestHighLevelClient(RestClient.builder(new HttpHost(DomainUrlUtil.SLD_ES_URL, DomainUrlUtil.SLD_ES_PORT, "http")).setHttpClientConfigCallback(
                httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
        ));
    }

    /**
     * 商品id的搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4goods(SearchConditionDTO qc) {
        //商品ID
        String[] strings = qc.getGoodsIds().split(",");
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        for (String goodsId : strings) {
            queryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.ID_, goodsId));
        }
        return queryBuilder;
    }

    /**
     * 商品分类页的搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4cate(SearchConditionDTO qc) {
        //商品ID
        String[] strings = qc.getCategoryIds().split(",");
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        for (String categoryId : strings) {
            //查询分类
            GoodsCategory goodsCategory = goodsCategoryReadMapper.getByPrimaryKey(categoryId);
            AssertUtil.notNull(goodsCategory, "分类信息有误");

            //根据分类等级构造查询条件
            switch (goodsCategory.getGrade()) {
                case GoodsCategoryConst.CATEGORY_GRADE_1://一级分类
                    queryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.CATEGORY_ID1_, categoryId));
                    break;
                case GoodsCategoryConst.CATEGORY_GRADE_2://二级分类
                    queryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.CATEGORY_ID2_, categoryId));
                    break;
                case GoodsCategoryConst.CATEGORY_GRADE_3://三级分类
                    queryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.CATEGORY_ID3_, categoryId));
                    break;
                default:
                    throw new MallException("分类信息有误");
            }
        }
        return queryBuilder;
    }

    /**
     * 商品检索属性搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4Attribute(SearchConditionDTO qc) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //查询条件中包含属性信息
        //属性名称_属性值 的数组
        String[] attributes = qc.getAttributeInfo().split(",");
        for (String attribute : attributes) {
            //完全匹配
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(SearchProductDTO.ATTRIBUTE_INFO_, attribute));

        }
        return boolQueryBuilder;
    }

    /**
     * 品牌页的搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4brand(SearchConditionDTO qc) {
        // 品牌ID
        String[] strings = qc.getBrandIds().split(",");
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        for (String brandIdStr : strings) {
            queryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.BRAND_ID_, brandIdStr));
        }

        return queryBuilder;
    }

    /**
     * 店铺的搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4Store(SearchConditionDTO qc) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 店铺ID
        boolQueryBuilder.must(QueryBuilders.matchQuery(SearchProductDTO.STORE_ID_, qc.getStoreId().toString()));

        return boolQueryBuilder;
    }

    /**
     * 店铺内部分类搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4StoreInnerLabel(SearchConditionDTO qc) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 店铺分类
        // 店铺分类ID
        StoreInnerLabel storeInnerLabel = storeInnerLabelReadMapper.getByPrimaryKey(qc.getStoreInnerLabelId());
        AssertUtil.notNull(storeInnerLabel, "店铺内部分类不存在");
        if (storeInnerLabel.getParentInnerLabelId() == 0) {
            //一级分类
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchProductDTO.STORE_INNER_LABEL_ID1_, qc.getStoreInnerLabelId().toString()));
        } else {
            //二级分类
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchProductDTO.STORE_INNER_LABEL_ID2_, qc.getStoreInnerLabelId().toString()));
        }
        return boolQueryBuilder;
    }

    /**
     * 搜索页的搜索条件的拼装
     *
     * @param qc
     * @return
     */
    private BoolQueryBuilder searchIndexAssembling4keyword(SearchConditionDTO qc) {
        String keyword = qc.getKeyword();
        keyword = StringUtil.stringFilterSpecial(keyword);

        return searchIndexAssembling(keyword);
    }

    /**
     * 搜索条件的拼装
     *
     * @param searchKeyword
     * @return
     */
    public BoolQueryBuilder searchIndexAssembling(String searchKeyword) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.CONTENT_, searchKeyword).minimumShouldMatch("-49%"));
        boolQueryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.GOODS_NAME_, searchKeyword).minimumShouldMatch("-49%"));
        boolQueryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.BRAND_NAME_, searchKeyword).minimumShouldMatch("-49%"));
        boolQueryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.STORE_NAME_, searchKeyword).minimumShouldMatch("-49%"));
        boolQueryBuilder.should(QueryBuilders.matchQuery(SearchProductDTO.CATEGORY_NAME_, searchKeyword).minimumShouldMatch("-49%"));
        return boolQueryBuilder;
    }
    //endregion es搜索商品
}
