diff --git a/framework/src/main/java/cn/lili/elasticsearch/BaseElasticsearchService.java b/framework/src/main/java/cn/lili/elasticsearch/BaseElasticsearchService.java index f5c9fb4f..7f08ddf7 100644 --- a/framework/src/main/java/cn/lili/elasticsearch/BaseElasticsearchService.java +++ b/framework/src/main/java/cn/lili/elasticsearch/BaseElasticsearchService.java @@ -90,6 +90,7 @@ public abstract class BaseElasticsearchService { request.settings(Settings.builder() .put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards()) .put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas()) + .put("index.max_result_window", 100000) //最大查询结果数 .put("index.mapping.total_fields.limit", 2000)); //创建索引 diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java b/framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java index dc1c2348..f6e2a2aa 100644 --- a/framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/util/PromotionPriceUtil.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * 促销价格计算业务层实现 @@ -66,7 +67,7 @@ public class PromotionPriceUtil { List skuVOList = tradeDTO.getSkuList(); // 获取map分配sku的总数,如果是最后一个商品分配金额,则将金额从百分比改为总金额扣减,避免出现小数除不尽 - Integer count = skuPromotionDetail.size(); + int count = skuPromotionDetail.size(); //已优惠金额 Double deducted = 0D; @@ -81,12 +82,12 @@ public class PromotionPriceUtil { count--; //sku 优惠金额 - Double skuDiscountPrice = 0d; + Double skuDiscountPrice; //非最后一个商品,则按照比例计算 if (count > 0) { //商品金额占比 - Double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4); + double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4); //商品优惠金额 skuDiscountPrice = CurrencyUtil.mul(discountPrice, point); //累加已优惠金额 @@ -96,64 +97,135 @@ public class PromotionPriceUtil { else { skuDiscountPrice = CurrencyUtil.sub(discountPrice, deducted); } - //优惠券金额,则计入优惠券 ,其他则计入总的discount price - if (promotionTypeEnum == PromotionTypeEnum.COUPON) { - cartSkuVO.getPriceDetailDTO().setCouponPrice( - CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), skuDiscountPrice)); - - cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( - DiscountPriceItem.builder() - .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) - .skuId(cartSkuVO.getGoodsSku().getId()) - .discountPrice(skuDiscountPrice) - .promotionTypeEnum(PromotionTypeEnum.COUPON) - .promotionId(activityId) - .build() - ); - - } else if (promotionTypeEnum == PromotionTypeEnum.PLATFORM_COUPON) { - - cartSkuVO.getPriceDetailDTO().setSiteCouponPrice( - CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), skuDiscountPrice)); - - cartSkuVO.getPriceDetailDTO().setCouponPrice( - CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), cartSkuVO.getPriceDetailDTO().getSiteCouponPrice())); - - - cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( - DiscountPriceItem.builder() - .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) - .skuId(cartSkuVO.getGoodsSku().getId()) - .discountPrice(skuDiscountPrice) - .promotionTypeEnum(PromotionTypeEnum.PLATFORM_COUPON) - .promotionId(activityId) - .build() - ); - - } else { - cartSkuVO.getPriceDetailDTO().setDiscountPrice( - CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getDiscountPrice(), skuDiscountPrice)); - - //目前剩余的只有满减金额活动。后续如果需要调整,这里建议传递活动类型进来 - cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( - DiscountPriceItem.builder() - .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) - .skuId(cartSkuVO.getGoodsSku().getId()) - .discountPrice(skuDiscountPrice) - .promotionTypeEnum(PromotionTypeEnum.FULL_DISCOUNT) - .promotionId(activityId) - .build() - ); - } + calculateCartSkuPromotionsPrice(cartSkuVO, skuDiscountPrice, promotionTypeEnum, activityId); } } } + calculateNotEnoughPromotionsPrice(skuVOList, skuPromotionDetail, discountPrice, totalPrice, promotionTypeEnum, activityId); } + /** + * 计算购物车商品优惠金额 + * + * @param cartSkuVO 购物车商品 + * @param skuDiscountPrice 商品优惠金额 + * @param promotionTypeEnum 优惠类型 + * @param activityId 优惠活动id + */ + private static void calculateCartSkuPromotionsPrice(CartSkuVO cartSkuVO, Double skuDiscountPrice, PromotionTypeEnum promotionTypeEnum, String activityId) { + //优惠券金额,则计入优惠券 ,其他则计入总的discount price + if (promotionTypeEnum == PromotionTypeEnum.COUPON) { + + cartSkuVO.getPriceDetailDTO().setCouponPrice( + CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), skuDiscountPrice)); + + cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( + DiscountPriceItem.builder() + .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) + .skuId(cartSkuVO.getGoodsSku().getId()) + .discountPrice(skuDiscountPrice) + .promotionTypeEnum(PromotionTypeEnum.COUPON) + .promotionId(activityId) + .build() + ); + + } else if (promotionTypeEnum == PromotionTypeEnum.PLATFORM_COUPON) { + + cartSkuVO.getPriceDetailDTO().setSiteCouponPrice( + CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), skuDiscountPrice)); + + cartSkuVO.getPriceDetailDTO().setCouponPrice( + CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getCouponPrice(), cartSkuVO.getPriceDetailDTO().getSiteCouponPrice())); + + + cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( + DiscountPriceItem.builder() + .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) + .skuId(cartSkuVO.getGoodsSku().getId()) + .discountPrice(skuDiscountPrice) + .promotionTypeEnum(PromotionTypeEnum.PLATFORM_COUPON) + .promotionId(activityId) + .build() + ); + + } else { + cartSkuVO.getPriceDetailDTO().setDiscountPrice( + CurrencyUtil.add(cartSkuVO.getPriceDetailDTO().getDiscountPrice(), skuDiscountPrice)); + + //目前剩余的只有满减金额活动。后续如果需要调整,这里建议传递活动类型进来 + cartSkuVO.getPriceDetailDTO().addDiscountPriceItem( + DiscountPriceItem.builder() + .goodsId(cartSkuVO.getGoodsSku().getGoodsId()) + .skuId(cartSkuVO.getGoodsSku().getId()) + .discountPrice(skuDiscountPrice) + .promotionTypeEnum(PromotionTypeEnum.FULL_DISCOUNT) + .promotionId(activityId) + .build() + ); + } + } + + /** + * 特殊情况处理,如参与多个促销活动,部分商品在其他促销计算后的金额不足以满足与当前参与的促销活动的优惠金额 + * + * @param skuVOList 获取购物车信息 + * @param skuPromotionDetail 参与活动的商品,以及商品总金额 + * @param discountPrice 需要分发的优惠金额 + * @param totalPrice 计算总金额 + * @param promotionTypeEnum 优惠类型 + * @param activityId 优惠活动id + */ + private static void calculateNotEnoughPromotionsPrice(List skuVOList, Map skuPromotionDetail, Double discountPrice, Double totalPrice, PromotionTypeEnum promotionTypeEnum, String activityId) { + // 特殊情况处理,如参与多个促销活动,部分商品在其他促销计算后的金额不足以满足与当前参与的促销活动的优惠金额 + // 但当前购物车内存在当前当前促销活动的其他商品且剩余金额也满足分摊不足商品的不足金额,则分摊到其他商品上 + // 满足当前促销的总优惠金额 + if (skuPromotionDetail == null || skuPromotionDetail.isEmpty()) { + return; + } + long matchPromotionsZeroCount = skuVOList.stream().filter(l -> l.getPriceDetailDTO().getFlowPrice() == 0 && skuPromotionDetail.containsKey(l.getGoodsSku().getId())).count(); + long matchPromotionsCount = skuVOList.stream().filter(l -> skuPromotionDetail.containsKey(l.getGoodsSku().getId())).count(); + if (matchPromotionsZeroCount == matchPromotionsCount) { + return; + } + // 获取剩余金额不足优惠金额的商品 + List unEnoughSku = skuVOList.stream().filter(k -> { + if (skuPromotionDetail.containsKey(k.getGoodsSku().getId()) && skuPromotionDetail.size() >= 2) { + //商品金额占比 + double point = CurrencyUtil.div(k.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4); + //商品优惠金额 + Double skuDiscountPrice = CurrencyUtil.mul(discountPrice, point); + return skuDiscountPrice > k.getPriceDetailDTO().getCouponPrice(); + } + return false; + }).collect(Collectors.toList()); + if (!unEnoughSku.isEmpty()) { + for (CartSkuVO cartSkuVO : skuVOList) { + if (unEnoughSku.isEmpty()) { + return; + } + if (skuPromotionDetail.containsKey(cartSkuVO.getGoodsSku().getId()) && unEnoughSku.stream().noneMatch(k -> k.getGoodsSku().getId().equals(cartSkuVO.getGoodsSku().getId()))) { + // 商品金额占比 + double point = CurrencyUtil.div(cartSkuVO.getPriceDetailDTO().getGoodsPrice(), totalPrice, 4); + // 商品优惠金额 + Double skuDiscountPrice = CurrencyUtil.mul(discountPrice, point); + // 商品优惠金额 - 不足优惠金额 = 差额 + Double sub = CurrencyUtil.sub(skuDiscountPrice, unEnoughSku.get(0).getPriceDetailDTO().getCouponPrice()); + // 分摊到其他商品: 其他商品原优惠金额 + 差额 + calculateCartSkuPromotionsPrice(cartSkuVO, sub, promotionTypeEnum, activityId); + // 从不足商品列表中移除 + unEnoughSku.remove(0); + } + } + } else { + return; + } + calculateNotEnoughPromotionsPrice(skuVOList, skuPromotionDetail, discountPrice, totalPrice, promotionTypeEnum, activityId); + } + /** * 检查活动有效时间 * diff --git a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java index 32359149..e2c34666 100644 --- a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java @@ -66,6 +66,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Service; @@ -178,7 +179,7 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements skuCountQueryWrapper.eq("auth_flag", GoodsAuthEnum.PASS.name()); skuCountQueryWrapper.eq("market_enable", GoodsStatusEnum.UPPER.name()); skuCountQueryWrapper.eq("delete_flag", false); - skuCountQueryWrapper.ge("quantity", 0); + skuCountQueryWrapper.gt("quantity", 0); resultMap = new HashMap<>(); resultMap.put(KEY_SUCCESS, 0L); resultMap.put(KEY_FAIL, 0L); @@ -675,14 +676,30 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements @Override public void deleteEsGoodsPromotionByPromotionKey(String promotionsKey) { ThreadUtil.execAsync(() -> { - BulkRequest bulkRequest = new BulkRequest(); - for (EsGoodsIndex goodsIndex : this.goodsIndexRepository.findAll()) { - UpdateRequest updateRequest = this.removePromotionByPromotionKey(goodsIndex, promotionsKey); - if (updateRequest != null) { - bulkRequest.add(updateRequest); + for (int i = 0; ; i++) { + BulkRequest bulkRequest = new BulkRequest(); + + NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); + nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); + nativeSearchQueryBuilder.withPageable(PageRequest.of(i, 1000)); + try { + SearchHits esGoodsIndices = this.restTemplate.search(nativeSearchQueryBuilder.build(), EsGoodsIndex.class); + if (esGoodsIndices.isEmpty() || esGoodsIndices.getSearchHits().isEmpty()) { + break; + } + for (SearchHit searchHit : esGoodsIndices.getSearchHits()) { + EsGoodsIndex goodsIndex = searchHit.getContent(); + UpdateRequest updateRequest = this.removePromotionByPromotionKey(goodsIndex, promotionsKey); + if (updateRequest != null) { + bulkRequest.add(updateRequest); + } + } + this.executeBulkUpdateRequest(bulkRequest); + } catch (Exception e) { + log.error("删除索引中指定的促销活动id的促销活动失败!key: {}", promotionsKey, e); + return; } } - this.executeBulkUpdateRequest(bulkRequest); }); } diff --git a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java index 6078d77e..708400e7 100644 --- a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java @@ -84,11 +84,12 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService { //如果搜索词不为空,且明显不是sql注入,那么就将搜索词加入热搜词 //PS:线上环境运行很多客户反馈被sql攻击,写在了搜索热词里,这里控制命中关键字就不做热词统计,如果线上比较严格可以调用关键词替换,不过不建议这么做 - if (CharSequenceUtil.isNotBlank(searchDTO.getKeyword()) && !SqlFilter.hit(searchDTO.getKeyword())) { + if (CharSequenceUtil.isNotBlank(searchDTO.getKeyword()) && Boolean.FALSE.equals(SqlFilter.hit(searchDTO.getKeyword()))) { cache.incrementScore(CachePrefix.HOT_WORD.getPrefix(), searchDTO.getKeyword()); } NativeSearchQueryBuilder searchQueryBuilder = createSearchQueryBuilder(searchDTO, pageVo); NativeSearchQuery searchQuery = searchQueryBuilder.build(); + searchQuery.setTrackTotalHits(true); log.debug("searchGoods DSL:{}", searchQuery.getQuery()); SearchHits search = restTemplate.search(searchQuery, EsGoodsIndex.class); return SearchHitSupport.searchPageFor(search, searchQuery.getPageable());