Commit 67809be46d94a124d0bb7a92171f1f672072a791
1 parent
d7236068
【功能完善】商城: APP 完善积分商城活动下单
Showing
11 changed files
with
223 additions
and
3 deletions
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApi.java
| @@ -21,5 +21,22 @@ public interface PointActivityApi { | @@ -21,5 +21,22 @@ public interface PointActivityApi { | ||
| 21 | */ | 21 | */ |
| 22 | PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count); | 22 | PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count); |
| 23 | 23 | ||
| 24 | + /** | ||
| 25 | + * 更新积分商城商品库存(减少) | ||
| 26 | + * | ||
| 27 | + * @param id 活动编号 | ||
| 28 | + * @param skuId sku 编号 | ||
| 29 | + * @param count 数量(正数) | ||
| 30 | + */ | ||
| 31 | + void updatePointStockDecr(Long id, Long skuId, Integer count); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 更新积分商城商品库存(增加) | ||
| 35 | + * | ||
| 36 | + * @param id 活动编号 | ||
| 37 | + * @param skuId sku 编号 | ||
| 38 | + * @param count 数量(正数) | ||
| 39 | + */ | ||
| 40 | + void updatePointStockIncr(Long id, Long skuId, Integer count); | ||
| 24 | 41 | ||
| 25 | } | 42 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java
| @@ -23,4 +23,14 @@ public class PointActivityApiImpl implements PointActivityApi { | @@ -23,4 +23,14 @@ public class PointActivityApiImpl implements PointActivityApi { | ||
| 23 | return pointActivityService.validateJoinPointActivity(activityId, skuId, count); | 23 | return pointActivityService.validateJoinPointActivity(activityId, skuId, count); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | + @Override | ||
| 27 | + public void updatePointStockDecr(Long id, Long skuId, Integer count) { | ||
| 28 | + pointActivityService.updatePointStockDecr(id, skuId, count); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public void updatePointStockIncr(Long id, Long skuId, Integer count) { | ||
| 33 | + pointActivityService.updatePointStockIncr(id, skuId, count); | ||
| 34 | + } | ||
| 35 | + | ||
| 26 | } | 36 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java
| 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.point; | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.point; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; |
| 7 | import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; |
| 9 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 10 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 11 | ||
| 10 | /** | 12 | /** |
| @@ -25,4 +27,33 @@ public interface PointActivityMapper extends BaseMapperX<PointActivityDO> { | @@ -25,4 +27,33 @@ public interface PointActivityMapper extends BaseMapperX<PointActivityDO> { | ||
| 25 | .orderByDesc(PointActivityDO::getId)); | 27 | .orderByDesc(PointActivityDO::getId)); |
| 26 | } | 28 | } |
| 27 | 29 | ||
| 30 | + /** | ||
| 31 | + * 更新活动库存(减少) | ||
| 32 | + * | ||
| 33 | + * @param id 活动编号 | ||
| 34 | + * @param count 扣减的库存数量(正数) | ||
| 35 | + * @return 影响的行数 | ||
| 36 | + */ | ||
| 37 | + default int updateStockDecr(Long id, int count) { | ||
| 38 | + Assert.isTrue(count > 0); | ||
| 39 | + return update(null, new LambdaUpdateWrapper<PointActivityDO>() | ||
| 40 | + .eq(PointActivityDO::getId, id) | ||
| 41 | + .ge(PointActivityDO::getStock, count) | ||
| 42 | + .setSql("stock = stock - " + count)); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 更新活动库存(增加) | ||
| 47 | + * | ||
| 48 | + * @param id 活动编号 | ||
| 49 | + * @param count 增加的库存数量(正数) | ||
| 50 | + * @return 影响的行数 | ||
| 51 | + */ | ||
| 52 | + default int updateStockIncr(Long id, int count) { | ||
| 53 | + Assert.isTrue(count > 0); | ||
| 54 | + return update(null, new LambdaUpdateWrapper<PointActivityDO>() | ||
| 55 | + .eq(PointActivityDO::getId, id) | ||
| 56 | + .setSql("stock = stock + " + count)); | ||
| 57 | + } | ||
| 58 | + | ||
| 28 | } | 59 | } |
| 29 | \ No newline at end of file | 60 | \ No newline at end of file |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java
| 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.point; | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.point; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 4 | import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; | 5 | import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; |
| 5 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | 6 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| @@ -34,4 +35,32 @@ public interface PointProductMapper extends BaseMapperX<PointProductDO> { | @@ -34,4 +35,32 @@ public interface PointProductMapper extends BaseMapperX<PointProductDO> { | ||
| 34 | PointProductDO::getSkuId, skuId); | 35 | PointProductDO::getSkuId, skuId); |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 38 | + /** | ||
| 39 | + * 更新活动库存(减少) | ||
| 40 | + * | ||
| 41 | + * @param id 活动编号 | ||
| 42 | + * @param count 扣减的库存数量(减少库存) | ||
| 43 | + * @return 影响的行数 | ||
| 44 | + */ | ||
| 45 | + default int updateStockDecr(Long id, int count) { | ||
| 46 | + Assert.isTrue(count > 0); | ||
| 47 | + return update(null, new LambdaUpdateWrapper<PointProductDO>() | ||
| 48 | + .eq(PointProductDO::getId, id) | ||
| 49 | + .ge(PointProductDO::getStock, count) | ||
| 50 | + .setSql("stock = stock - " + count)); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 更新活动库存(增加) | ||
| 55 | + * | ||
| 56 | + * @param id 活动编号 | ||
| 57 | + * @param count 需要增加的库存(增加库存) | ||
| 58 | + * @return 影响的行数 | ||
| 59 | + */ | ||
| 60 | + default int updateStockIncr(Long id, int count) { | ||
| 61 | + Assert.isTrue(count > 0); | ||
| 62 | + return update(null, new LambdaUpdateWrapper<PointProductDO>() | ||
| 63 | + .eq(PointProductDO::getId, id) | ||
| 64 | + .setSql("stock = stock + " + count)); | ||
| 65 | + } | ||
| 37 | } | 66 | } |
| 38 | \ No newline at end of file | 67 | \ No newline at end of file |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java
| @@ -34,6 +34,24 @@ public interface PointActivityService { | @@ -34,6 +34,24 @@ public interface PointActivityService { | ||
| 34 | void updatePointActivity(@Valid PointActivitySaveReqVO updateReqVO); | 34 | void updatePointActivity(@Valid PointActivitySaveReqVO updateReqVO); |
| 35 | 35 | ||
| 36 | /** | 36 | /** |
| 37 | + * 更新积分商城商品库存(减少) | ||
| 38 | + * | ||
| 39 | + * @param id 活动编号 | ||
| 40 | + * @param skuId sku 编号 | ||
| 41 | + * @param count 数量(正数) | ||
| 42 | + */ | ||
| 43 | + void updatePointStockDecr(Long id, Long skuId, Integer count); | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 更新积分商城商品库存(增加) | ||
| 47 | + * | ||
| 48 | + * @param id 活动编号 | ||
| 49 | + * @param skuId sku 编号 | ||
| 50 | + * @param count 数量(正数) | ||
| 51 | + */ | ||
| 52 | + void updatePointStockIncr(Long id, Long skuId, Integer count); | ||
| 53 | + | ||
| 54 | + /** | ||
| 37 | * 关闭积分商城活动 | 55 | * 关闭积分商城活动 |
| 38 | * | 56 | * |
| 39 | * @param id 编号 | 57 | * @param id 编号 |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
| @@ -105,6 +105,43 @@ public class PointActivityServiceImpl implements PointActivityService { | @@ -105,6 +105,43 @@ public class PointActivityServiceImpl implements PointActivityService { | ||
| 105 | 105 | ||
| 106 | @Override | 106 | @Override |
| 107 | @Transactional(rollbackFor = Exception.class) | 107 | @Transactional(rollbackFor = Exception.class) |
| 108 | + public void updatePointStockDecr(Long id, Long skuId, Integer count) { | ||
| 109 | + // 1.1 校验活动库存是否充足 | ||
| 110 | + PointActivityDO activity = validatePointActivityExists(id); | ||
| 111 | + if (count > activity.getStock()) { | ||
| 112 | + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); | ||
| 113 | + } | ||
| 114 | + // 1.2 校验商品库存是否充足 | ||
| 115 | + PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId); | ||
| 116 | + if (product == null || count > product.getStock()) { | ||
| 117 | + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + // 2.1 更新活动商品库存 | ||
| 121 | + int updateCount = pointProductMapper.updateStockDecr(product.getId(), count); | ||
| 122 | + if (updateCount == 0) { | ||
| 123 | + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + // 2.2 更新活动库存 | ||
| 127 | + updateCount = pointActivityMapper.updateStockDecr(id, count); | ||
| 128 | + if (updateCount == 0) { | ||
| 129 | + throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + @Transactional(rollbackFor = Exception.class) | ||
| 135 | + public void updatePointStockIncr(Long id, Long skuId, Integer count) { | ||
| 136 | + PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId); | ||
| 137 | + // 更新活动商品库存 | ||
| 138 | + pointProductMapper.updateStockIncr(product.getId(), count); | ||
| 139 | + // 更新活动库存 | ||
| 140 | + pointActivityMapper.updateStockIncr(id, count); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + @Override | ||
| 144 | + @Transactional(rollbackFor = Exception.class) | ||
| 108 | public void closePointActivity(Long id) { | 145 | public void closePointActivity(Long id) { |
| 109 | // 校验存在 | 146 | // 校验存在 |
| 110 | PointActivityDO pointActivity = validatePointActivityExists(id); | 147 | PointActivityDO pointActivity = validatePointActivityExists(id); |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
| @@ -160,7 +160,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | @@ -160,7 +160,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { | ||
| 160 | public void updateSeckillStockDecr(Long id, Long skuId, Integer count) { | 160 | public void updateSeckillStockDecr(Long id, Long skuId, Integer count) { |
| 161 | // 1.1 校验活动库存是否充足 | 161 | // 1.1 校验活动库存是否充足 |
| 162 | SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); | 162 | SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); |
| 163 | - if (count > seckillActivity.getTotalStock()) { | 163 | + if (count > seckillActivity.getStock()) { |
| 164 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); | 164 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 165 | } | 165 | } |
| 166 | // 1.2 校验商品库存是否充足 | 166 | // 1.2 校验商品库存是否充足 |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
| @@ -55,4 +55,8 @@ public enum TradeOrderTypeEnum implements IntArrayValuable { | @@ -55,4 +55,8 @@ public enum TradeOrderTypeEnum implements IntArrayValuable { | ||
| 55 | return ObjectUtil.equal(type, COMBINATION.getType()); | 55 | return ObjectUtil.equal(type, COMBINATION.getType()); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | + public static boolean isPoint(Integer type) { | ||
| 59 | + return ObjectUtil.equal(type, POINT.getType()); | ||
| 60 | + } | ||
| 61 | + | ||
| 58 | } | 62 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
| @@ -353,4 +353,11 @@ public class TradeOrderDO extends BaseDO { | @@ -353,4 +353,11 @@ public class TradeOrderDO extends BaseDO { | ||
| 353 | */ | 353 | */ |
| 354 | private Long combinationRecordId; | 354 | private Long combinationRecordId; |
| 355 | 355 | ||
| 356 | + /** | ||
| 357 | + * 积分商城活动的编号 | ||
| 358 | + * | ||
| 359 | + * 关联 PointActivityDO 的 id 字段 | ||
| 360 | + */ | ||
| 361 | + private Long pointActivityId; | ||
| 362 | + | ||
| 356 | } | 363 | } |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.order.handler; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.lang.Assert; | ||
| 5 | +import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; | ||
| 6 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | ||
| 7 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | ||
| 8 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | ||
| 9 | +import jakarta.annotation.Resource; | ||
| 10 | +import org.springframework.stereotype.Component; | ||
| 11 | + | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 积分商城活动订单的 {@link TradeOrderHandler} 实现类 | ||
| 16 | + * | ||
| 17 | + * @author HUIHUI | ||
| 18 | + */ | ||
| 19 | +@Component | ||
| 20 | +public class TradePointOrderHandler implements TradeOrderHandler { | ||
| 21 | + | ||
| 22 | + @Resource | ||
| 23 | + private PointActivityApi pointActivityApi; | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { | ||
| 27 | + if (!TradeOrderTypeEnum.isPoint(order.getType())) { | ||
| 28 | + return; | ||
| 29 | + } | ||
| 30 | + // 明确校验一下 | ||
| 31 | + Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品"); | ||
| 32 | + | ||
| 33 | + // 扣减积分商城活动的库存 | ||
| 34 | + pointActivityApi.updatePointStockDecr(order.getPointActivityId(), | ||
| 35 | + orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { | ||
| 40 | + if (!TradeOrderTypeEnum.isPoint(order.getType())) { | ||
| 41 | + return; | ||
| 42 | + } | ||
| 43 | + // 明确校验一下 | ||
| 44 | + Assert.isTrue(orderItems.size() == 1, "积分商城活动兑换商品兑换时,只允许选择一个商品"); | ||
| 45 | + | ||
| 46 | + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚 | ||
| 47 | + orderItems = filterOrderItemListByNoneAfterSale(orderItems); | ||
| 48 | + if (CollUtil.isEmpty(orderItems)) { | ||
| 49 | + return; | ||
| 50 | + } | ||
| 51 | + afterCancelOrderItem(order, orderItems.get(0)); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + @Override | ||
| 55 | + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) { | ||
| 56 | + if (!TradeOrderTypeEnum.isPoint(order.getType())) { | ||
| 57 | + return; | ||
| 58 | + } | ||
| 59 | + // 恢复积分商城活动的库存 | ||
| 60 | + pointActivityApi.updatePointStockIncr(order.getPointActivityId(), | ||
| 61 | + orderItem.getSkuId(), orderItem.getCount()); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | +} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java
| @@ -63,15 +63,18 @@ public class TradePointActivityPriceCalculator implements TradePriceCalculator { | @@ -63,15 +63,18 @@ public class TradePointActivityPriceCalculator implements TradePriceCalculator { | ||
| 63 | // 3.1 记录优惠明细 | 63 | // 3.1 记录优惠明细 |
| 64 | int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换 | 64 | int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换 |
| 65 | Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1"); | 65 | Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1"); |
| 66 | - result.setUsePoint(activity.getPoint()); | 66 | + result.setUsePoint(activity.getPoint() * orderItem.getCount()); |
| 67 | + orderItem.setUsePoint(activity.getPoint() * orderItem.getCount()); | ||
| 67 | if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额 | 68 | if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额 |
| 68 | discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); | 69 | discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); |
| 69 | } | 70 | } |
| 71 | + // 3.2 记录优惠明细 | ||
| 70 | TradePriceCalculatorHelper.addPromotion(result, orderItem, | 72 | TradePriceCalculatorHelper.addPromotion(result, orderItem, |
| 71 | param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), | 73 | param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), |
| 72 | StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)), | 74 | StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)), |
| 73 | discountPrice); | 75 | discountPrice); |
| 74 | - // 3.2 更新 SKU 优惠金额 | 76 | + |
| 77 | + // 3.3 更新 SKU 优惠金额 | ||
| 75 | orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); | 78 | orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); |
| 76 | TradePriceCalculatorHelper.recountPayPrice(orderItem); | 79 | TradePriceCalculatorHelper.recountPayPrice(orderItem); |
| 77 | TradePriceCalculatorHelper.recountAllPrice(result); | 80 | TradePriceCalculatorHelper.recountAllPrice(result); |