Authored by wujiexiang

优惠券优化 -- 券条件新增

... ... @@ -168,8 +168,14 @@ public class ChargeService {
switch (chargeParam.getChargeStage()) {
case PAYMENT: {
if (CollectionUtils.isEmpty(couponCodes)) {
//场景:砍价活动不需要推荐
return;
}
//在推荐情况是否需要自动使用券,根据apiVersion来使用
boolean useCoupon = new Payment.AUTO_RECOMMEND_AND_USE_COUPON().apply(Payment.of(chargeParam.getApiVersion()));
//自动匹配
calculateRecommendedCoupons(chargeContext);
calculateRecommendedCoupons(useCoupon, chargeContext);
break;
}
case SELECT_COUPON:
... ... @@ -190,15 +196,15 @@ public class ChargeService {
/**
* 推荐券
* @param useCoupon
* @param chargeContext
*/
private void calculateRecommendedCoupons(ChargeContext chargeContext) {
private void calculateRecommendedCoupons(boolean useCoupon, ChargeContext chargeContext) {
List<CouponMatchResult> usableCouponMatchResults = couponService.getRecommendedCoupons(chargeContext);
//可用张数
chargeContext.getChargeResult().getCouponPayResultList().setUsableCouponCount(usableCouponMatchResults.size());
//在推荐情况是否需要自动使用券,根据apiVersion来使用
if (new Payment.AUTO_RECOMMEND_AND_USE_COUPON().apply(Payment.of(chargeContext.getChargeParam().getApiVersion()))) {
if (useCoupon) {
doCalculateCoupons(usableCouponMatchResults, chargeContext);
}
}
... ...
... ... @@ -7,6 +7,7 @@ import com.yohobuy.ufo.model.promotion.constant.CouponUseLimitTypeEnum;
import com.yohoufo.order.charge.model.ChargeGoods;
import com.yohoufo.order.charge.model.CouponMatchResult;
import com.yohoufo.order.utils.LoggerUtils;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
... ... @@ -103,9 +104,15 @@ public class CouponRuleMatcher {
public boolean match(ChargeGoods chargeGoods, UserCouponsBo couponsBo) {
final CouponProductLimitTypeEnum limitTypeEnum = CouponProductLimitTypeEnum.of(couponsBo.getProductLimitType()).orElse(null);
if (limitTypeEnum == CouponProductLimitTypeEnum.SPECIFIC_PRODUCT) {
return couponsBo.getProductIdInclude().contains(chargeGoods.getProductId());
//包含商品
return CollectionUtils.isNotEmpty(couponsBo.getProductIdInclude())
&& couponsBo.getProductIdInclude().contains(chargeGoods.getProductId());
} else if (limitTypeEnum == CouponProductLimitTypeEnum.NON) {
return true;
} else if (limitTypeEnum == CouponProductLimitTypeEnum.EXCLUDE_PRODUCT) {
//排除商品
return CollectionUtils.isNotEmpty(couponsBo.getProductIdExclude())
&& !couponsBo.getProductIdExclude().contains(chargeGoods.getProductId());
} else {
//目前不存在
return false;
... ...
... ... @@ -17,6 +17,11 @@ public class RecommendedCouponInfo {
@JSONField(name = "usable_coupon_count")
private int usableCouponCount;
//使用券张数
@JSONField(name = "used_coupon_count")
private int usedCouponCount;
//多个用,分隔
@JSONField(name = "coupon_code")
private String couponCode;
... ... @@ -26,7 +31,7 @@ public class RecommendedCouponInfo {
private String couponAmountStr;
/**
*
* 描述
*/
private String desc;
}
... ...
... ... @@ -206,17 +206,21 @@ public class ShoppingSupport {
int usableCouponCount = couponPayResultList.getUsableCouponCount();
List<CouponPayResult> couponPayResults = couponPayResultList.getCouponPayResults();
if (CollectionUtils.isNotEmpty(couponPayResults)) {
int usedCouponCount = couponPayResults.size();
String couponPayAmountStr = MathUtils.formatCurrencyStr(couponPayResults.stream().mapToDouble(coupon -> coupon.getCouponAmount()).sum());
String couponCodes = CouponCodeUtils.asString(couponPayResults.stream().map(coupon -> coupon.getCouponCode()).collect(Collectors.toList()));
return RecommendedCouponInfo.builder()
.usableCouponCount(usableCouponCount)
.usedCouponCount(usedCouponCount)
.couponAmountStr(couponPayAmountStr)
.couponCode(couponCodes)
.desc(MessageFormat.format(CouponConstants.COUPON_AUTO_RECOMMENDED_DESC_FORMAT, usableCouponCount, couponPayResults.size())).build();
.desc(MessageFormat.format(CouponConstants.COUPON_AUTO_RECOMMENDED_DESC_FORMAT, usableCouponCount, usedCouponCount)).build();
} else {
return RecommendedCouponInfo.builder()
.usableCouponCount(usableCouponCount)
.usedCouponCount(0)
.desc(MessageFormat.format(CouponConstants.USABLE_COUPON_DESC_FORMAT, usableCouponCount))
.build();
}
return RecommendedCouponInfo.builder()
.usableCouponCount(usableCouponCount)
.desc(MessageFormat.format(CouponConstants.USABLE_COUPON_DESC_FORMAT,usableCouponCount))
.build();
}
}
... ...
... ... @@ -7,16 +7,28 @@ import com.yohobuy.ufo.model.promotion.constant.CouponUseLimitTypeEnum;
import com.yohobuy.ufo.model.promotion.constant.UserCouponsStatusEnum;
import com.yohobuy.ufo.model.promotion.response.CouponInfo;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.dal.promotion.model.*;
import com.yohoufo.dal.promotion.model.Coupon;
import com.yohoufo.dal.promotion.model.CouponAndType;
import com.yohoufo.dal.promotion.model.CouponType;
import com.yohoufo.dal.promotion.model.UserCoupon;
import org.apache.commons.lang3.StringUtils;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
public class CouponConvert {
public static UserCouponsBo covertCouponBo(UserCoupon userCoupon, Coupon coupon, CouponType couponType){
/**
* 订单默认使用
* @param userCoupon
* @param coupon
* @param couponType
* @param productIds
* @return
*/
public static UserCouponsBo covertCouponBo(UserCoupon userCoupon, Coupon coupon, CouponType couponType, List<Integer> productIds){
UserCouponsBo couponBo = new UserCouponsBo();
... ... @@ -46,12 +58,20 @@ public class CouponConvert {
couponBo.setCouponType(couponType.getId());
couponBo.setCouponTypeName(couponType.getCaption());
}
CouponProductLimitTypeEnum productLimitTypeEnum = CouponProductLimitTypeEnum.of(String.valueOf(coupon.getProductLimitType())).orElse(null);
if (productLimitTypeEnum == CouponProductLimitTypeEnum.SPECIFIC_PRODUCT) {
couponBo.setProductIdInclude(productIds);
} else if (productLimitTypeEnum == CouponProductLimitTypeEnum.EXCLUDE_PRODUCT) {
couponBo.setProductIdExclude(productIds);
}
return couponBo;
}
/**
* 用户优惠券信息
* 用户优惠券信息 用户中心展示
* @param userCoupon 用户优惠券
* @param coupon 优惠券
* @param couponType 优惠券类型
... ...
... ... @@ -32,10 +32,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
... ... @@ -279,9 +276,10 @@ public class CouponServiceImpl implements ICouponService {
}
// 批量获取coupon
List<String> couponIdList = list.stream().map(UserCoupon::getCouponToken).collect(Collectors.toList());
List<Coupon> couponList = couponCacheService.getCouponsWithCache(couponIdList);
Map<Integer, Coupon> couponMap = couponList.stream().filter(coupon -> {
List<String> couponTokens = list.stream().map(UserCoupon::getCouponToken).collect(Collectors.toList());
List<Coupon> couponList = couponCacheService.getCouponsWithCache(couponTokens);
final Map<Integer, Coupon> couponMap = couponList.stream().filter(coupon -> {
if (coupon.getStatus() != null && coupon.getStatus().intValue() == CouponsStatusEnum.VALID.getCode()) {
return true;
} else {
... ... @@ -290,34 +288,28 @@ public class CouponServiceImpl implements ICouponService {
}).collect(Collectors.toMap(Coupon::getId, Function.identity()));
// 优惠券商品限制 key=couponId, value=ProductId集合
Map<Integer, List<Integer>> couponProductIdMap = getProductIdListMap(couponList);
List<UserCouponsBo> couponBoList = list.stream().filter(userCoupon -> {
return couponMap.containsKey(userCoupon.getCouponId());
}).map(userCoupon -> {
Coupon coupon = couponMap.get(userCoupon.getCouponId());
List<Integer> productIds = couponProductIdMap.get(userCoupon.getCouponId());
// 单个获取couponType
CouponType couponType = couponCacheService.getCouponTypeWithCache(userCoupon.getCouponType() != null ? userCoupon.getCouponType().intValue() : null);
UserCouponsBo couponBo = CouponConvert.covertCouponBo(userCoupon, coupon, couponType);
couponBo.setProductIdInclude(productIds);
return couponBo;
}).collect(Collectors.toList());
final Map<Integer, List<Integer>> couponProductIdMap = getAssociatedProductIdListMap(couponList);
//包装返回结果
List<UserCouponsBo> couponBoList = list.stream()
.filter(userCoupon -> couponMap.containsKey(userCoupon.getCouponId()))
.map(userCoupon -> wrapperUserCoupon(userCoupon, couponMap.get(userCoupon.getCouponId()), couponProductIdMap.get(userCoupon.getCouponId())))
.collect(Collectors.toList());
return UserCouponsListBo.builder().coupons(couponBoList).build();
}
private Map<Integer, List<Integer>> getProductIdListMap(List<Coupon> couponList) {
// 批量获取 coupon_product_limt 过滤出商品限制的优惠券id
/**
* 关联的商品id
* @param couponList
* @return
*/
private Map<Integer, List<Integer>> getAssociatedProductIdListMap(List<Coupon> couponList) {
// 批量获取 coupon_product_limit 过滤出商品限制的优惠券id
List<Integer> productLimitCouponIds = couponList.stream().filter(coupon -> {
if (coupon.getProductLimitType()!=null && CouponProductLimitTypeEnum.SPECIFIC_PRODUCT.getLimitType().equals(String.valueOf(coupon.getProductLimitType()))){
if (coupon.getProductLimitType() != null && !StringUtils.equals(CouponProductLimitTypeEnum.NON.getLimitType(),String.valueOf(coupon.getProductLimitType()))) {
return true;
}else{
} else {
return false;
}
}).map(Coupon::getId).collect(Collectors.toList());
... ... @@ -326,6 +318,84 @@ public class CouponServiceImpl implements ICouponService {
}
@Override
public UserCouponsListBo checkAndGetCoupons(int uid, List<String> couponCodes) {
//数据校验
if (uid <= 0 || CollectionUtils.isEmpty(couponCodes)) {
logger.warn("check coupon use param error:{}, {}", uid, couponCodes);
throw new ServiceException(ServiceError.PROMOTION_PARAM_IS_ERROR);
}
// 获取用户券记录
List<UserCoupon> userCoupons = userCouponMapper.selectByUidAndCouponCodes(uid, couponCodes);
// 批量获取coupon
List<String> couponTokens = userCoupons.stream().map(UserCoupon::getCouponToken).collect(Collectors.toList());
List<Coupon> couponList = couponCacheService.getCouponsWithCache(couponTokens);
final Map<Integer, Coupon> couponMap = couponList.stream().filter(coupon -> {
if (coupon.getStatus() != null && coupon.getStatus().intValue() == CouponsStatusEnum.VALID.getCode()) {
return true;
} else {
return false;
}
}).collect(Collectors.toMap(Coupon::getId, Function.identity()));
// 优惠券商品限制 key=couponId, value=ProductId集合
Map<Integer, List<Integer>> couponProductIdMap = getAssociatedProductIdListMap(couponList);
int time = DateUtil.getCurrentTimeSecond();
List<UserCouponsBo> couponBoList = couponCodes.stream().map(couponCode -> {
UserCoupon userCoupon = userCoupons.stream().filter(e -> couponCode.equals(e.getCouponCode())).findAny().orElse(null);
logger.info("user coupon {},{},{}", uid, couponCode, userCoupon);
if (userCoupon == null) {
logger.warn("not find user coupon by{},{}", uid, couponCode);
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_YOUS);
}
//判断券状态
if (userCoupon.getStatus().intValue() != CouponUseStatusEnum.NOT_USED.getCode()) {
logger.warn("user this coupon is used:{},{}", userCoupon.getUid(), userCoupon.getCouponCode());
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_USE_DOUBLE);
}
//判断生失效时间
if (userCoupon.getStartTime() > time || userCoupon.getEndTime() < time) {
logger.warn("coupon has expire or not arrive time:{},{},{}", userCoupon.getStartTime(), userCoupon.getEndTime(), time);
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_ALLOW_OR_EXPIRE);
}
//券模板的状态
Coupon coupon = couponMap.get(userCoupon.getCouponId());
if (coupon == null) {
logger.warn("not find coupon by couponId:{}",userCoupon.getCouponId());
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_EXISTS);
}
if (coupon.getStatus() != CouponsStatusEnum.VALID.getCode()) {
logger.warn("coupon status can't be use:{},{}", coupon.getId(), coupon.getStatus());
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
}
//所有的校验都通过了,生成bo
return wrapperUserCoupon(userCoupon, coupon, couponProductIdMap.get(userCoupon.getCouponId()));
}).collect(Collectors.toList());
return UserCouponsListBo.builder().coupons(couponBoList).build();
}
private UserCouponsBo wrapperUserCoupon(final UserCoupon userCoupon, final Coupon coupon, final List<Integer> productIds) {
// 单个获取couponType
CouponType couponType = couponCacheService.getCouponTypeWithCache(userCoupon.getCouponType() != null ? userCoupon.getCouponType().intValue() : null);
UserCouponsBo couponBo = CouponConvert.covertCouponBo(userCoupon, coupon, couponType, productIds);
return couponBo;
}
@Override
... ... @@ -429,63 +499,4 @@ public class CouponServiceImpl implements ICouponService {
.size(req.getLimit())
.build();
}
@Override
public UserCouponsListBo checkAndGetCoupons(int uid, List<String> couponCodes) {
//数据校验
if (uid <= 0 || CollectionUtils.isEmpty(couponCodes)) {
logger.warn("check coupon use param error:{}, {}", uid, couponCodes);
throw new ServiceException(ServiceError.PROMOTION_PARAM_IS_ERROR);
}
// 获取用户券记录
List<UserCoupon> userCoupons = userCouponMapper.selectByUidAndCouponCodes(uid, couponCodes);
int time = DateUtil.getCurrentTimeSecond();
List<UserCouponsBo> couponBoList = couponCodes.stream().map(couponCode -> {
UserCoupon userCoupon = userCoupons.stream().filter(e -> couponCode.equals(e.getCouponCode())).findAny().orElse(null);
logger.info("user coupon {},{},{}", uid, couponCode, userCoupon);
if (userCoupon == null) {
logger.warn("not find user coupon by{},{}", uid, couponCode);
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_YOUS);
}
//判断券状态
if (userCoupon.getStatus().intValue() != CouponUseStatusEnum.NOT_USED.getCode()) {
logger.warn("user this coupon is used:{},{}", userCoupon.getUid(), userCoupon.getCouponCode());
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_USE_DOUBLE);
}
//判断生失效时间
if (userCoupon.getStartTime() > time || userCoupon.getEndTime() < time) {
logger.warn("coupon has expire or not arrive time:{},{},{}", userCoupon.getStartTime(), userCoupon.getEndTime(), time);
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_ALLOW_OR_EXPIRE);
}
//券模板的状态
Coupon coupon = couponCacheService.getCouponWithCache(userCoupon.getCouponToken());
if (coupon == null) {
logger.warn("coupons is null");
throw new ServiceException(ServiceError.PROMOTION_COUPON_NOT_EXISTS);
}
if (coupon.getStatus() != CouponsStatusEnum.VALID.getCode()) {
logger.warn("coupon status can't be use:{},{}", coupon.getId(), coupon.getStatus());
throw new ServiceException(ServiceError.PROMOTION_COUPON_IS_NOT_VAILD);
}
//所有的校验都通过了,生成bo
Map<Integer, List<Integer>> couponProductIdMap = getProductIdListMap(Lists.newArrayList(coupon));
List<Integer> productIds = couponProductIdMap.get(userCoupon.getCouponId());
// 单个获取couponType
CouponType couponType = couponCacheService.getCouponTypeWithCache(userCoupon.getCouponType() != null ? userCoupon.getCouponType().intValue() : null);
UserCouponsBo couponBo = CouponConvert.covertCouponBo(userCoupon, coupon, couponType);
couponBo.setProductIdInclude(productIds);
return couponBo;
}).collect(Collectors.toList());
return UserCouponsListBo.builder().coupons(couponBoList).build();
}
}
... ...