拼团活动同一店铺同一时间限制一个活动取消。

积分订单库存扣减,积分返还处理。
其他促销相关优化
This commit is contained in:
Chopper 2021-09-01 19:10:16 +08:00
parent 42e9ee06f4
commit fb006e13a0
11 changed files with 183 additions and 28 deletions

View File

@ -0,0 +1,57 @@
package cn.lili.event.impl;
import cn.lili.event.OrderStatusChangeEvent;
import cn.lili.modules.member.entity.enums.PointTypeEnum;
import cn.lili.modules.member.service.MemberService;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dto.OrderMessage;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 积分
*
* @author Chopper
* @since 2021-03-13 16:58
*/
@Slf4j
@Service
public class PointExecute implements OrderStatusChangeEvent {
@Autowired
private MemberService memberService;
@Autowired
private OrderService orderService;
@Override
public void orderChange(OrderMessage orderMessage) {
switch (orderMessage.getNewStatus()) {
case CANCELLED:
Order order = orderService.getBySn(orderMessage.getOrderSn());
Long point = order.getPriceDetailDTO().getPayPoint();
if (point <= 0) {
return;
}
//如果未付款则不去要退回相关代码执行
if (order.getPayStatus().equals(PayStatusEnum.UNPAID.name())) {
return;
}
//如果他不处于连续赠送阶段则只赠送签到积分数
String content = "订单取消,积分返还:" + point + "";
//赠送会员积分
memberService.updateMemberPoint(point, PointTypeEnum.INCREASE.name(), order.getMemberId(), content);
break;
default:
break;
}
}
}

View File

