|
|
package com.yoho.search.service.scene;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
|
|
import org.elasticsearch.index.query.QueryBuilders;
|
|
|
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
|
|
import org.elasticsearch.search.aggregations.Aggregation;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.yoho.search.base.utils.ConvertUtils;
|
|
|
import com.yoho.search.base.utils.ISearchConstants;
|
|
|
import com.yoho.search.base.utils.ProductIndexEsField;
|
|
|
import com.yoho.search.common.cache.SearchCacheMatchLogger;
|
|
|
import com.yoho.search.common.cache.model.SearchCache;
|
|
|
import com.yoho.search.core.es.agg.IAggregation;
|
|
|
import com.yoho.search.core.es.model.SearchParam;
|
|
|
import com.yoho.search.core.es.model.SearchResult;
|
|
|
import com.yoho.search.models.PromotionConditions;
|
|
|
import com.yoho.search.models.PromotionParam;
|
|
|
import com.yoho.search.models.SearchApiResult;
|
|
|
import com.yoho.search.service.aggregations.impls.AggregationFactoryService;
|
|
|
import com.yoho.search.service.base.SearchCommonService;
|
|
|
import com.yoho.search.service.base.index.ProductIndexBaseService;
|
|
|
import com.yoho.search.service.helper.SearchAfterCacheHelper;
|
|
|
import com.yoho.search.service.helper.SearchParamHelper;
|
|
|
import com.yoho.search.service.helper.SearchSortHelper;
|
|
|
import com.yoho.search.service.scene.common.AbstractCacheAbleService;
|
|
|
import com.yoho.search.service.scene.common.SceneSelectionsService;
|
|
|
import com.yoho.search.service.service.IBrandService;
|
|
|
import com.yoho.search.service.service.IDiscountService;
|
|
|
import com.yoho.search.service.service.ISearchSortGroupService;
|
|
|
import com.yoho.search.service.service.ISelectionsForApp;
|
|
|
import com.yoho.search.service.service.ISelectionsForPc;
|
|
|
|
|
|
@Service
|
|
|
public class PromotionSceneService extends AbstractCacheAbleService {
|
|
|
|
|
|
private static Logger logger = LoggerFactory.getLogger(PromotionSceneService.class);
|
|
|
|
|
|
@Autowired
|
|
|
private SearchSortHelper searchSortHelper;
|
|
|
@Autowired
|
|
|
private SearchCommonService searchCommonService;
|
|
|
@Autowired
|
|
|
private AggregationFactoryService aggregationFactoryService;
|
|
|
@Autowired
|
|
|
private ISelectionsForApp selectionsWithOutAdvanceService;
|
|
|
@Autowired
|
|
|
private SearchAfterCacheHelper searchAfterCacheService;
|
|
|
@Autowired
|
|
|
private ISelectionsForPc selectionsForPc;
|
|
|
@Autowired
|
|
|
private IBrandService brandService;
|
|
|
@Autowired
|
|
|
private ISearchSortGroupService searchSortGroupService;
|
|
|
@Autowired
|
|
|
private IDiscountService discountService;
|
|
|
@Autowired
|
|
|
private ProductIndexBaseService productIndexBaseService;
|
|
|
@Autowired
|
|
|
private SearchParamHelper searchParamHelper;
|
|
|
@Autowired
|
|
|
private SceneSelectionsService sceneSelectionsService;
|
|
|
|
|
|
@Override
|
|
|
public SearchCache getSearchCache() {
|
|
|
return searchCacheFactory.getPromotionSearchCache();
|
|
|
}
|
|
|
|
|
|
private BoolQueryBuilder genConditionFilter(PromotionParam param, boolean isAnd) {
|
|
|
BoolQueryBuilder conditionFilter = QueryBuilders.boolQuery();
|
|
|
// 判断品牌
|
|
|
if (StringUtils.isNotBlank(param.getBrand())) {
|
|
|
int[] brandids = ConvertUtils.stringToIntArray(param.getBrand(), ",");
|
|
|
BoolQueryBuilder brandFilter = QueryBuilders.boolQuery().must(QueryBuilders.termsQuery(ProductIndexEsField.brandId, brandids));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(brandFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(brandFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断不包含品牌
|
|
|
if (StringUtils.isNotBlank(param.getNotBrandId())) {
|
|
|
int[] notBrandids = ConvertUtils.stringToIntArray(param.getNotBrandId(), ",");
|
|
|
BoolQueryBuilder notBrandFilter = QueryBuilders.boolQuery();
|
|
|
notBrandFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.brandId, notBrandids));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(notBrandFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(notBrandFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断品类
|
|
|
if (StringUtils.isNotBlank(param.getSort())) {
|
|
|
int[] sortIds = ConvertUtils.stringToIntArray(param.getSort(), ",");
|
|
|
BoolQueryBuilder sortFilter = QueryBuilders.boolQuery();
|
|
|
sortFilter.should(QueryBuilders.termsQuery(ProductIndexEsField.maxSortId, sortIds));
|
|
|
sortFilter.should(QueryBuilders.termsQuery(ProductIndexEsField.middleSortId, sortIds));
|
|
|
sortFilter.should(QueryBuilders.termsQuery(ProductIndexEsField.smallSortId, sortIds));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(QueryBuilders.boolQuery().must(sortFilter));
|
|
|
} else {
|
|
|
conditionFilter.should(QueryBuilders.boolQuery().must(sortFilter));
|
|
|
}
|
|
|
}
|
|
|
// 判断非品类
|
|
|
if (StringUtils.isNotBlank(param.getNotSort())) {
|
|
|
int[] notSortIds = ConvertUtils.stringToIntArray(param.getNotSort(), ",");
|
|
|
BoolQueryBuilder notSortFilter = QueryBuilders.boolQuery();
|
|
|
notSortFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.maxSortId, notSortIds));
|
|
|
notSortFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.middleSortId, notSortIds));
|
|
|
notSortFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.smallSortId, notSortIds));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(notSortFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(notSortFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断promotion
|
|
|
if (StringUtils.isNotBlank(param.getPromotion())) {
|
|
|
int[] promotionIds = ConvertUtils.stringToIntArray(param.getPromotion(), ",");
|
|
|
BoolQueryBuilder promotionFilter = QueryBuilders.boolQuery();
|
|
|
promotionFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.ispromotion, promotionIds));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(promotionFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(promotionFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断非promotion
|
|
|
if (StringUtils.isNotBlank(param.getNotPromotion())) {
|
|
|
int[] notPromotionIds = ConvertUtils.stringToIntArray(param.getNotPromotion(), ",");
|
|
|
BoolQueryBuilder notPromotionFilter = QueryBuilders.boolQuery();
|
|
|
notPromotionFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.ispromotion, notPromotionIds));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(notPromotionFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(notPromotionFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断skn
|
|
|
if (StringUtils.isNotBlank(param.getProductSknStr())) {
|
|
|
BoolQueryBuilder sknFilter = QueryBuilders.boolQuery();
|
|
|
sknFilter.must(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, param.getProductSknStr().split(",")));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(sknFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(sknFilter);
|
|
|
}
|
|
|
}
|
|
|
// 判断 非skn
|
|
|
if (StringUtils.isNotBlank(param.getNotProductSkn())) {
|
|
|
BoolQueryBuilder sknFilter = QueryBuilders.boolQuery();
|
|
|
sknFilter.mustNot(QueryBuilders.termsQuery(ProductIndexEsField.productSkn, param.getNotProductSkn().split(",")));
|
|
|
if (isAnd) {
|
|
|
conditionFilter.must(sknFilter);
|
|
|
} else {
|
|
|
conditionFilter.should(sknFilter);
|
|
|
}
|
|
|
}
|
|
|
return conditionFilter;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据促销列表参数生成过滤条件
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
public BoolQueryBuilder getMustFilterByPromotion(PromotionConditions promotionConditions) {
|
|
|
if (promotionConditions == null) {
|
|
|
return null;
|
|
|
}
|
|
|
// 生成每种condition的filter列表
|
|
|
List<BoolQueryBuilder> conditionsFilter = new ArrayList<BoolQueryBuilder>();
|
|
|
List<PromotionParam> promotionParams = promotionConditions.getPromotionParams();
|
|
|
for (PromotionParam promotionParam : promotionParams) {
|
|
|
boolean isAnd = "any".equals(promotionParam.getAggregator()) ? false : true;
|
|
|
BoolQueryBuilder conditionFilter = this.genConditionFilter(promotionParam, isAnd);
|
|
|
conditionsFilter.add(conditionFilter);
|
|
|
}
|
|
|
// 处理总结果
|
|
|
String aggregator = promotionConditions.getAggregator();
|
|
|
boolean isTotalAnd = "any".equals(aggregator) ? false : true;
|
|
|
BoolQueryBuilder allBuilder = QueryBuilders.boolQuery();
|
|
|
for (BoolQueryBuilder conditionFilter : conditionsFilter) {
|
|
|
if (isTotalAnd) {
|
|
|
allBuilder.must(conditionFilter);
|
|
|
} else {
|
|
|
allBuilder.should(conditionFilter);
|
|
|
}
|
|
|
}
|
|
|
return allBuilder;
|
|
|
}
|
|
|
|
|
|
public SearchApiResult list(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception {
|
|
|
// 1、获取mustFilterByPromotion
|
|
|
BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions);
|
|
|
|
|
|
// 2、构造带filter和Query的SearchParam
|
|
|
SearchParam searchParam = searchParamHelper.buildWithPersional(paramMap, true, mustFilterByPromotion);
|
|
|
|
|
|
// 3、设置聚合条件
|
|
|
searchParam.setAggregationBuilders(null);
|
|
|
// 4、设置分页参数
|
|
|
int pageSize = StringUtils.isBlank(paramMap.get("viewNum")) ? 10 : Integer.parseInt(paramMap.get("viewNum"));
|
|
|
int page = StringUtils.isBlank(paramMap.get("page")) ? 1 : Integer.parseInt(paramMap.get("page"));
|
|
|
if (page < 1 || pageSize < 0) {
|
|
|
throw new IllegalArgumentException("分页参数不合法");
|
|
|
}
|
|
|
if (pageSize > 100) {
|
|
|
pageSize = 100;
|
|
|
}
|
|
|
searchParam.setOffset((page - 1) * pageSize);
|
|
|
searchParam.setSize(pageSize);
|
|
|
// 5)设置排序字段
|
|
|
searchParam.setSortBuilders(searchSortHelper.buildSortList(paramMap));
|
|
|
|
|
|
// 6)从缓存中获取数据
|
|
|
final String productIndexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(this.searchCache, productIndexName, searchParam);
|
|
|
if (cacheObject != null) {
|
|
|
SearchCacheMatchLogger.doSearchCacheMatchLog("/promotion/list.json", paramMap);
|
|
|
return new SearchApiResult().setData(cacheObject);
|
|
|
}
|
|
|
|
|
|
// 7)查询ES
|
|
|
SearchResult searchResult = searchCommonService.doSearch(productIndexName, searchParam);
|
|
|
if (searchResult == null) {
|
|
|
return new SearchApiResult().setCode(500).setMessage("execption");
|
|
|
}
|
|
|
// 8)构造返回结果
|
|
|
JSONObject dataMap = new JSONObject();
|
|
|
dataMap.put("total", searchResult.getTotal());
|
|
|
dataMap.put("page", searchResult.getPage());
|
|
|
dataMap.put("page_size", searchParam.getSize());
|
|
|
dataMap.put("page_total", searchResult.getTotalPage());
|
|
|
dataMap.put("product_list", productIndexBaseService.getProductListWithPricePlan(searchResult.getResultList()));
|
|
|
|
|
|
// 10)将结果存进缓存
|
|
|
searchCacheService.addJSONObjectToCache(this.searchCache, productIndexName, searchParam, dataMap);
|
|
|
return new SearchApiResult().setData(dataMap);
|
|
|
}
|
|
|
|
|
|
public SearchApiResult selectionsForApp(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception {
|
|
|
// 1、获取mustFilterByPromotion
|
|
|
BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions);
|
|
|
// 2、构造带filter和Query的SearchParam
|
|
|
SearchParam searchParam = searchParamHelper.buildWithMustFilter(paramMap, mustFilterByPromotion);
|
|
|
// 3、构造聚合条件【7种】
|
|
|
List<AbstractAggregationBuilder<?>> aggregationBuilders = new ArrayList<AbstractAggregationBuilder<?>>();
|
|
|
searchParam.setAggregationBuilders(aggregationBuilders);
|
|
|
// 3.1获取通用的聚合场景
|
|
|
aggregationBuilders.addAll(selectionsWithOutAdvanceService.getAllAggregationBuilders(paramMap));
|
|
|
// 3.2加上折扣聚合场景
|
|
|
IAggregation discountAggregation = aggregationFactoryService.getDiscountAggregation();
|
|
|
aggregationBuilders.add(discountAggregation.getBuilder());
|
|
|
// 3.3加上品类聚合场景
|
|
|
IAggregation sortGroupAggregation = aggregationFactoryService.getSortGroupAggregation(paramMap);
|
|
|
aggregationBuilders.add(sortGroupAggregation.getBuilder());
|
|
|
|
|
|
// 4、设置分页参数
|
|
|
searchParam.setOffset(0);
|
|
|
searchParam.setSize(0);
|
|
|
|
|
|
final String productIndexName = ISearchConstants.INDEX_NAME_PRODUCT_INDEX;
|
|
|
|
|
|
// 5、从缓存中取
|
|
|
JSONObject cacheObject = searchCacheService.getJSONObjectFromCache(this.searchCache, productIndexName, searchParam);
|
|
|
if (cacheObject != null) {
|
|
|
Object newCacheObject = searchAfterCacheService.dealFilterCacheObject(cacheObject);
|
|
|
newCacheObject = searchAfterCacheService.dealDiscountCacheObject((JSONObject) newCacheObject);
|
|
|
SearchCacheMatchLogger.doSearchCacheMatchLog("/promotion/selections.json", paramMap);
|
|
|
return new SearchApiResult().setData(newCacheObject);
|
|
|
}
|
|
|
|
|
|
// 6)查询ES
|
|
|
SearchResult searchResult = searchCommonService.doSearch(productIndexName, searchParam);
|
|
|
if (searchResult == null) {
|
|
|
return new SearchApiResult().setCode(500).setMessage("execption");
|
|
|
}
|
|
|
// 7)构造并返回聚合结果
|
|
|
Map<String, Aggregation> aggMaps = searchResult.getAggMaps();
|
|
|
if (aggMaps == null) {
|
|
|
return null;
|
|
|
}
|
|
|
// 8)先拿通用的聚合结果
|
|
|
Map<String, Object> commonAggResult = selectionsWithOutAdvanceService.getAggResultMap(paramMap, aggMaps);
|
|
|
// 9)获取折扣的聚合结果
|
|
|
Object discountAggResult = discountAggregation.getAggregationResponseMap(aggMaps);
|
|
|
// 10)获取品类的聚合结果
|
|
|
Object sortGroupAggResult = sortGroupAggregation.getAggregationResponseMap(aggMaps);
|
|
|
|
|
|
// 11)构造返回对象
|
|
|
JSONObject result = new JSONObject();
|
|
|
result.put("filter", commonAggResult);
|
|
|
result.put("discount", discountAggResult);
|
|
|
result.put("sort", sortGroupAggResult);
|
|
|
|
|
|
// 12)将结果加入缓存
|
|
|
searchCacheService.addJSONObjectToCache(this.searchCache, productIndexName, searchParam, result);
|
|
|
return new SearchApiResult().setData(result);
|
|
|
}
|
|
|
|
|
|
public SearchApiResult selectionsForPc(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception {
|
|
|
// 1、获取默认的mustFilterByPromotion
|
|
|
BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions);
|
|
|
|
|
|
// 2、调通用的PC聚合逻辑
|
|
|
SearchApiResult apiResult = selectionsForPc.getSelectionsForPc(paramMap, mustFilterByPromotion);
|
|
|
if (apiResult == null || apiResult.getData() == null) {
|
|
|
return apiResult;
|
|
|
}
|
|
|
JSONObject result = (JSONObject) apiResult.getData();
|
|
|
// 3、加入品类聚合结果
|
|
|
JSONArray jsonArray = searchSortGroupService.sortGroup(paramMap, mustFilterByPromotion);
|
|
|
result.put("sort", jsonArray);
|
|
|
// 4、加入折扣聚合结果
|
|
|
JSONObject discount = discountService.discount(paramMap, mustFilterByPromotion);
|
|
|
result.put("dicount", discount);
|
|
|
return new SearchApiResult().setData(result);
|
|
|
}
|
|
|
|
|
|
public SearchApiResult aggPromotionBrands(PromotionConditions promotionConditions, Map<String, String> paramMap) throws Exception {
|
|
|
logger.info("[func=aggPromotionBrands][param={}][begin={}]", paramMap.toString(), System.currentTimeMillis());
|
|
|
// 1、设置必须满足的过滤条件
|
|
|
BoolQueryBuilder mustFilterByPromotion = this.getMustFilterByPromotion(promotionConditions);
|
|
|
// 2、调聚合品牌的方法
|
|
|
SearchApiResult searchApiResult = brandService.aggBrand(paramMap, mustFilterByPromotion);
|
|
|
return searchApiResult;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 促销列表新版筛选项
|
|
|
*
|
|
|
* @param promotionConditions
|
|
|
* @param paramMap
|
|
|
* @return
|
|
|
*/
|
|
|
public SearchApiResult aggregations(PromotionConditions promotionConditions, Map<String, String> paramMap) {
|
|
|
try {
|
|
|
// 0、解析promotion的限制条件
|
|
|
BoolQueryBuilder mustFilter = this.getMustFilterByPromotion(promotionConditions);
|
|
|
return sceneSelectionsService.aggregations(paramMap, mustFilter);
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
|
return new SearchApiResult().setData(null).setMessage("PromotionAggregations Exception").setCode(500);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} |