前端:订单确认页,接入优惠劵列表(不包括计算)
后端:增加优惠劵的计算逻辑
This commit is contained in:
parent
ea4593adbc
commit
cb07eb3f71
@ -124,8 +124,9 @@
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onCouponChange(a, b, c) {
|
onCouponChange(index) {
|
||||||
debugger;
|
this.chosenCoupon = index;
|
||||||
|
this.showCouponPopup = false;
|
||||||
},
|
},
|
||||||
onCouponExchange(a, b, c) {
|
onCouponExchange(a, b, c) {
|
||||||
Dialog.alert({
|
Dialog.alert({
|
||||||
@ -204,7 +205,7 @@
|
|||||||
endAt: card.validEndTime / 1000,
|
endAt: card.validEndTime / 1000,
|
||||||
// description: '述信息,优惠券可用时展示',
|
// description: '述信息,优惠券可用时展示',
|
||||||
reason: card.unavailableReason,
|
reason: card.unavailableReason,
|
||||||
value: card.preferentialType === 1 ? card.priceOff : card.percentOff,
|
value: 0, // TODO ,需要服务端算
|
||||||
valueDesc: card.preferentialType === 1 ? card.priceOff / 100 : card.percentOff / 10.0,
|
valueDesc: card.preferentialType === 1 ? card.priceOff / 100 : card.percentOff / 10.0,
|
||||||
unitDesc: card.preferentialType === 1 ? '元' : '折'
|
unitDesc: card.preferentialType === 1 ? '元' : '折'
|
||||||
})
|
})
|
||||||
|
@ -96,7 +96,7 @@ public class UsersCartController {
|
|||||||
return CommonResult.success(result);
|
return CommonResult.success(result);
|
||||||
}
|
}
|
||||||
// 计算商品价格
|
// 计算商品价格
|
||||||
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = list0(cartItems);
|
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = list0(cartItems, null);
|
||||||
if (calcOrderPriceResult.isError()) {
|
if (calcOrderPriceResult.isError()) {
|
||||||
return CommonResult.error(calcOrderPriceResult);
|
return CommonResult.error(calcOrderPriceResult);
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ public class UsersCartController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/confirm_create_order")
|
@GetMapping("/confirm_create_order")
|
||||||
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder() {
|
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("couponCardId") Integer couponCardId) {
|
||||||
Integer userId = UserSecurityContextHolder.getContext().getUserId();
|
Integer userId = UserSecurityContextHolder.getContext().getUserId();
|
||||||
// 获得购物车中选中的
|
// 获得购物车中选中的
|
||||||
List<CartItemBO> cartItems = cartService.list(userId, true).getData();
|
List<CartItemBO> cartItems = cartService.list(userId, true).getData();
|
||||||
@ -117,7 +117,7 @@ public class UsersCartController {
|
|||||||
return CommonResult.success(result);
|
return CommonResult.success(result);
|
||||||
}
|
}
|
||||||
// 计算商品价格
|
// 计算商品价格
|
||||||
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = list0(cartItems);
|
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = list0(cartItems, couponCardId);
|
||||||
if (calcOrderPriceResult.isError()) {
|
if (calcOrderPriceResult.isError()) {
|
||||||
return CommonResult.error(calcOrderPriceResult);
|
return CommonResult.error(calcOrderPriceResult);
|
||||||
}
|
}
|
||||||
@ -130,10 +130,12 @@ public class UsersCartController {
|
|||||||
.setCouponCards(couponCards));
|
.setCouponCards(couponCards));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommonResult<CalcOrderPriceBO> list0(List<CartItemBO> cartItems) {
|
private CommonResult<CalcOrderPriceBO> list0(List<CartItemBO> cartItems, Integer couponCardId) {
|
||||||
// 创建计算的 DTO
|
// 创建计算的 DTO
|
||||||
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
|
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
|
||||||
.setItems(new ArrayList<>(cartItems.size()));
|
.setUserId(UserSecurityContextHolder.getContext().getUserId())
|
||||||
|
.setItems(new ArrayList<>(cartItems.size()))
|
||||||
|
.setCouponCardId(couponCardId);
|
||||||
for (CartItemBO item : cartItems) {
|
for (CartItemBO item : cartItems) {
|
||||||
calcOrderPriceDTO.getItems().add(new CalcOrderPriceDTO.Item(item.getSkuId(), item.getQuantity(), item.getSelected()));
|
calcOrderPriceDTO.getItems().add(new CalcOrderPriceDTO.Item(item.getSkuId(), item.getQuantity(), item.getSelected()));
|
||||||
}
|
}
|
||||||
|
@ -93,10 +93,13 @@ public class UsersOrderController {
|
|||||||
@GetMapping("confirm_create_order")
|
@GetMapping("confirm_create_order")
|
||||||
@ApiOperation("确认创建订单")
|
@ApiOperation("确认创建订单")
|
||||||
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId,
|
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId,
|
||||||
@RequestParam("quantity") Integer quantity) {
|
@RequestParam("quantity") Integer quantity,
|
||||||
|
@RequestParam("couponCardId") Integer couponCardId) {
|
||||||
// 创建 CalcOrderPriceDTO 对象,并执行价格计算
|
// 创建 CalcOrderPriceDTO 对象,并执行价格计算
|
||||||
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
|
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
|
||||||
.setItems(Collections.singletonList(new CalcOrderPriceDTO.Item(skuId, quantity, true)));
|
.setUserId(UserSecurityContextHolder.getContext().getUserId())
|
||||||
|
.setItems(Collections.singletonList(new CalcOrderPriceDTO.Item(skuId, quantity, true)))
|
||||||
|
.setCouponCardId(couponCardId);
|
||||||
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = cartService.calcOrderPrice(calcOrderPriceDTO);
|
CommonResult<CalcOrderPriceBO> calcOrderPriceResult = cartService.calcOrderPrice(calcOrderPriceDTO);
|
||||||
if (calcOrderPriceResult.isError()) {
|
if (calcOrderPriceResult.isError()) {
|
||||||
return CommonResult.error(calcOrderPriceResult);
|
return CommonResult.error(calcOrderPriceResult);
|
||||||
|
@ -24,7 +24,6 @@ public class UsersOrderConfirmCreateVO {
|
|||||||
* 优惠劵列表 TODO 芋艿,后续改改
|
* 优惠劵列表 TODO 芋艿,后续改改
|
||||||
*/
|
*/
|
||||||
private List<CouponCardAvailableBO> couponCards;
|
private List<CouponCardAvailableBO> couponCards;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品分组
|
* 商品分组
|
||||||
*
|
*
|
||||||
|
@ -18,6 +18,17 @@ public class CalcOrderPriceBO {
|
|||||||
* 商品分组数组
|
* 商品分组数组
|
||||||
*/
|
*/
|
||||||
private List<ItemGroup> itemGroups;
|
private List<ItemGroup> itemGroups;
|
||||||
|
/**
|
||||||
|
* 优惠劵编号
|
||||||
|
*/
|
||||||
|
private Integer couponCardId;
|
||||||
|
/**
|
||||||
|
* 优惠劵减少的金额
|
||||||
|
*
|
||||||
|
* 1. 若未使用优惠劵,返回 null
|
||||||
|
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
|
||||||
|
*/
|
||||||
|
private Integer couponCardDiscountTotal;
|
||||||
/**
|
/**
|
||||||
* 邮费信息
|
* 邮费信息
|
||||||
*
|
*
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.mall.order.api.dto;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,9 +13,13 @@ import java.util.List;
|
|||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class CalcOrderPriceDTO {
|
public class CalcOrderPriceDTO {
|
||||||
|
|
||||||
|
@NotNull(message = "用户编号不能为空")
|
||||||
|
private Integer userId;
|
||||||
/**
|
/**
|
||||||
* 商品数组
|
* 优惠劵编号
|
||||||
*/
|
*/
|
||||||
|
private Integer couponCardId;
|
||||||
|
@NotNull(message = "商品数组不能为空")
|
||||||
private List<Item> items;
|
private List<Item> items;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@ -16,7 +16,9 @@ import cn.iocoder.mall.order.biz.dataobject.CartItemDO;
|
|||||||
import cn.iocoder.mall.product.api.ProductSpuService;
|
import cn.iocoder.mall.product.api.ProductSpuService;
|
||||||
import cn.iocoder.mall.product.api.bo.ProductSkuBO;
|
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.CouponService;
|
||||||
import cn.iocoder.mall.promotion.api.PromotionActivityService;
|
import cn.iocoder.mall.promotion.api.PromotionActivityService;
|
||||||
|
import cn.iocoder.mall.promotion.api.bo.CouponCardDetailBO;
|
||||||
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
|
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
|
||||||
import cn.iocoder.mall.promotion.api.constant.*;
|
import cn.iocoder.mall.promotion.api.constant.*;
|
||||||
import com.alibaba.dubbo.config.annotation.Reference;
|
import com.alibaba.dubbo.config.annotation.Reference;
|
||||||
@ -38,6 +40,8 @@ public class CartServiceImpl implements CartService {
|
|||||||
private ProductSpuService productSpuService;
|
private ProductSpuService productSpuService;
|
||||||
@Reference(validation = "true")
|
@Reference(validation = "true")
|
||||||
private PromotionActivityService promotionActivityService;
|
private PromotionActivityService promotionActivityService;
|
||||||
|
@Reference(validation = "true")
|
||||||
|
private CouponService couponService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CartMapper cartMapper;
|
private CartMapper cartMapper;
|
||||||
@ -152,6 +156,7 @@ public class CartServiceImpl implements CartService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult<CalcOrderPriceBO> calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO) {
|
public CommonResult<CalcOrderPriceBO> calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO) {
|
||||||
|
// TODO 芋艿,补充一些表单校验。例如说,需要传入用户编号。
|
||||||
// 校验商品都存在
|
// 校验商品都存在
|
||||||
Map<Integer, CalcOrderPriceDTO.Item> calcOrderItemMap = calcOrderPriceDTO.getItems().stream()
|
Map<Integer, CalcOrderPriceDTO.Item> calcOrderItemMap = calcOrderPriceDTO.getItems().stream()
|
||||||
.collect(Collectors.toMap(CalcOrderPriceDTO.Item::getSkuId, item -> item)); // KEY:skuId
|
.collect(Collectors.toMap(CalcOrderPriceDTO.Item::getSkuId, item -> item)); // KEY:skuId
|
||||||
@ -177,7 +182,15 @@ public class CartServiceImpl implements CartService {
|
|||||||
// 3. 计算【满减送】促销
|
// 3. 计算【满减送】促销
|
||||||
List<CalcOrderPriceBO.ItemGroup> itemGroups = groupByFullPrivilege(items, activityList);
|
List<CalcOrderPriceBO.ItemGroup> itemGroups = groupByFullPrivilege(items, activityList);
|
||||||
calcOrderPriceBO.setItemGroups(itemGroups);
|
calcOrderPriceBO.setItemGroups(itemGroups);
|
||||||
// 4. 计算最终的价格
|
// 4. 计算优惠劵
|
||||||
|
if (calcOrderPriceDTO.getCouponCardId() != null) {
|
||||||
|
CommonResult<Integer> result = modifyPriceByCouponCard(calcOrderPriceDTO.getUserId(), calcOrderPriceDTO.getCouponCardId(), itemGroups);
|
||||||
|
if (result.isError()) {
|
||||||
|
return CommonResult.error(result);
|
||||||
|
}
|
||||||
|
calcOrderPriceBO.setCouponCardDiscountTotal(result.getData());
|
||||||
|
}
|
||||||
|
// 5. 计算最终的价格
|
||||||
int buyTotal = 0;
|
int buyTotal = 0;
|
||||||
int discountTotal = 0;
|
int discountTotal = 0;
|
||||||
int presentTotal = 0;
|
int presentTotal = 0;
|
||||||
@ -314,6 +327,72 @@ public class CartServiceImpl implements CartService {
|
|||||||
return itemGroups;
|
return itemGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommonResult<Integer> modifyPriceByCouponCard(Integer userId, Integer couponCardId, List<CalcOrderPriceBO.ItemGroup> itemGroups) {
|
||||||
|
Assert.isTrue(couponCardId != null, "优惠劵编号不能为空");
|
||||||
|
// 查询优惠劵
|
||||||
|
CommonResult<CouponCardDetailBO> couponCardResult = couponService.getCouponCardDetail(userId, couponCardId);
|
||||||
|
if (couponCardResult.isError()) {
|
||||||
|
return CommonResult.error(couponCardResult);
|
||||||
|
}
|
||||||
|
CouponCardDetailBO couponCard = couponCardResult.getData();
|
||||||
|
// 获得匹配的商品
|
||||||
|
List<CalcOrderPriceBO.Item> items = new ArrayList<>();
|
||||||
|
if (RangeTypeEnum.ALL.getValue().equals(couponCard.getRangeType())) {
|
||||||
|
// totalPrice = spus.stream().mapToInt(spu -> spu.getPrice() * spu.getQuantity()).sum();
|
||||||
|
itemGroups.forEach(itemGroup -> items.addAll(itemGroup.getItems()));
|
||||||
|
} else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(couponCard.getRangeType())) {
|
||||||
|
itemGroups.forEach(itemGroup -> items.forEach(item -> {
|
||||||
|
if (couponCard.getRangeValues().contains(item.getSpu().getId())) {
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (RangeTypeEnum.PRODUCT_EXCLUDE_PART.getValue().equals(couponCard.getRangeType())) {
|
||||||
|
itemGroups.forEach(itemGroup -> items.forEach(item -> {
|
||||||
|
if (!couponCard.getRangeValues().contains(item.getSpu().getId())) {
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (RangeTypeEnum.CATEGORY_INCLUDE_PART.getValue().equals(couponCard.getRangeType())) {
|
||||||
|
itemGroups.forEach(itemGroup -> items.forEach(item -> {
|
||||||
|
if (couponCard.getRangeValues().contains(item.getSpu().getCid())) {
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else if (RangeTypeEnum.CATEGORY_EXCLUDE_PART.getValue().equals(couponCard.getRangeType())) {
|
||||||
|
itemGroups.forEach(itemGroup -> items.forEach(item -> {
|
||||||
|
if (!couponCard.getRangeValues().contains(item.getSpu().getCid())) {
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// 判断是否符合条件
|
||||||
|
int originalTotal = items.stream().mapToInt(CalcOrderPriceBO.Item::getPresentTotal).sum(); // 此处,指的是以优惠劵视角的原价
|
||||||
|
if (originalTotal == 0 || originalTotal < couponCard.getPriceAvailable()) {
|
||||||
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_NOT_MATCH.getCode()); // TODO 芋艿,这种情况,会出现错误码的提示,无法格式化出来。另外,这块的最佳实践,找人讨论下。
|
||||||
|
}
|
||||||
|
// 计算价格
|
||||||
|
// 获得到优惠信息,进行价格计算
|
||||||
|
int presentTotal;
|
||||||
|
if (PreferentialTypeEnum.PRICE.getValue().equals(couponCard.getPreferentialType())) { // 减价
|
||||||
|
// 计算循环次数。这样,后续优惠的金额就是相乘了
|
||||||
|
presentTotal = originalTotal - couponCard.getPriceOff();
|
||||||
|
Assert.isTrue(presentTotal > 0, "计算后,价格为负数:" + presentTotal);
|
||||||
|
} else if (PreferentialTypeEnum.DISCOUNT.getValue().equals(couponCard.getPreferentialType())) { // 打折
|
||||||
|
presentTotal = originalTotal * couponCard.getPercentOff() / 100;
|
||||||
|
if (originalTotal - presentTotal > couponCard.getDiscountPriceLimit()) {
|
||||||
|
presentTotal = originalTotal - couponCard.getDiscountPriceLimit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format("优惠劵(%s) 的优惠类型不正确", couponCard.toString()));
|
||||||
|
}
|
||||||
|
int discountTotal = originalTotal - presentTotal;
|
||||||
|
Assert.isTrue(discountTotal > 0, "计算后,不产生优惠:" + discountTotal);
|
||||||
|
// 按比例,拆分 presentTotal
|
||||||
|
splitDiscountPriceToItems(items, discountTotal, presentTotal);
|
||||||
|
// 返回优惠金额
|
||||||
|
return CommonResult.success(originalTotal - presentTotal);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算指定 SKU 在限时折扣下的价格
|
* 计算指定 SKU 在限时折扣下的价格
|
||||||
*
|
*
|
||||||
@ -393,11 +472,31 @@ public class CartServiceImpl implements CartService {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(String.format("满减送促销(%s) 的优惠类型不正确", activity.toString()));
|
throw new IllegalArgumentException(String.format("满减送促销(%s) 的优惠类型不正确", activity.toString()));
|
||||||
}
|
}
|
||||||
Integer discountTotal = originalTotal - presentTotal;
|
int discountTotal = originalTotal - presentTotal;
|
||||||
if (discountTotal == 0) {
|
if (discountTotal == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 按比例,拆分 presentTotal
|
// 按比例,拆分 presentTotal
|
||||||
|
// for (int i = 0; i < items.size(); i++) {
|
||||||
|
// CalcOrderPriceBO.Item item = items.get(i);
|
||||||
|
// Integer discountPart;
|
||||||
|
// if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
|
||||||
|
// discountPart = (int) (discountTotal * (1.0D * item.getPresentTotal() / presentTotal));
|
||||||
|
// discountTotal -= discountPart;
|
||||||
|
// } else {
|
||||||
|
// discountPart = discountTotal;
|
||||||
|
// }
|
||||||
|
// Assert.isTrue(discountPart > 0, "优惠金额必须大于 0");
|
||||||
|
// item.setDiscountTotal(item.getDiscountTotal() + discountPart);
|
||||||
|
// item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal());
|
||||||
|
// item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity());
|
||||||
|
// }
|
||||||
|
splitDiscountPriceToItems(items, discountTotal, presentTotal);
|
||||||
|
// 返回优惠金额
|
||||||
|
return originalTotal - presentTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void splitDiscountPriceToItems(List<CalcOrderPriceBO.Item> items, Integer discountTotal, Integer presentTotal) {
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
CalcOrderPriceBO.Item item = items.get(i);
|
CalcOrderPriceBO.Item item = items.get(i);
|
||||||
Integer discountPart;
|
Integer discountPart;
|
||||||
@ -412,7 +511,6 @@ public class CartServiceImpl implements CartService {
|
|||||||
item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal());
|
item.setPresentTotal(item.getBuyTotal() - item.getDiscountTotal());
|
||||||
item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity());
|
item.setPresentPrice(item.getPresentTotal() / item.getBuyQuantity());
|
||||||
}
|
}
|
||||||
return originalTotal - presentTotal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
|
private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
|
||||||
|
@ -92,6 +92,15 @@ public interface CouponService {
|
|||||||
*/
|
*/
|
||||||
CommonResult<Boolean> cancelUseCouponCard(Integer userId, Integer couponCardId);
|
CommonResult<Boolean> cancelUseCouponCard(Integer userId, Integer couponCardId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定优惠劵
|
||||||
|
*
|
||||||
|
* @param userId 用户编号
|
||||||
|
* @param couponCardId 优惠劵编号
|
||||||
|
* @return 优惠劵
|
||||||
|
*/
|
||||||
|
CommonResult<CouponCardDetailBO> getCouponCardDetail(Integer userId, Integer couponCardId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得用户所有优惠劵,并标明是否可用
|
* 获得用户所有优惠劵,并标明是否可用
|
||||||
*
|
*
|
||||||
@ -103,6 +112,7 @@ public interface CouponService {
|
|||||||
*/
|
*/
|
||||||
CommonResult<List<CouponCardAvailableBO>> getCouponCardList(Integer userId, List<CouponCardSpuDTO> spus);
|
CommonResult<List<CouponCardAvailableBO>> getCouponCardList(Integer userId, List<CouponCardSpuDTO> spus);
|
||||||
|
|
||||||
|
|
||||||
// ========== 优惠码 ==========
|
// ========== 优惠码 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
package cn.iocoder.mall.promotion.api.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠劵明细 BO 。
|
||||||
|
*
|
||||||
|
* 主要是,会带上 {@link CouponTemplateBO} 的信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class CouponCardDetailBO {
|
||||||
|
|
||||||
|
// ========== 基本信息 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 优惠劵编号
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
/**
|
||||||
|
* 优惠劵(码)分组编号
|
||||||
|
*/
|
||||||
|
private Integer templateId;
|
||||||
|
/**
|
||||||
|
* 优惠劵名
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
// /**
|
||||||
|
// * 核销码
|
||||||
|
// */
|
||||||
|
// private String verifyCode;
|
||||||
|
/**
|
||||||
|
* 优惠码状态
|
||||||
|
*
|
||||||
|
* 1-未使用
|
||||||
|
* 2-已使用
|
||||||
|
* 3-已失效
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
// ========== 基本信息 END ==========
|
||||||
|
|
||||||
|
// ========== 领取情况 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 用户编号
|
||||||
|
*/
|
||||||
|
private Integer userId;
|
||||||
|
/**
|
||||||
|
* 领取类型
|
||||||
|
*
|
||||||
|
* 1 - 用户主动领取
|
||||||
|
* 2 - 后台自动发放
|
||||||
|
*/
|
||||||
|
private Integer takeType;
|
||||||
|
// ========== 领取情况 END ==========
|
||||||
|
|
||||||
|
// ========== 使用规则 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 是否设置满多少金额可用,单位:分
|
||||||
|
*/
|
||||||
|
private Integer priceAvailable;
|
||||||
|
/**
|
||||||
|
* 生效开始时间
|
||||||
|
*/
|
||||||
|
private Date validStartTime;
|
||||||
|
/**
|
||||||
|
* 生效结束时间
|
||||||
|
*/
|
||||||
|
private Date validEndTime;
|
||||||
|
// ========== 使用规则 END ==========
|
||||||
|
|
||||||
|
// ========== 使用效果 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 优惠类型
|
||||||
|
*
|
||||||
|
* 1-代金卷
|
||||||
|
* 2-折扣卷
|
||||||
|
*/
|
||||||
|
private Integer preferentialType;
|
||||||
|
/**
|
||||||
|
* 折扣
|
||||||
|
*/
|
||||||
|
private Integer percentOff;
|
||||||
|
/**
|
||||||
|
* 优惠金额,单位:分。
|
||||||
|
*/
|
||||||
|
private Integer priceOff;
|
||||||
|
/**
|
||||||
|
* 折扣上限,仅在 {@link #preferentialType} 等于 2 时生效。
|
||||||
|
*
|
||||||
|
* 例如,折扣上限为 20 元,当使用 8 折优惠券,订单金额为 1000 元时,最高只可折扣 20 元,而非 80 元。
|
||||||
|
*/
|
||||||
|
private Integer discountPriceLimit;
|
||||||
|
// ========== 使用效果 END ==========
|
||||||
|
|
||||||
|
// ========== 使用情况 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 是否使用
|
||||||
|
*/
|
||||||
|
private Boolean used;
|
||||||
|
/**
|
||||||
|
* 使用订单号
|
||||||
|
*/
|
||||||
|
private Integer usedOrderId;
|
||||||
|
/**
|
||||||
|
* 订单中优惠面值,单位:分
|
||||||
|
*/
|
||||||
|
private Integer usedPrice;
|
||||||
|
/**
|
||||||
|
* 使用时间
|
||||||
|
*/
|
||||||
|
private Date usedTime;
|
||||||
|
|
||||||
|
// TODO 芋艿,后续要加优惠劵的使用日志,因为下单后,可能会取消。
|
||||||
|
|
||||||
|
// ========== 使用情况 END ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
// ========== FROM template 使用规则 BEGIN ==========
|
||||||
|
/**
|
||||||
|
* 可用范围的类型
|
||||||
|
*
|
||||||
|
* 10-全部(ALL):所有可用
|
||||||
|
* 20-部分(PART):部分商品可用,或指定商品可用
|
||||||
|
* 21-部分(PART):部分商品不可用,或指定商品可用
|
||||||
|
* 30-部分(PART):部分分类可用,或指定商品可用
|
||||||
|
* 31-部分(PART):部分分类不可用,或指定商品可用
|
||||||
|
*/
|
||||||
|
private Integer rangeType;
|
||||||
|
/**
|
||||||
|
* 指定商品 / 分类列表,使用逗号分隔商品编号
|
||||||
|
*/
|
||||||
|
private List<Integer> rangeValues;
|
||||||
|
|
||||||
|
}
|
@ -17,15 +17,21 @@ public enum PromotionErrorCodeEnum {
|
|||||||
|
|
||||||
|
|
||||||
// ========== COUPON TEMPLATE 模块 ==========
|
// ========== COUPON TEMPLATE 模块 ==========
|
||||||
PRODUCT_TEMPLATE_NOT_EXISTS(1006002000, "优惠劵模板(码)不存在"),
|
COUPON_TEMPLATE_NOT_EXISTS(1006002000, "优惠劵模板(码)不存在"),
|
||||||
PRODUCT_TEMPLATE_NOT_CARD(1006002001, "不是优惠劵模板"),
|
COUPON_TEMPLATE_NOT_CARD(1006002001, "不是优惠劵模板"),
|
||||||
PRODUCT_TEMPLATE_NOT_CODE(1006002002, "不是优惠码模板"),
|
COUPON_TEMPLATE_NOT_CODE(1006002002, "不是优惠码模板"),
|
||||||
PRODUCT_TEMPLATE_TOTAL_CAN_NOT_REDUCE(1006002003, "优惠劵(码)模板的发放数量不能减小"),
|
COUPON_TEMPLATE_TOTAL_CAN_NOT_REDUCE(1006002003, "优惠劵(码)模板的发放数量不能减小"),
|
||||||
PRODUCT_TEMPLATE_STATUS_NOT_ENABLE(1006002004, "优惠劵模板(码)未开启"),
|
COUPON_TEMPLATE_STATUS_NOT_ENABLE(1006002004, "优惠劵模板(码)未开启"),
|
||||||
PRODUCT_TEMPLATE_TOTAL_NOT_ENOUGH(1006002005, "优惠劵(码)模板的发放量不足"),
|
COUPON_TEMPLATE_TOTAL_NOT_ENOUGH(1006002005, "优惠劵(码)模板的发放量不足"),
|
||||||
PRODUCT_TEMPLATE_CARD_ADD_EXCEED_QUOTA(1006002006, "优惠劵领取到达上限"),
|
COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA(1006002006, "优惠劵领取到达上限"),
|
||||||
|
|
||||||
|
// ========== COUPON CARD 模块 ==========
|
||||||
|
COUPON_CARD_NOT_EXISTS(1006003000, "优惠劵不存在"),
|
||||||
|
COUPON_CARD_ERROR_USER(1006003001, "优惠劵不属于当前用户"),
|
||||||
|
COUPON_CARD_NOT_MATCH(1006003002, "优惠劵不匹配,无法使用"),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
private final int code;
|
private final int code;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package cn.iocoder.mall.promotion.biz.convert;
|
|||||||
|
|
||||||
import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO;
|
import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO;
|
||||||
import cn.iocoder.mall.promotion.api.bo.CouponCardBO;
|
import cn.iocoder.mall.promotion.api.bo.CouponCardBO;
|
||||||
|
import cn.iocoder.mall.promotion.api.bo.CouponCardDetailBO;
|
||||||
import cn.iocoder.mall.promotion.biz.dataobject.CouponCardDO;
|
import cn.iocoder.mall.promotion.biz.dataobject.CouponCardDO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
@ -23,6 +24,9 @@ public interface CouponCardConvert {
|
|||||||
@Mappings({})
|
@Mappings({})
|
||||||
CouponCardBO convert(CouponCardDO card);
|
CouponCardBO convert(CouponCardDO card);
|
||||||
|
|
||||||
|
@Mappings({})
|
||||||
|
CouponCardDetailBO convert2(CouponCardDO card);
|
||||||
|
|
||||||
@Mappings({})
|
@Mappings({})
|
||||||
CouponCardAvailableBO convert2(CouponCardDO card, boolean x); // TODO 芋艿,临时用来解决 mapstruct 无法正确匹配方法的问题
|
CouponCardAvailableBO convert2(CouponCardDO card, boolean x); // TODO 芋艿,临时用来解决 mapstruct 无法正确匹配方法的问题
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service // 实际上不用添加。添加的原因是,必须 Spring 报错提示
|
@Service // 实际上不用添加。添加的原因是,必须 Spring 报错提示
|
||||||
@ -99,15 +102,15 @@ public class CouponServiceImpl implements CouponService {
|
|||||||
// 校验 CouponCardTemplate 存在
|
// 校验 CouponCardTemplate 存在
|
||||||
CouponTemplateDO template = couponTemplateMapper.selectById(couponCardTemplateUpdateDTO.getId());
|
CouponTemplateDO template = couponTemplateMapper.selectById(couponCardTemplateUpdateDTO.getId());
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_NOT_EXISTS.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_EXISTS.getCode());
|
||||||
}
|
}
|
||||||
// 校验 CouponCardTemplate 是 CARD
|
// 校验 CouponCardTemplate 是 CARD
|
||||||
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
|
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_NOT_CARD.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_CARD.getCode());
|
||||||
}
|
}
|
||||||
// 校验发放数量不能减少
|
// 校验发放数量不能减少
|
||||||
if (couponCardTemplateUpdateDTO.getTotal() < template.getTotal()) {
|
if (couponCardTemplateUpdateDTO.getTotal() < template.getTotal()) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_TOTAL_CAN_NOT_REDUCE.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_TOTAL_CAN_NOT_REDUCE.getCode());
|
||||||
}
|
}
|
||||||
// 更新优惠劵模板到数据库
|
// 更新优惠劵模板到数据库
|
||||||
CouponTemplateDO updateTemplateDO = CouponTemplateConvert.INSTANCE.convert(couponCardTemplateUpdateDTO);
|
CouponTemplateDO updateTemplateDO = CouponTemplateConvert.INSTANCE.convert(couponCardTemplateUpdateDTO);
|
||||||
@ -121,7 +124,7 @@ public class CouponServiceImpl implements CouponService {
|
|||||||
// 校验 CouponCardTemplate 存在
|
// 校验 CouponCardTemplate 存在
|
||||||
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
|
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_NOT_EXISTS.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_EXISTS.getCode());
|
||||||
}
|
}
|
||||||
// 更新到数据库
|
// 更新到数据库
|
||||||
CouponTemplateDO updateTemplateDO = new CouponTemplateDO().setId(couponTemplateId).setStatus(status);
|
CouponTemplateDO updateTemplateDO = new CouponTemplateDO().setId(couponTemplateId).setStatus(status);
|
||||||
@ -195,28 +198,28 @@ public class CouponServiceImpl implements CouponService {
|
|||||||
// 校验 CouponCardTemplate 存在
|
// 校验 CouponCardTemplate 存在
|
||||||
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
|
CouponTemplateDO template = couponTemplateMapper.selectById(couponTemplateId);
|
||||||
if (template == null) {
|
if (template == null) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_NOT_EXISTS.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_EXISTS.getCode());
|
||||||
}
|
}
|
||||||
// 校验 CouponCardTemplate 是 CARD
|
// 校验 CouponCardTemplate 是 CARD
|
||||||
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
|
if (!CouponTemplateTypeEnum.CARD.getValue().equals(template.getType())) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_NOT_CARD.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_CARD.getCode());
|
||||||
}
|
}
|
||||||
// 校验 CouponCardTemplate 状态是否开启
|
// 校验 CouponCardTemplate 状态是否开启
|
||||||
if (!CouponTemplateStatusEnum.ENABLE.getValue().equals(template.getStatus())) {
|
if (!CouponTemplateStatusEnum.ENABLE.getValue().equals(template.getStatus())) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_STATUS_NOT_ENABLE.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_STATUS_NOT_ENABLE.getCode());
|
||||||
}
|
}
|
||||||
// 校验 CouponCardTemplate 是否到达可领取的上限
|
// 校验 CouponCardTemplate 是否到达可领取的上限
|
||||||
if (template.getStatFetchNum() > template.getTotal()) {
|
if (template.getStatFetchNum() > template.getTotal()) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_TOTAL_NOT_ENOUGH.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_TOTAL_NOT_ENOUGH.getCode());
|
||||||
}
|
}
|
||||||
// 校验单人可领取优惠劵是否到达上限
|
// 校验单人可领取优惠劵是否到达上限
|
||||||
if (couponCardMapper.selectCountByUserIdAndTemplateId(userId, couponTemplateId) > template.getQuota()) {
|
if (couponCardMapper.selectCountByUserIdAndTemplateId(userId, couponTemplateId) > template.getQuota()) {
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
|
||||||
}
|
}
|
||||||
// 增加优惠劵已领取量
|
// 增加优惠劵已领取量
|
||||||
int updateTemplateCount = couponTemplateMapper.updateStatFetchNumIncr(couponTemplateId);
|
int updateTemplateCount = couponTemplateMapper.updateStatFetchNumIncr(couponTemplateId);
|
||||||
if (updateTemplateCount == 0) { // 超过 CouponCardTemplate 发放量
|
if (updateTemplateCount == 0) { // 超过 CouponCardTemplate 发放量
|
||||||
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.PRODUCT_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_CARD_ADD_EXCEED_QUOTA.getCode());
|
||||||
}
|
}
|
||||||
// 创建优惠劵
|
// 创建优惠劵
|
||||||
// 1. 基本信息 + 领取情况
|
// 1. 基本信息 + 领取情况
|
||||||
@ -250,6 +253,28 @@ public class CouponServiceImpl implements CouponService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<CouponCardDetailBO> getCouponCardDetail(Integer userId, Integer couponCardId) {
|
||||||
|
// 查询优惠劵
|
||||||
|
CouponCardDO card = couponCardMapper.selectById(couponCardId);
|
||||||
|
if (card == null) {
|
||||||
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_NOT_EXISTS.getCode());
|
||||||
|
}
|
||||||
|
if (!userId.equals(card.getUserId())) {
|
||||||
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode());
|
||||||
|
}
|
||||||
|
// 查询优惠劵模板
|
||||||
|
CouponTemplateDO template = couponTemplateMapper.selectById(card.getTemplateId());
|
||||||
|
if (template == null) {
|
||||||
|
return ServiceExceptionUtil.error(PromotionErrorCodeEnum.COUPON_TEMPLATE_NOT_EXISTS.getCode());
|
||||||
|
}
|
||||||
|
// 拼接结果
|
||||||
|
CouponCardDetailBO detail = CouponCardConvert.INSTANCE.convert2(card);
|
||||||
|
detail.setRangeType(template.getRangeType());
|
||||||
|
detail.setRangeValues(StringUtil.splitToInt(template.getRangeValues(), ","));
|
||||||
|
return CommonResult.success(detail);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult<List<CouponCardAvailableBO>> getCouponCardList(Integer userId, List<CouponCardSpuDTO> spus) {
|
public CommonResult<List<CouponCardAvailableBO>> getCouponCardList(Integer userId, List<CouponCardSpuDTO> spus) {
|
||||||
// 查询用户未使用的优惠劵列表
|
// 查询用户未使用的优惠劵列表
|
||||||
|
Loading…
Reference in New Issue
Block a user