@ -15,10 +15,7 @@ import cn.lili.modules.promotion.entity.dos.KanjiaActivity;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.dto.KanjiaActivityGoodsDTO;
import cn.lili.modules.promotion.entity.vos.PointsGoodsVO;
import cn.lili.modules.promotion.service.KanjiaActivityGoodsService;
import cn.lili.modules.promotion.service.KanjiaActivityService;
import cn.lili.modules.promotion.service.PointsGoodsService;
import cn.lili.modules.promotion.service.PromotionGoodsService;
import cn.lili.modules.promotion.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
@ -65,11 +62,17 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
*/
@Autowired
private PromotionGoodsService promotionGoodsService;
/**
* 促销商品
*/
@Autowired
private SeckillApplyService seckillApplyService;
/**
* 缓存
*/
@Autowired
private Cache cache;
@Autowired
private KanjiaActivityService kanjiaActivityService;
@Autowired
@ -95,6 +98,11 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
values.add(Integer.toString(i));
setPromotionStock(keys, values, orderItem);
}
List<Integer> stocks = cache.multiGet(keys);
//如果缓存中不存在存在等量的库存值则重新写入缓存防止缓存击穿导致无法下单
checkStocks(stocks, order);
//库存扣除结果
Boolean skuResult = stringRedisTemplate.execute(quantityScript, keys, values.toArray());
//如果库存扣减都成功则记录成交订单
@ -142,6 +150,75 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
}
}
/**
* 校验库存是否有效
*
* @param stocks
*/
private void checkStocks(List<Integer> stocks, OrderDetailVO order) {
for (int i = 0; i < stocks.size(); i++) {
if (null == stocks.get(i)) {
initSkuCache(order.getOrderItems());
initPromotionCache(order.getOrderItems());
return;
}
}
}
/**
* 缓存中sku库存值不存在时将不存在的信息重新写入一边
*
* @param orderItems
*/
private void initSkuCache(List<OrderItem> orderItems) {
orderItems.forEach(orderItem -> {
//如果不存在
if (!cache.hasKey(GoodsSkuService.getStockCacheKey(orderItem.getSkuId()))) {
//内部会自动写入这里不需要进行二次处理
goodsSkuService.getStock(orderItem.getSkuId());
}
});
}
/**
* 初始化促销商品缓存
*
* @param orderItems
*/
private void initPromotionCache(List<OrderItem> orderItems) {
//如果促销类型需要库存判定则做对应处理
orderItems.forEach(orderItem -> {
if (orderItem.getPromotionType() != null) {
//如果此促销有库存概念则计入
if (PromotionTypeEnum.haveStock(orderItem.getPromotionType())) {
PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(orderItem.getPromotionType());
String cacheKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId());
switch (promotionTypeEnum) {
case KANJIA:
cache.put(cacheKey, kanjiaActivityGoodsService.getKanJiaGoodsBySku(orderItem.getSkuId()).getStock().intValue());
return;
case POINTS_GOODS:
cache.put(cacheKey, pointsGoodsService.getPointsGoodsVOByMongo(orderItem.getSkuId()).getActiveStock().intValue());
return;
case SECKILL:
case PINTUAN:
cache.put(cacheKey, promotionGoodsService.getPromotionGoodsStock(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId()));
return;
default:
break;
}
}
}
});
}
/**
* 订单出库失败
*
@ -208,13 +285,12 @@ public class StockUpdateExecute implements OrderStatusChangeEvent {
List promotionStocks = cache.multiGet(promotionKey);
//修改砍价商品库存
if (promotionTypeEnum.equals(PromotionTypeEnum.KANJIA)) {
KanjiaActivity kanjiaActivity=kanjiaActivityService.getById(orderItem.getPromotionId());
KanjiaActivityGoodsDTO kanjiaActivityGoodsDTO=kanjiaActivityGoodsService.getKanjiaGoodsDetail(kanjiaActivity.getKanjiaActivityGoodsId());
KanjiaActivity kanjiaActivity = kanjiaActivityService.getById(orderItem.getPromotionId());
KanjiaActivityGoodsDTO kanjiaActivityGoodsDTO = kanjiaActivityGoodsService.getKanjiaGoodsDetail(kanjiaActivity.getKanjiaActivityGoodsId());
kanjiaActivityGoodsDTO.setStock(Convert.toInt(promotionStocks.get(0).toString()));
kanjiaActivityGoodsService.updateById(kanjiaActivityGoodsDTO);
this.mongoTemplate.save(kanjiaActivityGoodsDTO);
orderItem.getPromotionId();
//修改积分商品库存
//修改积分商品库存
} else if (promotionTypeEnum.equals(PromotionTypeEnum.POINTS_GOODS)) {
PointsGoodsVO pointsGoodsVO = pointsGoodsService.getPointsGoodsDetail(orderItem.getPromotionId());
pointsGoodsVO.setActiveStock(Convert.toLong(promotionStocks.get(0).toString()));

View File

@ -178,15 +178,15 @@ public class GoodsMessageListener implements RocketMQListener<MessageExt> {
*/
private void updateGoodsNum(MessageExt messageExt) {
Goods goods = null;
Goods goods;
try {
goods = JSONUtil.toBean(new String(messageExt.getBody()), Goods.class);
//更新店铺商品数量
assert goods != null;
storeService.updateStoreGoodsNum(goods.getStoreId());
} catch (Exception e) {
log.error("商品传输信息错误-{}", messageExt.toString());
log.error("商品MQ信息错误{}", messageExt.toString());
}
//更新店铺商品数量
assert goods != null;
storeService.updateStoreGoodsNum(goods.getStoreId());
}
/**

View File

@ -1,6 +1,5 @@
package cn.lili.timetask.handler.impl.coupon;
import cn.hutool.core.date.DateUtil;
import cn.lili.modules.promotion.entity.dos.MemberCoupon;
import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum;
import cn.lili.modules.promotion.service.MemberCouponService;
@ -18,6 +17,11 @@ import org.springframework.stereotype.Component;
@Component
public class CouponExecute implements EveryDayExecute {
/**
* 过期常量过期后或者使用后一定时间内删除无效的优惠券物理删除
*/
static final int EXPIRATION_DAY = 7;
@Autowired
private MemberCouponService memberCouponService;
@ -27,12 +31,19 @@ public class CouponExecute implements EveryDayExecute {
*/
@Override
public void execute() {
//将过期优惠券变更为过期状体
LambdaUpdateWrapper<MemberCoupon> updateWrapper = new LambdaUpdateWrapper<MemberCoupon>()
.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name())
.le(MemberCoupon::getEndTime, DateUtil.date())
.le(MemberCoupon::getEndTime, System.currentTimeMillis())
.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name());
this.memberCouponService.update(updateWrapper);
//删除过期/已使用的优惠券
LambdaUpdateWrapper<MemberCoupon> deleteWrapper = new LambdaUpdateWrapper<MemberCoupon>()
//如果结束时间小于 当前时间增加指定删除日期则删除
.le(MemberCoupon::getEndTime, System.currentTimeMillis() + 24 * 60 * 60 * 1000 * EXPIRATION_DAY);
this.memberCouponService.remove(deleteWrapper);
}
}

View File

