后端:商品价格计算,接入促销活动

H5 前端:购物车接入促销
This commit is contained in:
YunaiV 2019-04-17 20:34:49 +08:00
parent 355c53df66
commit d672976b84
13 changed files with 360 additions and 122 deletions

View File

@ -1 +0,0 @@
##################### 业务模块 #####################

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 使用驼峰命名法转换字段。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="Integer" type="java.lang.Integer"/>
<typeAlias alias="Long" type="java.lang.Long"/>
<typeAlias alias="HashMap" type="java.util.HashMap"/>
<typeAlias alias="LinkedHashMap" type="java.util.LinkedHashMap"/>
<typeAlias alias="ArrayList" type="java.util.ArrayList"/>
<typeAlias alias="LinkedList" type="java.util.LinkedList"/>
</typeAliases>
</configuration>

View File

@ -14,20 +14,19 @@
<!-- <van-checkbox :name="item.id" />--> <!-- <van-checkbox :name="item.id" />-->
<!-- <product-card :product='item' :iscard='false' >--> <!-- <product-card :product='item' :iscard='false' >-->
<!-- <template slot>-->
<!-- <van-cell value="修改" >-->
<!-- <template slot="title">-->
<!-- <van-tag type="danger">促销</van-tag>-->
<!-- <span class="van-cell-text" >满60元减5元</span>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- </template>-->
<!-- </product-card>--> <!-- </product-card>-->
<!-- </div>--> <!-- </div>-->
<div v-for="(itemGroup, i) in itemGroups" class="card-goods__item"> <div v-for="(itemGroup, i) in itemGroups" class="card-goods__item">
<van-cell >
<template v-if="itemGroup.activity" slot="title">
<van-tag type="danger">满减送</van-tag>
<span class="van-cell-text" > {{ formatFullPrivilegeText(itemGroup.activity) }} </span>
</template>
</van-cell>
<div class="card" v-for="(item, j) in itemGroup.items" :key="j"> <div class="card" v-for="(item, j) in itemGroup.items" :key="j">
<van-checkbox :key="item.id" :name="item.id" v-model="item.selected" style="position: relative;" /> <van-checkbox :key="item.id" :name="item.id" v-model="item.selected" style="position: relative;top: 40px;" />
<product-card :product='convertProduct(item)'/> <product-card :product='convertProduct(item)'/>
</div> </div>
<div style="height:15px;"></div> <div style="height:15px;"></div>
@ -47,13 +46,14 @@
<div style="height:50px;"></div> <div style="height:50px;"></div>
<van-submit-bar <van-submit-bar
:tip="this.formatItemGroupDiscountPriceText()"
:price="fee.presentTotal" :price="fee.presentTotal"
:disabled="!checkedItemIds || !checkedItemIds.length" :disabled="!checkedItemIds || !checkedItemIds.length"
:button-text="submitBarText" :button-text="submitBarText"
@submit="onSubmit" @submit="onSubmit"
> >
<template slot> <template slot>
<van-checkbox v-model="checkedAll" @change="onSelectAll">全选</van-checkbox> <van-checkbox v-model="checkedAll" @click="onSelectAll">全选</van-checkbox>
</template> </template>
</van-submit-bar> </van-submit-bar>
</div> </div>
@ -87,6 +87,39 @@ export default {
}, },
}, },
methods: { methods: {
formatFullPrivilegeText(activity) {
let text = '';
let fullPrivilege = activity.fullPrivilege;
for (let i in fullPrivilege.privileges) {
let privilege = fullPrivilege.privileges[i];
if (i > 0) {
text += ';';
}
if (fullPrivilege.cycled) {
text += '每';
}
if (privilege.meetType === 1) {
text += '满 ' + privilege.meetValue / 100.0 + ' 元,';
} else if (privilege.meetType === 2) {
text += '满 ' + privilege.meetValue + ' 件,';
}
if (privilege.preferentialType === 1) {
text += '减 ' + privilege.preferentialValue / 100.0 + ' 元';
} else if (privilege.preferentialType === 2) {
text += '打 ' + privilege.preferentialValue / 10.0 + ' 折';
}
}
return text;
},
formatItemGroupDiscountPriceText() {
let price = 0;
for (let i in this.itemGroups) {
let itemGroup = this.itemGroups[i];
price += itemGroup.fee.discountTotal;
}
return price > 0 ? '立减 ' + price / 100.0 + ' 元' : '';
},
calCheckedItemIds() { calCheckedItemIds() {
// debugger; // debugger;
let itemIds = []; let itemIds = [];
@ -122,11 +155,10 @@ export default {
// checkedItemIds + checkedAll // checkedItemIds + checkedAll
this.calCheckedItemIds(); this.calCheckedItemIds();
}, },
onItemSelectedChange(newVal) { onItemSelectedChange(newVal) { // TODO
if (!this.checkedItemIds) { if (!this.checkedItemIds) {
return; return;
} }
// debugger;
let selected; let selected;
let diffItemIds; let diffItemIds;
if (newVal.length > this.oldCheckedItemIds.length) { // if (newVal.length > this.oldCheckedItemIds.length) { //
@ -152,9 +184,17 @@ export default {
if (this.checkedAll === undefined) { if (this.checkedAll === undefined) {
return; return;
} }
updateCartSelected(this.getItemIds(), newVal).then(data => { // debugger;
this.handleData(data); // updateCartSelected(this.getItemIds(), newVal).then(data => {
}) // this.handleData(data);
// })
if (newVal) {
this.onItemSelectedChange(this.getItemIds());
} else {
// alert(' bug ');
// this.onItemSelectedChange(this.getItemIds());
// TODO bug
}
}, },
onSubmit() { onSubmit() {
this.$router.push('/order?from=cart') this.$router.push('/order?from=cart')
@ -164,7 +204,7 @@ export default {
return { return {
...item.spu, ...item.spu,
quantity: item.buyQuantity, quantity: item.buyQuantity,
price: item.price, price: item.discountPrice || item.price,
sku: { sku: {
...item, ...item,
spu: undefined, spu: undefined,

View File

@ -248,7 +248,7 @@
text += '每'; text += '每';
} }
if (privilege.meetType === 1) { if (privilege.meetType === 1) {
text += '满 ' + privilege.meetValue + ' 元,'; text += '满 ' + privilege.meetValue / 100.0 + ' 元,';
} else if (privilege.meetType === 2) { } else if (privilege.meetType === 2) {
text += '满 ' + privilege.meetValue + ' 件,'; text += '满 ' + privilege.meetValue + ' 件,';
} }

View File

@ -1,6 +1,7 @@
package cn.iocoder.mall.order.application.vo; package cn.iocoder.mall.order.application.vo;
import cn.iocoder.mall.product.api.bo.ProductAttrAndValuePairBO; import cn.iocoder.mall.product.api.bo.ProductAttrAndValuePairBO;
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -30,12 +31,30 @@ public class UsersCartDetailVO {
@Accessors(chain = true) @Accessors(chain = true)
public static class ItemGroup { public static class ItemGroup {
// TODO 优惠活动 /**
private Object activity; * 优惠活动
*/
private PromotionActivityBO activity; // TODO 芋艿偷懒
/**
* 优惠活动是否生效
*
* 多个商品参与某个活动因为并发达到条件所以会存在未生效的情况所以一共有三种情况
*
* 1. activity 非空activityEffectEffective true参与活动且生效
* 2. activity 非空activityEffectEffective false 参与活动并未生效
* 3. activity 为空activityEffectEffective 为空并未参与活动
*/
private Boolean activityEffectEffective;
/** /**
* 商品数组 * 商品数组
*/ */
private List<Sku> items; private List<Sku> items;
/**
* 费用
*
* TODO 芋艿这里先偷懒postageTotal 字段用不到
*/
private Fee fee;
} }
@ -79,6 +98,20 @@ public class UsersCartDetailVO {
* 是否选中 * 是否选中
*/ */
private Boolean selected; private Boolean selected;
/**
* 优惠活动
*/
private PromotionActivityBO activity;
/**
* 折扣价
*/
private Integer discountPrice;
/**
* 费用
*
* TODO 芋艿这里先偷懒postageTotal 字段用不到
*/
private Fee fee;
} }
@ -151,7 +184,7 @@ public class UsersCartDetailVO {
} }
/** /**
* 邮费信息 * 邮费信息 TODO 芋艿未完成
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)

View File

@ -1,6 +1,7 @@
package cn.iocoder.mall.order.application.vo; package cn.iocoder.mall.order.application.vo;
import cn.iocoder.mall.product.api.bo.ProductAttrAndValuePairBO; import cn.iocoder.mall.product.api.bo.ProductAttrAndValuePairBO;
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -28,8 +29,12 @@ public class UsersOrderConfirmCreateVO {
@Accessors(chain = true) @Accessors(chain = true)
public static class ItemGroup { public static class ItemGroup {
// TODO 优惠活动 /**
private Object activity; * 优惠活动
*/
// TODO 芋艿目前只会有满减送的情况未来有新的促销方式可能需要改成数组
// TODO 芋艿后面改成 VO
private PromotionActivityBO activity;
/** /**
* 商品数组 * 商品数组
*/ */
@ -73,6 +78,10 @@ public class UsersOrderConfirmCreateVO {
* 购买数量 * 购买数量
*/ */
private Integer buyQuantity; private Integer buyQuantity;
/**
* 折扣价
*/
private Integer discountPrice;
} }

View File

@ -41,10 +41,26 @@ public class CalcOrderPriceBO {
*/ */
// TODO 芋艿目前只会有满减送的情况未来有新的促销方式可能需要改成数组 // TODO 芋艿目前只会有满减送的情况未来有新的促销方式可能需要改成数组
private PromotionActivityBO activity; private PromotionActivityBO activity;
/**
* 优惠活动是否生效
*
* 多个商品参与某个活动因为并发达到条件所以会存在未生效的情况所以一共有三种情况
*
* 1. activity 非空activityEffectEffective true参与活动且生效
* 2. activity 非空activityEffectEffective false 参与活动并未生效
* 3. activity 为空activityEffectEffective 为空并未参与活动
*/
private Boolean activityEffectEffective;
/** /**
* 商品数组 * 商品数组
*/ */
private List<Item> items; private List<Item> items;
/**
* 费用
*
* TODO 芋艿这里先偷懒postageTotal 字段用不到
*/
private Fee fee;
} }
@ -70,6 +86,10 @@ public class CalcOrderPriceBO {
* TODO 芋艿这里先偷懒postageTotal 字段用不到 * TODO 芋艿这里先偷懒postageTotal 字段用不到
*/ */
private Fee fee; private Fee fee;
/**
* 折扣价
*/
private Integer discountPrice;
} }

View File

@ -1,24 +0,0 @@
package cn.iocoder.mall.order.api.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 购物车明细 BO
*/
@Data
@Accessors(chain = true)
public class CartBO {
/**
* 商品分组数组
*/
private List<CartItemGroupBO> itemGroups;
/**
* 费用
*/
private FeeMessageBO fee;
}

View File

@ -1,4 +0,0 @@
package cn.iocoder.mall.order.api.bo;
public class FeeDetailBO {
}

View File

@ -1,19 +0,0 @@
package cn.iocoder.mall.order.api.bo;
import java.util.List;
/**
* 商家商品分组
*/
public class MerchantItemGroup {
/**
* 商品分组数组
*/
private List<CartItemGroupBO> itemGroups;
/**
* 运费详情
*/
private PostageDetailBO postageDetail;
}

View File

@ -18,12 +18,11 @@ import cn.iocoder.mall.product.api.bo.ProductSkuBO;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO; import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import cn.iocoder.mall.promotion.api.PromotionActivityService; import cn.iocoder.mall.promotion.api.PromotionActivityService;
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO; import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
import cn.iocoder.mall.promotion.api.constant.PreferentialTypeEnum; import cn.iocoder.mall.promotion.api.constant.*;
import cn.iocoder.mall.promotion.api.constant.PromotionActivityStatusEnum;
import cn.iocoder.mall.promotion.api.constant.PromotionActivityTypeEnum;
import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -155,43 +154,41 @@ public class CartServiceImpl implements CartService {
public CommonResult<CalcOrderPriceBO> calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO) { public CommonResult<CalcOrderPriceBO> calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO) {
// 校验商品都存在 // 校验商品都存在
Map<Integer, CalcOrderPriceDTO.Item> calcOrderItemMap = calcOrderPriceDTO.getItems().stream() Map<Integer, CalcOrderPriceDTO.Item> calcOrderItemMap = calcOrderPriceDTO.getItems().stream()
.collect(Collectors.toMap(CalcOrderPriceDTO.Item::getSkuId, item -> item)); .collect(Collectors.toMap(CalcOrderPriceDTO.Item::getSkuId, item -> item)); // KEYskuId
List<ProductSkuDetailBO> skus = productSpuService.getProductSkuDetailList(calcOrderItemMap.keySet()).getData(); List<ProductSkuDetailBO> skus = productSpuService.getProductSkuDetailList(calcOrderItemMap.keySet()).getData();
if (skus.size() != calcOrderPriceDTO.getItems().size()) { if (skus.size() != calcOrderPriceDTO.getItems().size()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_ITEM_SOME_NOT_EXISTS.getCode()); return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_ITEM_SOME_NOT_EXISTS.getCode());
} }
// TODO 库存相关 // TODO 库存相关
// TODO 获得促销活动 // 查询促销活动
// TODO 处理促销相关信息 CommonResult<List<PromotionActivityBO>> activityListResult = promotionActivityService.getPromotionActivityListBySpuIds(
// 拼装结果 skus.stream().map(sku -> sku.getSpu().getId()).collect(Collectors.toSet()),
Collections.singletonList(PromotionActivityStatusEnum.RUN.getValue()));
if (activityListResult.isError()) {
return CommonResult.error(activityListResult);
}
List<PromotionActivityBO> activityList = activityListResult.getData();
// 拼装结果主要是计算价格
CalcOrderPriceBO calcOrderPriceBO = new CalcOrderPriceBO(); CalcOrderPriceBO calcOrderPriceBO = new CalcOrderPriceBO();
// 1. 商品分组 // 1. 创建初始的每一项的数组
CalcOrderPriceBO.ItemGroup itemGroup0 = new CalcOrderPriceBO.ItemGroup() List<CalcOrderPriceBO.Item> items = initCalcOrderPriceItems(skus, calcOrderItemMap);
.setItems(new ArrayList<>()); // 2. 计算限时折扣促销
for (ProductSkuDetailBO sku : skus) { modifyPriceByTimeLimitDiscount(items, activityList);
CalcOrderPriceBO.Item item = CartConvert.INSTANCE.convert(sku); // 3. 计算满减送促销
// 将是否选中购物数量复制到 item List<CalcOrderPriceBO.ItemGroup> itemGroups = groupByFullPrivilege(items, activityList);
CalcOrderPriceDTO.Item calcOrderItem = calcOrderItemMap.get(sku.getId()); calcOrderPriceBO.setItemGroups(itemGroups);
item.setSelected(calcOrderItem.getSelected()); // 4. 计算最终的价格
item.setBuyQuantity(calcOrderItem.getQuantity()); Integer originalTotal = 0;
// 添加到 itemGroup Integer presentTotal = 0;
itemGroup0.getItems().add(item); Integer discountTotal = 0;
}
calcOrderPriceBO.setItemGroups(Collections.singletonList(itemGroup0));
// 2. 计算价格
CalcOrderPriceBO.Fee fee = new CalcOrderPriceBO.Fee(0, 0, 0, 0);
for (CalcOrderPriceBO.ItemGroup itemGroup : calcOrderPriceBO.getItemGroups()) { for (CalcOrderPriceBO.ItemGroup itemGroup : calcOrderPriceBO.getItemGroups()) {
int originalTotal = 0; originalTotal += itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getFee().getOriginalTotal() : 0).sum();
for (CalcOrderPriceBO.Item item : itemGroup.getItems()) { discountTotal += itemGroup.getFee().getDiscountTotal() + itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getFee().getDiscountTotal() : 0).sum();
if (!item.getSelected()) { // 未选中则不计算到其中 presentTotal += itemGroup.getFee().getPresentTotal();
continue;
}
originalTotal += item.getPrice() * item.getBuyQuantity();
}
fee.setOriginalTotal(fee.getOriginalTotal() + originalTotal);
fee.setPresentTotal(fee.getOriginalTotal()); // TODO 芋艿后续要计算优惠价格
} }
calcOrderPriceBO.setFee(fee); Assert.isTrue(originalTotal - discountTotal == presentTotal,
String.format("价格合计( %d - %d == %d )不正确", originalTotal, discountTotal, presentTotal));
calcOrderPriceBO.setFee(new CalcOrderPriceBO.Fee(originalTotal, discountTotal, 0, presentTotal));
// 返回 // 返回
return CommonResult.success(calcOrderPriceBO); return CommonResult.success(calcOrderPriceBO);
} }
@ -229,7 +226,107 @@ public class CartServiceImpl implements CartService {
.setOriginalPrice(sku.getPrice()).setPresentPrice(presentPrice)); .setOriginalPrice(sku.getPrice()).setPresentPrice(presentPrice));
} }
private List<CalcOrderPriceBO.Item> initCalcOrderPriceItems(List<ProductSkuDetailBO> skus,
Map<Integer, CalcOrderPriceDTO.Item> calcOrderItemMap) {
List<CalcOrderPriceBO.Item> items = new ArrayList<>();
for (ProductSkuDetailBO sku : skus) {
CalcOrderPriceBO.Item item = CartConvert.INSTANCE.convert(sku);
items.add(item);
// 将是否选中购物数量复制到 item
CalcOrderPriceDTO.Item calcOrderItem = calcOrderItemMap.get(sku.getId());
item.setSelected(calcOrderItem.getSelected());
item.setBuyQuantity(calcOrderItem.getQuantity());
// 计算初始价格
CalcOrderPriceBO.Fee fee = new CalcOrderPriceBO.Fee(0, 0, 0, 0);
fee.setOriginalTotal(item.getPrice() * item.getBuyQuantity());
fee.setPresentTotal(fee.getOriginalTotal());
item.setFee(fee);
}
return items;
}
private void modifyPriceByTimeLimitDiscount(List<CalcOrderPriceBO.Item> items, List<PromotionActivityBO> activityList) {
for (CalcOrderPriceBO.Item item : items) {
// 获得符合条件的限时折扣
PromotionActivityBO timeLimitedDiscount = activityList.stream()
.filter(activity -> PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType())
&& activity.getTimeLimitedDiscount().getItems().stream().anyMatch(item0 -> item0.getSpuId().equals(item.getSpu().getId())))
.findFirst().orElse(null);
if (timeLimitedDiscount == null) {
continue;
}
// 计算价格
ProductSkuBO sku = new ProductSkuBO().setId(item.getId()).setSpuId(item.getSpu().getId()).setPrice(item.getPrice());
Integer newPrice = calcSkuPriceByTimeLimitDiscount(sku, timeLimitedDiscount);
if (newPrice.equals(item.getPrice())) {
continue;
}
// 设置优惠
item.setActivity(timeLimitedDiscount);
// 设置价格
item.setDiscountPrice(newPrice);
CalcOrderPriceBO.Fee fee = item.getFee();
fee.setDiscountTotal(fee.getDiscountTotal() + (item.getPrice() - newPrice) * item.getBuyQuantity());
fee.setPresentTotal(fee.getOriginalTotal() - fee.getDiscountTotal() + fee.getPostageTotal());
}
}
private List<CalcOrderPriceBO.ItemGroup> groupByFullPrivilege(List<CalcOrderPriceBO.Item> items, List<PromotionActivityBO> activityList) {
List<CalcOrderPriceBO.ItemGroup> itemGroups = new ArrayList<>();
// 获得所有满减送促销
List<PromotionActivityBO> fullPrivileges = activityList.stream()
.filter(activity -> PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()))
.collect(Collectors.toList());
// 基于满减送促销进行分组
if (!fullPrivileges.isEmpty()) {
items = new ArrayList<>(items); // 因为下面会修改数组进行浅拷贝避免影响传入的 items
for (PromotionActivityBO fullPrivilege : fullPrivileges) {
// 创建 fullPrivilege 对应的分组
CalcOrderPriceBO.ItemGroup itemGroup = new CalcOrderPriceBO.ItemGroup()
.setActivity(fullPrivilege)
.setItems(new ArrayList<>());
// 筛选商品到分组中
for (Iterator<CalcOrderPriceBO.Item> iterator = items.iterator(); iterator.hasNext(); ) {
CalcOrderPriceBO.Item item = iterator.next();
if (!isSpuMatchFullPrivilege(item.getSpu().getId(), fullPrivilege)) {
continue;
}
itemGroup.getItems().add(item);
iterator.remove();
}
// 如果匹配到则添加到 itemGroups
if (!itemGroup.getItems().isEmpty()) {
itemGroups.add(itemGroup);
}
}
}
// 处理未参加活动的商品形成一个分组
if (!items.isEmpty()) {
CalcOrderPriceBO.ItemGroup itemGroup = new CalcOrderPriceBO.ItemGroup()
.setItems(items);
itemGroups.add(itemGroup);
}
// 计算每个分组的价格
for (CalcOrderPriceBO.ItemGroup itemGroup : itemGroups) {
itemGroup.setFee(calcSkuPriceByFullPrivilege(itemGroup));
itemGroup.setActivityEffectEffective(itemGroup.getFee().getDiscountTotal() > 0);
}
// 返回结果
return itemGroups;
}
/**
* 计算指定 SKU 在限时折扣下的价格
*
* @param sku SKU
* @param timeLimitedDiscount 限时折扣促销
* 传入的该活动要保证该 SKU 在该促销下一定有优惠
* @return 计算后的价格
*/
private Integer calcSkuPriceByTimeLimitDiscount(ProductSkuBO sku, PromotionActivityBO timeLimitedDiscount) { private Integer calcSkuPriceByTimeLimitDiscount(ProductSkuBO sku, PromotionActivityBO timeLimitedDiscount) {
if (timeLimitedDiscount == null) {
return sku.getPrice();
}
// 获得对应的优惠项 // 获得对应的优惠项
PromotionActivityBO.TimeLimitedDiscount.Item item = timeLimitedDiscount.getTimeLimitedDiscount().getItems().stream() PromotionActivityBO.TimeLimitedDiscount.Item item = timeLimitedDiscount.getTimeLimitedDiscount().getItems().stream()
.filter(item0 -> item0.getSpuId().equals(sku.getSpuId())) .filter(item0 -> item0.getSpuId().equals(sku.getSpuId()))
@ -244,15 +341,84 @@ public class CartServiceImpl implements CartService {
return presentPrice >= 0 ? presentPrice : sku.getPrice(); // 如果计算优惠价格小于 0 则说明无法使用优惠 return presentPrice >= 0 ? presentPrice : sku.getPrice(); // 如果计算优惠价格小于 0 则说明无法使用优惠
} }
if (PreferentialTypeEnum.DISCOUNT.getValue().equals(item.getPreferentialType())) { // 打折 if (PreferentialTypeEnum.DISCOUNT.getValue().equals(item.getPreferentialType())) { // 打折
return sku.getPrice() * item.getPreferentialValue() / 10; return sku.getPrice() * item.getPreferentialValue() / 100;
} }
throw new IllegalArgumentException(String.format("折扣活动(%s) 的优惠类型不正确", timeLimitedDiscount.toString())); throw new IllegalArgumentException(String.format("折扣活动(%s) 的优惠类型不正确", timeLimitedDiscount.toString()));
} }
private CalcOrderPriceBO.Fee calcSkuPriceByFullPrivilege(CalcOrderPriceBO.ItemGroup itemGroup) {
if (itemGroup.getActivity() == null) {
Integer originalTotal = itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getFee().getPresentTotal() : 0).sum();
return new CalcOrderPriceBO.Fee(originalTotal, 0, 0, originalTotal);
}
PromotionActivityBO activity = itemGroup.getActivity();
Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
"传入的必须的满减送活动必须是满减送");
// 获得优惠信息
Integer itemCnt = itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getBuyQuantity() : 0).sum();
Integer originalTotal = itemGroup.getItems().stream().mapToInt(item -> item.getSelected() ? item.getFee().getPresentTotal() : 0).sum();
List<PromotionActivityBO.FullPrivilege.Privilege> privileges = activity.getFullPrivilege().getPrivileges().stream()
.filter(privilege -> {
if (MeetTypeEnum.PRICE.getValue().equals(privilege.getMeetType())) {
return originalTotal >= privilege.getMeetValue();
}
if (MeetTypeEnum.QUANTITY.getValue().equals(privilege.getMeetType())) {
return itemCnt >= privilege.getMeetValue();
}
throw new IllegalArgumentException(String.format("满减送活动(%s) 的匹配(%s)不正确", itemGroup.getActivity().toString(), privilege.toString()));
}).collect(Collectors.toList());
// 获得不到优惠信息返回原始价格
if (privileges.isEmpty()) {
return new CalcOrderPriceBO.Fee(originalTotal, 0, 0, originalTotal);
}
// 获得到优惠信息进行价格计算
PromotionActivityBO.FullPrivilege.Privilege privilege = privileges.get(privileges.size() - 1);
Integer presentTotal;
if (PreferentialTypeEnum.PRICE.getValue().equals(privilege.getPreferentialType())) { // 减价
// 计算循环次数这样后续优惠的金额就是相乘了
Integer cycleCount = 1;
if (activity.getFullPrivilege().getCycled()) {
if (MeetTypeEnum.PRICE.getValue().equals(privilege.getMeetType())) {
cycleCount = originalTotal / privilege.getMeetValue();
} else if (MeetTypeEnum.QUANTITY.getValue().equals(privilege.getMeetType())) {
cycleCount = itemCnt / privilege.getMeetValue();
}
}
presentTotal = originalTotal - cycleCount * privilege.getMeetValue();
if (presentTotal < 0) { // 如果计算优惠价格小于 0 则说明无法使用优惠
presentTotal = originalTotal;
}
} else if (PreferentialTypeEnum.DISCOUNT.getValue().equals(privilege.getPreferentialType())) { // 打折
presentTotal = originalTotal * privilege.getPreferentialValue() / 100;
} else {
throw new IllegalArgumentException(String.format("满减送促销(%s) 的优惠类型不正确", activity.toString()));
}
return new CalcOrderPriceBO.Fee(originalTotal, originalTotal - presentTotal, 0, presentTotal);
}
private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) { private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
return activityList.stream() return activityList.stream()
.filter(activity -> type.getValue().equals(activity.getActivityType())) .filter(activity -> type.getValue().equals(activity.getActivityType()))
.findFirst().orElse(null); .findFirst().orElse(null);
} }
private List<PromotionActivityBO> findPromotionActivityListByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
return activityList.stream()
.filter(activity -> type.getValue().equals(activity.getActivityType()))
.collect(Collectors.toList());
}
private boolean isSpuMatchFullPrivilege(Integer spuId, PromotionActivityBO activity) {
Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
"传入的必须的促销活动必须是满减送");
PromotionActivityBO.FullPrivilege fullPrivilege = activity.getFullPrivilege();
if (RangeTypeEnum.ALL.getValue().equals(fullPrivilege.getRangeType())) {
return true;
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(fullPrivilege.getRangeType())) {
return fullPrivilege.getRangeValues().contains(spuId);
} else {
throw new IllegalArgumentException(String.format("促销活动(%s) 可用范围的类型是不正确", activity.toString()));
}
}
} }

View File

@ -128,6 +128,10 @@ public class PromotionActivityBO implements Serializable {
* 指定可用商品列表 * 指定可用商品列表
*/ */
private List<Integer> rangeValues; private List<Integer> rangeValues;
/**
* 是否循环
*/
private Boolean cycled;
/** /**
* 优惠数组 * 优惠数组
*/ */

View File

@ -0,0 +1,33 @@
package cn.iocoder.mall.promotion.api.constant;
/**
* 匹配类型枚举
*/
public enum MeetTypeEnum {
PRICE(1, "金额"),
QUANTITY(2, "数量"),;
/**
*
*/
private final Integer value;
/**
* 名字
*/
private final String name;
MeetTypeEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
}