乱七八糟折扣计算 不包括会员整单折扣

This commit is contained in:
wangw 2025-10-24 15:40:51 +08:00
parent 96e3e9a68f
commit 56dfdcd523
3 changed files with 210 additions and 109 deletions

View File

@ -50,4 +50,18 @@ public class LimitRateDTO implements Serializable {
}
return foodIds;
}
public boolean isLimitFoodId(Long foodId) {
if (foodType == 1) {
return true;
} else {
Set<Long> foodIds = getFoodIds();
if (CollUtil.isEmpty(foodIds)) {
return false;
} else {
return foodIds.contains(foodId);
}
}
}
}

View File

@ -70,14 +70,13 @@ public class MkDiscountActivityServiceImpl extends ServiceImpl<MkDiscountActivit
throw new CzgException("满减活动与优惠券不可共用");
}
//检查是否开启了限时折扣
if (limitRateShare && activityDTO.getDiscountShare() == 0) {
throw new CzgException("满减活动与限时折扣不可共用");
}
// if (limitRateShare && activityDTO.getDiscountShare() == 0) {
// throw new CzgException("满减活动与限时折扣不可共用");
// }
//检查是否开启了会员抵扣
if (vipShare && activityDTO.getVipPriceShare() == 0) {
throw new CzgException("满减活动与会员价不可共用");
}
// if (vipShare && activityDTO.getVipPriceShare() == 0) {
// throw new CzgException("满减活动与会员价不可共用");
// }
//检查是否开启了积分抵扣
if (pointsShare && activityDTO.getPointsShare() == 0) {
throw new CzgException("满减活动与积分抵扣不可共用");

View File

@ -348,23 +348,38 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
log.info("订单信息:{},优惠信息:{}", JSONObject.toJSONString(orderInfo), JSONObject.toJSONString(param));
Long shopId = orderInfo.getShopId();
AssertUtil.isNull(shopId, "生成支付订单失败,订单信息异常");
//霸王餐不参与满减
if (param.isFreeDine() && param.getDiscountActId() != null) {
throw new ValidateException("生成订单失败,霸王餐不能享受满减活动");
}
MkDiscountActivityDTO discountAct = null;
// 满减活动校验 不为霸王餐 霸王餐不参与满减
if (!param.isFreeDine() && param.getDiscountActAmount().compareTo(BigDecimal.ZERO) > 0) {
// 满减活动校验
if (param.getDiscountActId() != null && param.getDiscountActAmount().compareTo(BigDecimal.ZERO) > 0) {
//检查满减活动是否开启
discountAct = discountActService.checkDiscountAct(param.getDiscountActId(),
CollUtil.isNotEmpty(param.getCouponList()), param.isVipPrice(), param.getLimitRate() != null,
param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0);
}
orderInfo.setSeatNum(param.getSeatNum());
if (shopInfo.getIsTableFee() != 1 && shopInfo.getTableFee().compareTo(BigDecimal.ZERO) != 0) {
orderInfo.setSeatAmount(new BigDecimal(param.getSeatNum()).multiply(shopInfo.getTableFee()));
} else {
orderInfo.setSeatAmount(BigDecimal.ZERO);
}
// 积分校验
PointsBasicSetting pointSetting = pointsBasicService.getById(shopId);
boolean usePointsDeduction = param.getPointsNum() > 0 || param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) > 0;
if (usePointsDeduction) {
if (pointSetting == null || !pointSetting.getEnableDeduction().equals(1)) {
throw new ValidateException("生成支付订单失败,该店铺未开启积分抵扣");
}
if (param.getUserId() == null) {
throw new ValidateException("生成支付订单失败,请选择用户后再使用积分抵扣");
}
//霸王餐
if (param.isFreeDine() && !param.isWithPoints()) {
throw new ValidateException("生成支付订单失败,霸王餐不支持积分抵扣");
}
} else {
if (param.getPointsNum() != 0 || param.getPointsDiscountAmount().compareTo(BigDecimal.ZERO) != 0) {
throw new ValidateException("生成支付订单失败,积分抵扣数量或金额不正确");
}
}
if (param.getPointsNum() > 0 && (!param.isFreeDine() || param.isWithPoints())) {
if (pointSetting == null || !pointSetting.getEnableDeduction().equals(1)) {
throw new ValidateException("生成支付订单失败,该店铺未开启积分抵扣");
@ -385,6 +400,10 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
orderInfo.setUserId(userInfo.getId());
}
//TODO 会员价开关
if (param.isVipPrice() && (shopUser == null || shopUser.getIsVip().equals(0))) {
throw new ValidateException("生成支付订单失败,仅会员可使用会员价");
}
List<OrderDetail> orderDetails = orderDetailService.queryChain().eq(OrderDetail::getOrderId, param.getOrderId()).select().list();
//总商品金额 不包含打包费 不包含临时菜
@ -411,90 +430,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
//优惠券部分 目前规则 每个券只能用一张
if (CollUtil.isNotEmpty(param.getCouponList()) && (!param.isFreeDine() || param.isWithCoupon())) {
QueryWrapper couponRecordQuery = new QueryWrapper();
couponRecordQuery.in(MkShopCouponRecord::getId, param.getCouponList());
couponRecordQuery.le(MkShopCouponRecord::getUseStartTime, LocalDateTime.now());
couponRecordQuery.ge(MkShopCouponRecord::getUseEndTime, LocalDateTime.now());
couponRecordQuery.eq(MkShopCouponRecord::getStatus, 0);
couponRecordQuery.ne(MkShopCouponRecord::getIsDel, 1);
List<MkShopCouponRecord> list = couponRecordService.list(couponRecordQuery);
if (CollUtil.isEmpty(list) || param.getCouponList().size() > list.size()) {
throw new ValidateException("生成支付订单失败,优惠券不可用");
}
Map<Integer, MkShopCouponRecord> couponRecordMap = list.stream().collect(Collectors.toMap(MkShopCouponRecord::getType, t -> t));
AtomicBoolean otherCouponShare = new AtomicBoolean(true);
couponRecordMap.forEach((type, record) -> {
ShopCoupon coupon = checkCoupon(record, param.isVipPrice());
boolean isTimeDiscountShare = coupon.getDiscountShare() == 1;
//1-满减券2-商品兑换券3-折扣券4-第二件半价券 6-买一送一券
// 7-固定价格券8-免配送费券 暂时没有
if (type == 2) {//商品券z
if (coupon.getOtherCouponShare() != 1) {
otherCouponShare.set(false);
}
if (coupon.getFullAmount().compareTo(BigDecimal.ZERO) > 0 && coupon.getFullAmount().compareTo(totalAmount.getPrice()) > 0) {
throw new ValidateException("生成支付订单失败,商品券:" + coupon + " 未达到使用门槛" + coupon.getFullAmount() + "");
}
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
foodsCalculate(orderDetails, isAllFoods, couponFoodIds, coupon.getDiscountNum(),
isTimeDiscountShare, "price_asc".equals(coupon.getUseRule()), prodCouponAmount);
} else if (type == 4 || type == 6) {//4-第二件半价券 6-买一送一券
if (!otherCouponShare.get()) {
throw new ValidateException("生成支付订单失败,商品券与其它券不可共用");
}
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
if (type == 6) {
oneGiftCalculate(orderDetails, isAllFoods, couponFoodIds, isTimeDiscountShare, "price_asc".equals(coupon.getUseRule()), oneGiftAmount);
} else {
twoHalfCalculate(orderDetails, isAllFoods, couponFoodIds, isTimeDiscountShare, "price_asc".equals(coupon.getUseRule()), twoHalfAmount);
}
} else if (type == 1 || type == 3) {//1-满减券 3-折扣券
if (!otherCouponShare.get()) {
throw new ValidateException("生成支付订单失败,商品券与其它券不可共用");
}
//计算门槛
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
thresholdCalculate(orderDetails, isAllFoods, couponFoodIds, coupon.getFullAmount());
if (type == 3) {
BigDecimal rate = new BigDecimal(100 - record.getDiscountRate());
rateAmount.setPrice(rate.multiply(totalAmount.getPrice().subtract(tempAmount.getPrice()).subtract(prodCouponAmount.getPrice()))
.divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN));
if (coupon.getMaxDiscountAmount().compareTo(BigDecimal.ZERO) > 0 && rateAmount.getPrice().compareTo(coupon.getMaxDiscountAmount()) > 0) {
rateAmount.setPrice(coupon.getMaxDiscountAmount());
}
} else {
fullReductionAmount.setPrice(record.getDiscountAmount());
}
}
});
}
if (prodCouponAmount.getPrice().compareTo(param.getProductCouponDiscountAmount()) != 0) {
log.info("支付计算金额不正确:商品券抵扣金额为:{},传递为:{}", prodCouponAmount.getPrice(), param.getProductCouponDiscountAmount());
throw new ValidateException("生成支付订单失败,商品优惠券优惠金额不正确");
}
couponExecute(orderDetails, param, totalAmount, tempAmount, prodCouponAmount,
oneGiftAmount, twoHalfAmount, rateAmount, fullReductionAmount);
orderInfo.setOriginAmount(param.getOriginAmount());
//总商品支付金额 不包含打包费 用来计算后续
BigDecimal newTotalAmount = totalAmount.getPrice();
@ -514,7 +451,16 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
if (newTotalAmount.compareTo(BigDecimal.ZERO) <= 0) {
newTotalAmount = BigDecimal.ZERO;
}
//优惠券金额结束 商品金额+打包费+餐位费
//优惠券金额结束
//餐位费
orderInfo.setSeatNum(param.getSeatNum());
if (shopInfo.getIsTableFee() != 1 && shopInfo.getTableFee().compareTo(BigDecimal.ZERO) != 0) {
orderInfo.setSeatAmount(new BigDecimal(param.getSeatNum()).multiply(shopInfo.getTableFee()));
} else {
orderInfo.setSeatAmount(BigDecimal.ZERO);
}
//商品金额+打包费+餐位费
newTotalAmount = newTotalAmount.add(packAmount.getPrice()).add(orderInfo.getSeatAmount());
//新客立减
@ -524,12 +470,15 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
param.setNewCustomerDiscountAmount(BigDecimal.ZERO);
}
//满减活动
BigDecimal discountActAmount = calculateDiscountActAmount(discountAct, newTotalAmount);
BigDecimal discountActAmount = calculateDiscountActAmount(orderDetails, discountAct);
if (discountActAmount.compareTo(param.getDiscountActAmount()) != 0) {
log.info("满减活动金额不正确:传递为:{},计算为:{}", param.getDiscountActAmount(), discountActAmount);
throw new ValidateException("生成支付订单失败,满减活动金额不正确");
}
newTotalAmount = newTotalAmount.subtract(discountActAmount);
//TODO 会员整单折扣
//积分抵扣 金额范围校验 抵扣金额校验
if (param.getPointsNum() > 0) {
if (pointSetting.getMinPaymentAmount().compareTo(newTotalAmount) > 0) {
@ -542,8 +491,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
if (pointAmount.compareTo(param.getPointsDiscountAmount()) != 0) {
throw new ValidateException("生成支付订单失败,积分抵扣金额不正确");
}
newTotalAmount = newTotalAmount.subtract(param.getPointsDiscountAmount());
}
newTotalAmount = newTotalAmount.subtract(param.getPointsDiscountAmount());
//商家最终改价
if (param.getDiscountAmount().compareTo(BigDecimal.ZERO) > 0) {
newTotalAmount = newTotalAmount.subtract(param.getDiscountAmount());
@ -626,6 +575,99 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
return CzgResult.success();
}
/**
* 优惠券部分 计算
*/
private void couponExecute(List<OrderDetail> orderDetails, CheckOrderPay param, BigDecimalDTO totalAmount, BigDecimalDTO tempAmount,
BigDecimalDTO prodCouponAmount, BigDecimalDTO oneGiftAmount, BigDecimalDTO twoHalfAmount,
BigDecimalDTO rateAmount, BigDecimalDTO fullReductionAmount) {
if (CollUtil.isNotEmpty(param.getCouponList()) && (!param.isFreeDine() || param.isWithCoupon())) {
QueryWrapper couponRecordQuery = new QueryWrapper();
couponRecordQuery.in(MkShopCouponRecord::getId, param.getCouponList());
// couponRecordQuery.le(MkShopCouponRecord::getUseStartTime, LocalDateTime.now());
// couponRecordQuery.ge(MkShopCouponRecord::getUseEndTime, LocalDateTime.now());
couponRecordQuery.eq(MkShopCouponRecord::getStatus, 0);
couponRecordQuery.ne(MkShopCouponRecord::getIsDel, 1);
List<MkShopCouponRecord> list = couponRecordService.list(couponRecordQuery);
if (CollUtil.isEmpty(list) || param.getCouponList().size() > list.size()) {
throw new ValidateException("生成支付订单失败,优惠券不可用");
}
Map<Integer, MkShopCouponRecord> couponRecordMap = list.stream().collect(Collectors.toMap(MkShopCouponRecord::getType, t -> t));
AtomicBoolean otherCouponShare = new AtomicBoolean(true);
couponRecordMap.forEach((type, record) -> {
ShopCoupon coupon = checkCoupon(record, param.isVipPrice());
boolean isTimeDiscountShare = coupon.getDiscountShare() == 1;
boolean isVipShare = coupon.getVipPriceShare() == 1;
//1-满减券2-商品兑换券3-折扣券4-第二件半价券 6-买一送一券
// 7-固定价格券8-免配送费券 暂时没有
if (type == 2) {//商品券z
if (coupon.getOtherCouponShare() != 1) {
otherCouponShare.set(false);
}
thresholdCalculation(orderDetails, coupon.getFullAmount(), true, null,
param.isVipPrice(), isTimeDiscountShare, isVipShare, "商品");
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
foodsCalculate(orderDetails, isTimeDiscountShare,
isAllFoods, couponFoodIds, coupon.getDiscountNum(),
"price_asc".equals(coupon.getUseRule()), prodCouponAmount);
} else if (type == 4 || type == 6) {//4-第二件半价券 6-买一送一券
if (!otherCouponShare.get()) {
throw new ValidateException("生成支付订单失败,商品券与其它券不可共用");
}
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
if (type == 6) {
oneGiftCalculate(orderDetails, isAllFoods, couponFoodIds, isTimeDiscountShare, "price_asc".equals(coupon.getUseRule()), oneGiftAmount);
} else {
twoHalfCalculate(orderDetails, isAllFoods, couponFoodIds, isTimeDiscountShare, "price_asc".equals(coupon.getUseRule()), twoHalfAmount);
}
} else if (type == 1 || type == 3) {//1-满减券 3-折扣券
if (!otherCouponShare.get()) {
throw new ValidateException("生成支付订单失败,商品券与其它券不可共用");
}
//计算门槛
boolean isAllFoods = true;
List<Long> couponFoodIds = new ArrayList<>();
if (StrUtil.isNotBlank(coupon.getFoods()) && !",".equals(coupon.getFoods())) {
couponFoodIds = Arrays.stream(coupon.getFoods().split(",")).map(Long::parseLong).toList();
if (CollUtil.isNotEmpty(couponFoodIds)) {
isAllFoods = false;
}
}
thresholdCalculation(orderDetails, coupon.getFullAmount(), isAllFoods, couponFoodIds,
param.isVipPrice(), isTimeDiscountShare, isVipShare, type == 1 ? "满减券" : "折扣券");
if (type == 3) {
BigDecimal rate = new BigDecimal(100 - record.getDiscountRate());
rateAmount.setPrice(rate.multiply(totalAmount.getPrice().subtract(tempAmount.getPrice()).subtract(prodCouponAmount.getPrice()))
.divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN));
if (coupon.getMaxDiscountAmount().compareTo(BigDecimal.ZERO) > 0 && rateAmount.getPrice().compareTo(coupon.getMaxDiscountAmount()) > 0) {
rateAmount.setPrice(coupon.getMaxDiscountAmount());
}
} else {
fullReductionAmount.setPrice(record.getDiscountAmount());
}
}
});
}
if (prodCouponAmount.getPrice().compareTo(param.getProductCouponDiscountAmount()) != 0) {
log.info("支付计算金额不正确:商品券抵扣金额为:{},传递为:{}", prodCouponAmount.getPrice(), param.getProductCouponDiscountAmount());
throw new ValidateException("生成支付订单失败,商品优惠券优惠金额不正确");
}
}
/**
* 优惠券通用校验规则 不包括门槛校验
@ -635,9 +677,9 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
private ShopCoupon checkCoupon(MkShopCouponRecord record, boolean isVipPrice) {
ShopCoupon coupon = couponService.getById(record.getCouponId());
AssertUtil.isNull(coupon, "生成支付订单失败,券信息不存在");
if (isVipPrice && coupon.getVipPriceShare() != 1) {
throw new ValidateException("生成支付订单失败,券:" + coupon.getTitle() + "与会员价不共享");
}
// if (isVipPrice && coupon.getVipPriceShare() != 1) {
// throw new ValidateException("生成支付订单失败,券:" + coupon.getTitle() + "与会员价不共享");
// }
isUseLimit(coupon, record);
// isUseTime(coupon);
return coupon;
@ -687,11 +729,50 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
}
/**
* 优惠券 门槛 计算 赠送商品和临时商品不参与计算
*
* @param orderDetails 订单商品列表
* @param isAllFoods 是否所有商品
* @param couponFoodIds 参与门槛计算商品id列表
* @param fullAmount 门槛金额
* @param isVipPrice 是否使用会员价
* @param isTimeDiscountShare 门槛计算是否限时折扣共享
* @param isVipShare 门槛计算是否与会员价共享
*/
private void thresholdCalculation(List<OrderDetail> orderDetails, BigDecimal fullAmount, boolean isAllFoods, List<Long> couponFoodIds,
boolean isVipPrice, boolean isTimeDiscountShare, boolean isVipShare, String title) {
BigDecimal calculationAmount = BigDecimal.ZERO;
for (OrderDetail orderDetail : orderDetails) {
if (orderDetail.getIsGift() == 1 || orderDetail.getIsTemporary() == 1) {
continue;
}
if (!isAllFoods || !couponFoodIds.contains(orderDetail.getProductId())) {
continue;
}
BigDecimal num = orderDetail.getNum().subtract(orderDetail.getReturnNum());
if (orderDetail.getDiscountSaleAmount().compareTo(BigDecimal.ZERO) > 0) {
//单品改价 按原价计算门槛金额
calculationAmount = calculationAmount.add(orderDetail.getPrice().multiply(num).setScale(2, RoundingMode.HALF_UP));
} else {
if (orderDetail.getIsTimeDiscount() == 1 && isTimeDiscountShare) {
calculationAmount = calculationAmount.add(orderDetail.getUnitPrice().multiply(num).setScale(2, RoundingMode.HALF_UP));
} else if (isVipPrice && isVipShare) {
calculationAmount = calculationAmount.add(orderDetail.getUnitPrice().multiply(num).setScale(2, RoundingMode.HALF_UP));
}
}
}
if (calculationAmount.compareTo(fullAmount) < 0) {
throw new ValidateException("生成支付订单失败," + title + "优惠券门槛金额不足");
}
}
/**
* 商品优惠券计算
*/
private void foodsCalculate(List<OrderDetail> orderDetails, boolean isAllFoods, List<Long> couponFoodIds, int discountNum,
boolean isTimeDiscountShare, boolean isAsc, BigDecimalDTO prodCouponAmount) {
private void foodsCalculate(List<OrderDetail> orderDetails, boolean isTimeDiscountShare,
boolean isAllFoods, List<Long> couponFoodIds, int discountNum,
boolean isAsc, BigDecimalDTO prodCouponAmount) {
orderDetails = getDetailsSort(orderDetails, isAsc);
// log.info("商品券 计算 orderDetails:{}", orderDetails);
BigDecimal remaining = new BigDecimal(discountNum);
@ -812,10 +893,17 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
/**
* 满减活动 校验门槛获取减免金额
*/
private BigDecimal calculateDiscountActAmount(MkDiscountActivityDTO discountAct, BigDecimal originalPrice) {
private BigDecimal calculateDiscountActAmount(List<OrderDetail> orderDetails, MkDiscountActivityDTO discountAct) {
if (discountAct == null) {
return BigDecimal.ZERO;
}
BigDecimal originalPrice = BigDecimal.ZERO;
for (OrderDetail orderDetail : orderDetails) {
if (orderDetail.getIsGift() == 1 || orderDetail.getIsTemporary() == 1) {
continue;
}
originalPrice = originalPrice.add(orderDetail.getPayAmount());
}
for (MkDiscountThreshold threshold : discountAct.getThresholds()) {
if (originalPrice.compareTo(threshold.getFullAmount()) >= 0) {
return threshold.getDiscountAmount();