@ -23,7 +23,7 @@ public enum PromotionTypeEnum {
/**
* 有促销库存的活动类型
*/
static PromotionTypeEnum[] haveStockPromotion = new PromotionTypeEnum[]{SECKILL, KANJIA, POINTS_GOODS};
static PromotionTypeEnum[] haveStockPromotion = new PromotionTypeEnum[]{PINTUAN, SECKILL, KANJIA, POINTS_GOODS};
private final String description;

View File

@ -1,5 +1,6 @@
package cn.lili.modules.order.cart.render;
import cn.lili.common.exception.ServiceException;
import cn.lili.modules.order.cart.entity.dto.TradeDTO;
import cn.lili.modules.order.cart.entity.enums.CartTypeEnum;
import cn.lili.modules.order.cart.entity.enums.RenderStepEnums;
@ -125,6 +126,8 @@ public class TradeBuilder {
if (render.step().equals(step)) {
render.render(tradeDTO);
}
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
log.error("购物车{}渲染异常:", render.getClass(), e);
}

View File

@ -58,6 +58,9 @@ public class CartPriceRender implements CartRenderStep {
List<CartVO> cartVOS = tradeDTO.getCartList();
cartVOS.forEach(cartVO -> {
List<PriceDetailDTO> skuPrices = cartVO.getSkuList().stream().filter(CartSkuVO::getChecked)
.map(CartSkuVO::getPriceDetailDTO).collect(Collectors.toList());
cartVO.getPriceDetailDTO().accumulationPriceDTO(
cartVO.getSkuList().stream().filter(CartSkuVO::getChecked)
.map(CartSkuVO::getPriceDetailDTO).collect(Collectors.toList())

View File

@ -85,6 +85,13 @@ public class CheckDataRender implements CartRenderStep {
private void checkData(TradeDTO tradeDTO) {
//循环购物车中的商品
for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) {
//如果失效确认sku为未选中状态
if (cartSkuVO.getInvalid()) {
//设置购物车未选中
cartSkuVO.setChecked(false);
}
//缓存中的商品信息
GoodsSku dataSku = goodsSkuService.getGoodsSkuByIdFromCache(cartSkuVO.getGoodsSku().getId());
//商品有效性判定

View File

@ -86,7 +86,7 @@ public class SkuPromotionRender implements CartRenderStep {
for (CartVO cartVO : tradeDTO.getCartList()) {
for (CartSkuVO cartSkuVO : cartVO.getSkuList()) {
cartSkuVO.getPriceDetailDTO().setPayPoint(cartSkuVO.getPoint());
PromotionSkuVO promotionSkuVO = new PromotionSkuVO(PromotionTypeEnum.PINTUAN.name(), cartSkuVO.getPointsId());
PromotionSkuVO promotionSkuVO = new PromotionSkuVO(PromotionTypeEnum.POINTS_GOODS.name(), cartSkuVO.getPointsId());
cartSkuVO.getPriceDetailDTO().getJoinPromotion().add(promotionSkuVO);
}
}

View File

@ -206,7 +206,6 @@ public class PintuanServiceImpl extends ServiceImpl<PintuanMapper, Pintuan> impl
@Override
public boolean addPintuan(PintuanVO pintuan) {
PromotionTools.checkPromotionTime(pintuan.getStartTime().getTime(), pintuan.getEndTime().getTime());
this.checkSamePromotion(pintuan.getStartTime(), pintuan.getEndTime(), pintuan.getStoreId(), null);
pintuan.setPromotionStatus(PromotionStatusEnum.NEW.name());
//保存到MYSQL中
boolean result = this.save(pintuan);
@ -224,8 +223,6 @@ public class PintuanServiceImpl extends ServiceImpl<PintuanMapper, Pintuan> impl
}
//检查促销时间
PromotionTools.checkPromotionTime(pintuan.getStartTime().getTime(), pintuan.getEndTime().getTime());
//检查同一时间同一店铺同一类型的促销活动
this.checkSamePromotion(pintuan.getStartTime(), pintuan.getEndTime(), pintuan.getStoreId(), pintuan.getId());
boolean result = this.updateById(pintuan);
if (pintuan.getPromotionGoodsList() != null) {
this.updatePintuanPromotionGoods(pintuan);
@ -393,14 +390,10 @@ public class PintuanServiceImpl extends ServiceImpl<PintuanMapper, Pintuan> impl
}
}
private void checkSamePromotion(Date startTime, Date endTime, String storeId, String pintuanId) {
QueryWrapper<Pintuan> queryWrapper = PromotionTools.checkActiveTime(startTime, endTime, PromotionTypeEnum.PINTUAN, storeId, pintuanId);
List<Pintuan> list = this.list(queryWrapper);
if (!list.isEmpty()) {
throw new ServiceException(ResultCode.PROMOTION_SAME_ERROR);
}
}
/**
* 增加拼团定时任务
* @param pintuan
*/
private void addPintuanStartTask(PintuanVO pintuan) {
PromotionMessage promotionMessage = new PromotionMessage(pintuan.getId(), PromotionTypeEnum.PINTUAN.name(), PromotionStatusEnum.START.name(), pintuan.getStartTime(), pintuan.getEndTime());
TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR,

View File

@ -2,6 +2,8 @@ package cn.lili.modules.promotion.serviceimpl;
import cn.hutool.core.util.StrUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.UserEnums;
import cn.lili.trigger.util.DelayQueueTools;
import cn.lili.trigger.enums.DelayTypeEnums;
import cn.lili.trigger.message.PromotionMessage;
@ -226,6 +228,9 @@ public class PointsGoodsServiceImpl extends ServiceImpl<PointsGoodsMapper, Point
@Override
public IPage<PointsGoodsVO> getPointsGoodsByPage(PointsGoodsSearchParams searchParams, PageVO page) {
IPage<PointsGoodsVO> pointsGoodsPage = new Page<>();
if (UserContext.getCurrentUser().getRole().equals(UserEnums.MEMBER)) {
searchParams.setPromotionStatus(PromotionStatusEnum.START.name());
}
Query query = searchParams.mongoQuery();
if (page != null) {
PromotionTools.mongoQueryPageParam(query, page);