Commit 052328c5fafca745051e5f6c9378f47ee21c44df
1 parent
dc46dff6
积分:完善积分获得、退还、使用 相关逻辑
Showing
24 changed files
with
381 additions
and
85 deletions
sql/mysql/point.sql
0 → 100644
| 1 | +ALTER TABLE trade_order ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price; | |
| 2 | +ALTER TABLE trade_order ADD COLUMN refund_point int NOT NULL DEFAULT 0 COMMENT '退还的使用积分' AFTER use_point; | |
| 3 | +ALTER TABLE trade_order ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER refund_point; | |
| 4 | + | |
| 5 | +ALTER TABLE trade_order_item ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price; | |
| 6 | +ALTER TABLE trade_order_item ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER use_point; | ... | ... |
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java
| ... | ... | @@ -23,7 +23,8 @@ public enum PromotionTypeEnum implements IntArrayValuable { |
| 23 | 23 | REWARD_ACTIVITY(5, "满减送"), |
| 24 | 24 | |
| 25 | 25 | MEMBER(6, "会员折扣"), |
| 26 | - COUPON(7, "优惠劵") | |
| 26 | + COUPON(7, "优惠劵"), | |
| 27 | + POINT(8, "积分") | |
| 27 | 28 | ; |
| 28 | 29 | |
| 29 | 30 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray(); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
| ... | ... | @@ -265,9 +265,13 @@ public class TradeOrderDO extends BaseDO { |
| 265 | 265 | * 对应 taobao 的 trade.point_fee 字段 |
| 266 | 266 | */ |
| 267 | 267 | private Integer pointPrice; |
| 268 | -// /** | |
| 269 | -// * 奖励的积分 TODO 疯狂:可以使用这个字段哈; | |
| 270 | -// */ | |
| 271 | -// private Integer rewardPoint; | |
| 268 | + /** | |
| 269 | + * 赠送的积分 | |
| 270 | + */ | |
| 271 | + private Integer givePoint; | |
| 272 | + /** | |
| 273 | + * 退还的使用的积分 | |
| 274 | + */ | |
| 275 | + private Integer refundPoint; | |
| 272 | 276 | |
| 273 | 277 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
| ... | ... | @@ -143,6 +143,14 @@ public class TradeOrderItemDO extends BaseDO { |
| 143 | 143 | * 对应 taobao 的 trade.point_fee 字段 |
| 144 | 144 | */ |
| 145 | 145 | private Integer pointPrice; |
| 146 | + /** | |
| 147 | + * 使用的积分 | |
| 148 | + */ | |
| 149 | + private Integer usePoint; | |
| 150 | + /** | |
| 151 | + * 赠送的积分 | |
| 152 | + */ | |
| 153 | + private Integer givePoint; | |
| 146 | 154 | // TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice; |
| 147 | 155 | // 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的; |
| 148 | 156 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
| ... | ... | @@ -24,6 +24,7 @@ import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 24 | 24 | import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; |
| 25 | 25 | import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; |
| 26 | 26 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 27 | +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | |
| 27 | 28 | import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; |
| 28 | 29 | import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; |
| 29 | 30 | import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; |
| ... | ... | @@ -48,8 +49,8 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 48 | 49 | import cn.iocoder.yudao.module.trade.enums.order.*; |
| 49 | 50 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 50 | 51 | import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; |
| 51 | -import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; | |
| 52 | 52 | import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; |
| 53 | +import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; | |
| 53 | 54 | import cn.iocoder.yudao.module.trade.service.cart.CartService; |
| 54 | 55 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 55 | 56 | import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; |
| ... | ... | @@ -106,6 +107,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 106 | 107 | private TradeMessageService tradeMessageService; |
| 107 | 108 | |
| 108 | 109 | @Resource |
| 110 | + private ProductSpuApi productSpuApi; | |
| 111 | + @Resource | |
| 109 | 112 | private ProductSkuApi productSkuApi; |
| 110 | 113 | @Resource |
| 111 | 114 | private PayOrderApi payOrderApi; |
| ... | ... | @@ -240,8 +243,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 240 | 243 | /** |
| 241 | 244 | * 订单创建前,执行前置逻辑 |
| 242 | 245 | * |
| 243 | - * @param userId 用户编号 | |
| 244 | - * @param createReqVO 创建订单请求 | |
| 246 | + * @param userId 用户编号 | |
| 247 | + * @param createReqVO 创建订单请求 | |
| 245 | 248 | * @param calculateRespBO 订单价格计算结果 |
| 246 | 249 | */ |
| 247 | 250 | private void beforeCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, |
| ... | ... | @@ -260,12 +263,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 260 | 263 | |
| 261 | 264 | /** |
| 262 | 265 | * 订单创建后,执行后置逻辑 |
| 263 | - * | |
| 266 | + * <p> | |
| 264 | 267 | * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 |
| 265 | 268 | * |
| 266 | 269 | * @param userId 用户编号 |
| 267 | 270 | * @param createReqVO 创建订单请求 |
| 268 | - * @param order 交易订单 | |
| 271 | + * @param order 交易订单 | |
| 269 | 272 | * @param calculateRespBO 订单价格计算结果 |
| 270 | 273 | */ |
| 271 | 274 | private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, |
| ... | ... | @@ -283,12 +286,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 283 | 286 | .setOrderId(order.getId())); |
| 284 | 287 | } |
| 285 | 288 | |
| 286 | - // 3. 扣减积分 | |
| 289 | + // 3. 扣减积分(抵扣) | |
| 287 | 290 | // 不在前置扣减的原因,是因为积分扣减时,需要记录关联业务 |
| 288 | - if (order.getUsePoint() != null && order.getUsePoint() > 0) { | |
| 289 | - memberPointApi.reducePoint(userId, calculateRespBO.getUsePoint(), | |
| 290 | - MemberPointBizTypeEnum.ORDER_USE.getType(), String.valueOf(order.getId())); | |
| 291 | - } | |
| 291 | + reduceUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId()); | |
| 292 | 292 | |
| 293 | 293 | // 4. 删除购物车商品 |
| 294 | 294 | Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); |
| ... | ... | @@ -342,8 +342,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 342 | 342 | |
| 343 | 343 | // TODO 芋艿:OrderLog |
| 344 | 344 | |
| 345 | - // 增加用户积分 | |
| 346 | - getSelf().addUserPointAsync(order.getUserId(), order.getPayPrice(), order.getId()); | |
| 345 | + // 增加用户积分(赠送) | |
| 346 | + addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_REWARD, order.getId()); | |
| 347 | 347 | // 增加用户经验 |
| 348 | 348 | getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId()); |
| 349 | 349 | // 增加用户佣金 |
| ... | ... | @@ -640,11 +640,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 640 | 640 | return; |
| 641 | 641 | } |
| 642 | 642 | // 计算总的退款金额 |
| 643 | - TradeOrderDO order = tradeOrderMapper.selectById(tradeOrderItemMapper.selectById(id).getOrderId()); | |
| 643 | + TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); | |
| 644 | + TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); | |
| 644 | 645 | Integer orderRefundPrice = order.getRefundPrice() + refundPrice; |
| 645 | 646 | if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单 |
| 646 | 647 | tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) |
| 647 | - .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice) | |
| 648 | + .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint()) | |
| 648 | 649 | .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now())); |
| 649 | 650 | |
| 650 | 651 | // TODO 芋艿:记录订单日志 |
| ... | ... | @@ -655,12 +656,17 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 655 | 656 | .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice)); |
| 656 | 657 | } |
| 657 | 658 | |
| 658 | - // 扣减用户积分 | |
| 659 | - getSelf().reduceUserPointAsync(order.getUserId(), orderRefundPrice, afterSaleId); | |
| 660 | - // 扣减用户经验 | |
| 661 | - getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId); | |
| 662 | - // 更新分佣记录为已失效 | |
| 663 | - getSelf().cancelBrokerageAsync(order.getUserId(), id); | |
| 659 | + // 售后成功后,执行数据回滚逻辑 | |
| 660 | + if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) { | |
| 661 | + // 扣减用户积分(赠送的) | |
| 662 | + reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId); | |
| 663 | + // 增加用户积分(返还抵扣) | |
| 664 | + addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, afterSaleId); | |
| 665 | + // 扣减用户经验 | |
| 666 | + getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId); | |
| 667 | + // 更新分佣记录为已失效 | |
| 668 | + getSelf().cancelBrokerageAsync(order.getUserId(), id); | |
| 669 | + } | |
| 664 | 670 | } |
| 665 | 671 | |
| 666 | 672 | @Override |
| ... | ... | @@ -728,8 +734,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 728 | 734 | // 3.回滚优惠券 |
| 729 | 735 | couponApi.returnUsedCoupon(order.getCouponId()); |
| 730 | 736 | |
| 731 | - // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择; | |
| 732 | - // TODO @疯狂:有赞是可配置(支付 or 确认收货),我们按照支付好列;然后这里的退积分,指的是下单时的积分抵扣。 | |
| 737 | + // 4.回滚积分(抵扣的) | |
| 738 | + addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId()); | |
| 733 | 739 | |
| 734 | 740 | // TODO 芋艿:OrderLog |
| 735 | 741 | |
| ... | ... | @@ -760,18 +766,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 760 | 766 | memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); |
| 761 | 767 | } |
| 762 | 768 | |
| 763 | - @Async | |
| 764 | - protected void addUserPointAsync(Long userId, Integer payPrice, Long orderId) { | |
| 765 | - // TODO @疯狂:具体多少积分,需要分成 2 不分:1. 支付金额;2. 商品金额 | |
| 766 | - int bizType = MemberPointBizTypeEnum.ORDER_REWARD.getType(); | |
| 767 | - memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId)); | |
| 769 | + protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { | |
| 770 | + if (point != null && point > 0) { | |
| 771 | + memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId)); | |
| 772 | + } | |
| 768 | 773 | } |
| 769 | 774 | |
| 770 | - @Async | |
| 771 | - protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) { | |
| 772 | - // TODO @疯狂:退款时,按照金额比例,退还积分;https://help.youzan.com/displaylist/detail_4_4-1-49185 | |
| 773 | - int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType(); | |
| 774 | - memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); | |
| 775 | + protected void reduceUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { | |
| 776 | + if (point != null && point > 0) { | |
| 777 | + memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId)); | |
| 778 | + } | |
| 775 | 779 | } |
| 776 | 780 | |
| 777 | 781 | @Async | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java
| ... | ... | @@ -41,7 +41,6 @@ public class TradePriceServiceImpl implements TradePriceService { |
| 41 | 41 | @Resource |
| 42 | 42 | private List<TradePriceCalculator> priceCalculators; |
| 43 | 43 | |
| 44 | - // TODO @疯狂:需要搞个 TradePriceCalculator,计算赠送积分; | |
| 45 | 44 | @Override |
| 46 | 45 | public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) { |
| 47 | 46 | // 1.1 获得商品 SKU 数组 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
| ... | ... | @@ -54,6 +54,11 @@ public class TradePriceCalculateRespBO { |
| 54 | 54 | private Integer usePoint; |
| 55 | 55 | |
| 56 | 56 | /** |
| 57 | + * 使用的积分 | |
| 58 | + */ | |
| 59 | + private Integer givePoint; | |
| 60 | + | |
| 61 | + /** | |
| 57 | 62 | * 订单价格 |
| 58 | 63 | */ |
| 59 | 64 | @Data |
| ... | ... | @@ -159,6 +164,10 @@ public class TradePriceCalculateRespBO { |
| 159 | 164 | */ |
| 160 | 165 | private Integer pointPrice; |
| 161 | 166 | /** |
| 167 | + * 使用的积分 | |
| 168 | + */ | |
| 169 | + private Integer usePoint; | |
| 170 | + /** | |
| 162 | 171 | * 应付金额(总),单位:分 |
| 163 | 172 | * |
| 164 | 173 | * = {@link #price} * {@link #count} |
| ... | ... | @@ -205,6 +214,11 @@ public class TradePriceCalculateRespBO { |
| 205 | 214 | */ |
| 206 | 215 | private List<ProductPropertyValueDetailRespDTO> properties; |
| 207 | 216 | |
| 217 | + /** | |
| 218 | + * 使用的积分 | |
| 219 | + */ | |
| 220 | + private Integer givePoint; | |
| 221 | + | |
| 208 | 222 | } |
| 209 | 223 | |
| 210 | 224 | /** | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.price.calculator; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.BooleanUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; | |
| 5 | +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; | |
| 6 | +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; | |
| 7 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | |
| 8 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | |
| 9 | +import lombok.extern.slf4j.Slf4j; | |
| 10 | +import org.springframework.core.annotation.Order; | |
| 11 | +import org.springframework.stereotype.Component; | |
| 12 | + | |
| 13 | +import javax.annotation.Resource; | |
| 14 | +import java.util.List; | |
| 15 | +import java.util.Optional; | |
| 16 | + | |
| 17 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * 赠送积分的 {@link TradePriceCalculator} 实现类 | |
| 21 | + * | |
| 22 | + * @author owen | |
| 23 | + */ | |
| 24 | +@Component | |
| 25 | +@Order(TradePriceCalculator.ORDER_POINT_GIVE) | |
| 26 | +@Slf4j | |
| 27 | +public class TradePointGiveCalculator implements TradePriceCalculator { | |
| 28 | + @Resource | |
| 29 | + private MemberPointApi memberPointApi; | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { | |
| 33 | + // 1.1 校验积分功能是否开启 | |
| 34 | + int givePointPerYuan = Optional.ofNullable(memberPointApi.getConfig()) | |
| 35 | + .filter(config -> BooleanUtil.isTrue(config.getTradeDeductEnable())) | |
| 36 | + .map(MemberPointConfigRespDTO::getTradeGivePoint) | |
| 37 | + .orElse(0); | |
| 38 | + if (givePointPerYuan <= 0) { | |
| 39 | + return; | |
| 40 | + } | |
| 41 | + // 1.2 校验支付金额 | |
| 42 | + if (result.getPrice().getPayPrice() <= 0) { | |
| 43 | + return; | |
| 44 | + } | |
| 45 | + | |
| 46 | + // 2.1 计算赠送积分 | |
| 47 | + int givePoint = MoneyUtils.calculateRatePriceFloor(result.getPrice().getPayPrice(), (double) givePointPerYuan); | |
| 48 | + // 2.2 计算分摊的赠送积分 | |
| 49 | + List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); | |
| 50 | + List<Integer> dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, givePoint); | |
| 51 | + | |
| 52 | + // 3.2 更新 SKU 赠送积分 | |
| 53 | + for (int i = 0; i < orderItems.size(); i++) { | |
| 54 | + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); | |
| 55 | + // 商品可能赠送了积分,所以这里要加上 | |
| 56 | + orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i)); | |
| 57 | + TradePriceCalculatorHelper.recountPayPrice(orderItem); | |
| 58 | + } | |
| 59 | + // 3.3 更新订单赠送积分 | |
| 60 | + TradePriceCalculatorHelper.recountAllGivePoint(result); | |
| 61 | + } | |
| 62 | +} | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.BooleanUtil; | |
| 4 | +import cn.hutool.core.util.NumberUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; | |
| 7 | +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 9 | +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | |
| 10 | +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | |
| 3 | 11 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 4 | 12 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 5 | 13 | import lombok.extern.slf4j.Slf4j; |
| 6 | 14 | import org.springframework.core.annotation.Order; |
| 7 | 15 | import org.springframework.stereotype.Component; |
| 8 | 16 | |
| 17 | +import javax.annotation.Resource; | |
| 18 | +import java.math.RoundingMode; | |
| 19 | +import java.util.List; | |
| 20 | + | |
| 21 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | |
| 22 | + | |
| 9 | 23 | /** |
| 10 | 24 | * 使用积分的 {@link TradePriceCalculator} 实现类 |
| 11 | 25 | * |
| ... | ... | @@ -15,15 +29,81 @@ import org.springframework.stereotype.Component; |
| 15 | 29 | @Order(TradePriceCalculator.ORDER_POINT_USE) |
| 16 | 30 | @Slf4j |
| 17 | 31 | public class TradePointUsePriceCalculator implements TradePriceCalculator { |
| 32 | + @Resource | |
| 33 | + private MemberPointApi memberPointApi; | |
| 34 | + @Resource | |
| 35 | + private MemberUserApi memberUserApi; | |
| 18 | 36 | |
| 19 | 37 | @Override |
| 20 | 38 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 21 | - // TODO 疯狂:待实现,嘿嘿; | |
| 22 | - if (param.getPointStatus()) { | |
| 23 | - result.setUsePoint(10); | |
| 24 | - } else { | |
| 39 | + // 1.1 校验是否使用积分 | |
| 40 | + if (!BooleanUtil.isTrue(param.getPointStatus())) { | |
| 25 | 41 | result.setUsePoint(0); |
| 42 | + return; | |
| 43 | + } | |
| 44 | + // 1.2 校验积分抵扣是否开启 | |
| 45 | + MemberPointConfigRespDTO config = memberPointApi.getConfig(); | |
| 46 | + if (!checkDeductPointEnable(config)) { | |
| 47 | + return; | |
| 48 | + } | |
| 49 | + // 1.3 校验用户积分余额 | |
| 50 | + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()); | |
| 51 | + if (user.getPoint() == null || user.getPoint() < 0) { | |
| 52 | + return; | |
| 53 | + } | |
| 54 | + | |
| 55 | + // 2.1 计算积分优惠金额 | |
| 56 | + int pointPrice = calculatePointPrice(config, user.getPoint(), result); | |
| 57 | + // 2.1 计算分摊的积分、抵扣金额 | |
| 58 | + List<TradePriceCalculateRespBO.OrderItem> orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); | |
| 59 | + List<Integer> dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice); | |
| 60 | + List<Integer> divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint()); | |
| 61 | + | |
| 62 | + // 3.1 记录优惠明细 | |
| 63 | + TradePriceCalculatorHelper.addPromotion(result, orderItems, | |
| 64 | + param.getUserId(), "积分抵扣", PromotionTypeEnum.POINT.getType(), | |
| 65 | + StrUtil.format("积分抵扣:省 {} 元", TradePriceCalculatorHelper.formatPrice(pointPrice)), | |
| 66 | + dividePointPrices); | |
| 67 | + // 3.2 更新 SKU 优惠金额 | |
| 68 | + for (int i = 0; i < orderItems.size(); i++) { | |
| 69 | + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); | |
| 70 | + orderItem.setPointPrice(dividePointPrices.get(i)); | |
| 71 | + orderItem.setUsePoint(divideUsePoints.get(i)); | |
| 72 | + TradePriceCalculatorHelper.recountPayPrice(orderItem); | |
| 73 | + } | |
| 74 | + TradePriceCalculatorHelper.recountAllPrice(result); | |
| 75 | + } | |
| 76 | + | |
| 77 | + private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) { | |
| 78 | + if (config == null) { | |
| 79 | + return false; | |
| 26 | 80 | } |
| 81 | + if (!BooleanUtil.isTrue(config.getTradeDeductEnable())) { | |
| 82 | + return false; | |
| 83 | + } | |
| 84 | + | |
| 85 | + // 有没有配置:1 积分抵扣多少分 | |
| 86 | + return config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; | |
| 27 | 87 | } |
| 28 | 88 | |
| 89 | + private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) { | |
| 90 | + // 每个订单最多可以使用的积分数量 | |
| 91 | + if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) { | |
| 92 | + usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice()); | |
| 93 | + } | |
| 94 | + // 积分优惠金额(分) | |
| 95 | + int pointPrice = usePoint * config.getTradeDeductUnitPrice(); | |
| 96 | + // 0元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额, 这时需要根据支付金额反推使用多少积分 | |
| 97 | + if (result.getPrice().getPayPrice() < pointPrice) { | |
| 98 | + pointPrice = result.getPrice().getPayPrice(); | |
| 99 | + // 反推需要扣除的积分 | |
| 100 | + usePoint = NumberUtil.toBigDecimal(pointPrice) | |
| 101 | + .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP) | |
| 102 | + .intValue(); | |
| 103 | + } | |
| 104 | + // 记录使用的积分 | |
| 105 | + result.setUsePoint(usePoint); | |
| 106 | + | |
| 107 | + return pointPrice; | |
| 108 | + } | |
| 29 | 109 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
| ... | ... | @@ -23,6 +23,10 @@ public interface TradePriceCalculator { |
| 23 | 23 | * 放在各种营销活动、优惠劵后面 TODO |
| 24 | 24 | */ |
| 25 | 25 | int ORDER_DELIVERY = 50; |
| 26 | + /** | |
| 27 | + * 赠送积分,放最后 | |
| 28 | + */ | |
| 29 | + int ORDER_POINT_GIVE = 999; | |
| 26 | 30 | |
| 27 | 31 | void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); |
| 28 | 32 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
| ... | ... | @@ -58,7 +58,8 @@ public class TradePriceCalculatorHelper { |
| 58 | 58 | .setWeight(sku.getWeight()).setVolume(sku.getVolume()); |
| 59 | 59 | // spu 信息 |
| 60 | 60 | orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()) |
| 61 | - .setDeliveryTemplateId(spu.getDeliveryTemplateId()); | |
| 61 | + .setDeliveryTemplateId(spu.getDeliveryTemplateId()) | |
| 62 | + .setGivePoint(spu.getGiveIntegral()).setUsePoint(0); | |
| 62 | 63 | if (orderItem.getPicUrl() == null) { |
| 63 | 64 | orderItem.setPicUrl(spu.getPicUrl()); |
| 64 | 65 | } |
| ... | ... | @@ -67,6 +68,7 @@ public class TradePriceCalculatorHelper { |
| 67 | 68 | // 创建它的 Price 属性 |
| 68 | 69 | result.setPrice(new TradePriceCalculateRespBO.Price()); |
| 69 | 70 | recountAllPrice(result); |
| 71 | + recountAllGivePoint(result); | |
| 70 | 72 | return result; |
| 71 | 73 | } |
| 72 | 74 | |
| ... | ... | @@ -112,12 +114,21 @@ public class TradePriceCalculatorHelper { |
| 112 | 114 | } |
| 113 | 115 | |
| 114 | 116 | /** |
| 117 | + * 基于订单项,重新计算赠送积分 | |
| 118 | + * | |
| 119 | + * @param result 计算结果 | |
| 120 | + */ | |
| 121 | + public static void recountAllGivePoint(TradePriceCalculateRespBO result) { | |
| 122 | + result.setGivePoint(getSumValue(result.getItems(), item -> item.getSelected() ? item.getGivePoint() : 0, Integer::sum)); | |
| 123 | + } | |
| 124 | + | |
| 125 | + /** | |
| 115 | 126 | * 重新计算单个订单项的支付金额 |
| 116 | 127 | * |
| 117 | 128 | * @param orderItem 订单项 |
| 118 | 129 | */ |
| 119 | 130 | public static void recountPayPrice(TradePriceCalculateRespBO.OrderItem orderItem) { |
| 120 | - orderItem.setPayPrice(orderItem.getPrice()* orderItem.getCount() | |
| 131 | + orderItem.setPayPrice(orderItem.getPrice() * orderItem.getCount() | |
| 121 | 132 | - orderItem.getDiscountPrice() |
| 122 | 133 | + orderItem.getDeliveryPrice() |
| 123 | 134 | - orderItem.getCouponPrice() |
| ... | ... | @@ -145,6 +156,12 @@ public class TradePriceCalculatorHelper { |
| 145 | 156 | if (orderItem.getPointPrice() == null) { |
| 146 | 157 | orderItem.setPointPrice(0); |
| 147 | 158 | } |
| 159 | + if (orderItem.getUsePoint() == null) { | |
| 160 | + orderItem.setUsePoint(0); | |
| 161 | + } | |
| 162 | + if (orderItem.getGivePoint() == null) { | |
| 163 | + orderItem.setGivePoint(0); | |
| 164 | + } | |
| 148 | 165 | recountPayPrice(orderItem); |
| 149 | 166 | }); |
| 150 | 167 | } |
| ... | ... | @@ -169,7 +186,7 @@ public class TradePriceCalculatorHelper { |
| 169 | 186 | */ |
| 170 | 187 | public static Integer calculateTotalCount(List<TradePriceCalculateRespBO.OrderItem> orderItems) { |
| 171 | 188 | return getSumValue(orderItems, |
| 172 | - orderItem -> orderItem.getSelected() ? orderItem.getCount() : 0, // 未选中的情况下,不计算数量 | |
| 189 | + orderItem -> orderItem.getSelected() ? orderItem.getCount() : 0, // 未选中的情况下,不计算数量 | |
| 173 | 190 | Integer::sum); |
| 174 | 191 | } |
| 175 | 192 | |
| ... | ... | @@ -177,7 +194,7 @@ public class TradePriceCalculatorHelper { |
| 177 | 194 | * 按照支付金额,返回每个订单项的分摊金额数组 |
| 178 | 195 | * |
| 179 | 196 | * @param orderItems 订单项数组 |
| 180 | - * @param price 金额 | |
| 197 | + * @param price 金额 | |
| 181 | 198 | * @return 分摊金额数组,和传入的 orderItems 一一对应 |
| 182 | 199 | */ |
| 183 | 200 | public static List<Integer> dividePrice(List<TradePriceCalculateRespBO.OrderItem> orderItems, Integer price) { |
| ... | ... | @@ -210,12 +227,12 @@ public class TradePriceCalculatorHelper { |
| 210 | 227 | /** |
| 211 | 228 | * 添加【匹配】单个 OrderItem 的营销明细 |
| 212 | 229 | * |
| 213 | - * @param result 价格计算结果 | |
| 230 | + * @param result 价格计算结果 | |
| 214 | 231 | * @param orderItem 单个订单商品 SKU |
| 215 | - * @param id 营销编号 | |
| 216 | - * @param name 营销名字 | |
| 217 | - * @param description 满足条件的提示 | |
| 218 | - * @param type 营销类型 | |
| 232 | + * @param id 营销编号 | |
| 233 | + * @param name 营销名字 | |
| 234 | + * @param description 满足条件的提示 | |
| 235 | + * @param type 营销类型 | |
| 219 | 236 | * @param discountPrice 单个订单商品 SKU 的优惠价格(总) |
| 220 | 237 | */ |
| 221 | 238 | public static void addPromotion(TradePriceCalculateRespBO result, TradePriceCalculateRespBO.OrderItem orderItem, |
| ... | ... | @@ -226,7 +243,7 @@ public class TradePriceCalculatorHelper { |
| 226 | 243 | /** |
| 227 | 244 | * 添加【匹配】多个 OrderItem 的营销明细 |
| 228 | 245 | * |
| 229 | - * @param result 价格计算结果 | |
| 246 | + * @param result 价格计算结果 | |
| 230 | 247 | * @param orderItems 多个订单商品 SKU |
| 231 | 248 | * @param id 营销编号 |
| 232 | 249 | * @param name 营销名字 |
| ... | ... | @@ -235,7 +252,7 @@ public class TradePriceCalculatorHelper { |
| 235 | 252 | * @param discountPrices 多个订单商品 SKU 的优惠价格(总),和 orderItems 一一对应 |
| 236 | 253 | */ |
| 237 | 254 | public static void addPromotion(TradePriceCalculateRespBO result, List<TradePriceCalculateRespBO.OrderItem> orderItems, |
| 238 | - Long id, String name, Integer type, String description, List<Integer> discountPrices) { | |
| 255 | + Long id, String name, Integer type, String description, List<Integer> discountPrices) { | |
| 239 | 256 | // 创建营销明细 Item |
| 240 | 257 | List<TradePriceCalculateRespBO.PromotionItem> promotionItems = new ArrayList<>(discountPrices.size()); |
| 241 | 258 | for (int i = 0; i < orderItems.size(); i++) { |
| ... | ... | @@ -255,12 +272,12 @@ public class TradePriceCalculatorHelper { |
| 255 | 272 | /** |
| 256 | 273 | * 添加【不匹配】多个 OrderItem 的营销明细 |
| 257 | 274 | * |
| 258 | - * @param result 价格计算结果 | |
| 259 | - * @param orderItems 多个订单商品 SKU | |
| 260 | - * @param id 营销编号 | |
| 261 | - * @param name 营销名字 | |
| 262 | - * @param description 满足条件的提示 | |
| 263 | - * @param type 营销类型 | |
| 275 | + * @param result 价格计算结果 | |
| 276 | + * @param orderItems 多个订单商品 SKU | |
| 277 | + * @param id 营销编号 | |
| 278 | + * @param name 营销名字 | |
| 279 | + * @param description 满足条件的提示 | |
| 280 | + * @param type 营销类型 | |
| 264 | 281 | */ |
| 265 | 282 | public static void addNotMatchPromotion(TradePriceCalculateRespBO result, List<TradePriceCalculateRespBO.OrderItem> orderItems, |
| 266 | 283 | Long id, String name, Integer type, String description) { | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java
| 1 | 1 | package cn.iocoder.yudao.module.member.api.point; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; | |
| 3 | 4 | import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; |
| 4 | 5 | |
| 5 | 6 | import javax.validation.constraints.Min; |
| ... | ... | @@ -12,6 +13,13 @@ import javax.validation.constraints.Min; |
| 12 | 13 | public interface MemberPointApi { |
| 13 | 14 | |
| 14 | 15 | /** |
| 16 | + * 获得积分配置 | |
| 17 | + * | |
| 18 | + * @return 积分配置 | |
| 19 | + */ | |
| 20 | + MemberPointConfigRespDTO getConfig(); | |
| 21 | + | |
| 22 | + /** | |
| 15 | 23 | * 增加用户积分 |
| 16 | 24 | * |
| 17 | 25 | * @param userId 用户编号 | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/dto/MemberPointConfigRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.member.api.point.dto; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 用户信息 Response DTO | |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 9 | + */ | |
| 10 | +@Data | |
| 11 | +public class MemberPointConfigRespDTO { | |
| 12 | + /** | |
| 13 | + * 积分抵扣开关 | |
| 14 | + */ | |
| 15 | + private Boolean tradeDeductEnable; | |
| 16 | + /** | |
| 17 | + * 积分抵扣,单位:分 | |
| 18 | + * <p> | |
| 19 | + * 1 积分抵扣多少分 | |
| 20 | + */ | |
| 21 | + private Integer tradeDeductUnitPrice; | |
| 22 | + /** | |
| 23 | + * 积分抵扣最大值 | |
| 24 | + */ | |
| 25 | + private Integer tradeDeductMaxPrice; | |
| 26 | + /** | |
| 27 | + * 1 元赠送多少分 | |
| 28 | + */ | |
| 29 | + private Integer tradeGivePoint; | |
| 30 | +} | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
| ... | ... | @@ -13,6 +13,7 @@ public interface ErrorCodeConstants { |
| 13 | 13 | ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); |
| 14 | 14 | ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1004001001, "手机号未注册用户"); |
| 15 | 15 | ErrorCode USER_MOBILE_USED = new ErrorCode(1004001002, "修改手机失败,该手机号({})已经被使用"); |
| 16 | + ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1004001003, "用户积分余额不足"); | |
| 16 | 17 | |
| 17 | 18 | // ========== AUTH 模块 1004003000 ========== |
| 18 | 19 | ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java
| ... | ... | @@ -18,8 +18,10 @@ public enum MemberPointBizTypeEnum implements IntArrayValuable { |
| 18 | 18 | |
| 19 | 19 | SIGN(1, "签到", "签到获得 {} 积分", true), |
| 20 | 20 | ORDER_REWARD(10, "订单奖励", "下单获得 {} 积分", true), |
| 21 | - ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false), // 退回积分 | |
| 21 | + ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 退回积分 | |
| 22 | 22 | ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 扣减积分 |
| 23 | + AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 退回积分 | |
| 24 | + AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 扣减积分 | |
| 23 | 25 | ; |
| 24 | 26 | |
| 25 | 27 | /** | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.member.api.point; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.lang.Assert; | |
| 4 | +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; | |
| 5 | +import cn.iocoder.yudao.module.member.convert.point.MemberPointConfigConvert; | |
| 3 | 6 | import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; |
| 7 | +import cn.iocoder.yudao.module.member.service.point.MemberPointConfigService; | |
| 4 | 8 | import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; |
| 5 | 9 | import org.springframework.stereotype.Service; |
| 6 | 10 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -21,9 +25,17 @@ public class MemberPointApiImpl implements MemberPointApi { |
| 21 | 25 | |
| 22 | 26 | @Resource |
| 23 | 27 | private MemberPointRecordService memberPointRecordService; |
| 28 | + @Resource | |
| 29 | + private MemberPointConfigService memberPointConfigService; | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public MemberPointConfigRespDTO getConfig() { | |
| 33 | + return MemberPointConfigConvert.INSTANCE.convert01(memberPointConfigService.getPointConfig()); | |
| 34 | + } | |
| 24 | 35 | |
| 25 | 36 | @Override |
| 26 | 37 | public void addPoint(Long userId, Integer point, Integer bizType, String bizId) { |
| 38 | + Assert.isTrue(point > 0); | |
| 27 | 39 | MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); |
| 28 | 40 | if (bizTypeEnum == null) { |
| 29 | 41 | throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); |
| ... | ... | @@ -33,11 +45,12 @@ public class MemberPointApiImpl implements MemberPointApi { |
| 33 | 45 | |
| 34 | 46 | @Override |
| 35 | 47 | public void reducePoint(Long userId, Integer point, Integer bizType, String bizId) { |
| 48 | + Assert.isTrue(point > 0); | |
| 36 | 49 | MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); |
| 37 | 50 | if (bizTypeEnum == null) { |
| 38 | 51 | throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); |
| 39 | 52 | } |
| 40 | - memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId); | |
| 53 | + memberPointRecordService.createPointRecord(userId, -point, bizTypeEnum, bizId); | |
| 41 | 54 | } |
| 42 | 55 | |
| 43 | 56 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.member.convert.point; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; | |
| 3 | 4 | import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO; |
| 4 | 5 | import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO; |
| ... | ... | @@ -20,4 +21,5 @@ public interface MemberPointConfigConvert { |
| 20 | 21 | |
| 21 | 22 | MemberPointConfigDO convert(MemberPointConfigSaveReqVO bean); |
| 22 | 23 | |
| 24 | + MemberPointConfigRespDTO convert01(MemberPointConfigDO pointConfig); | |
| 23 | 25 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.member.dal.mysql.user; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 7 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 7 | 8 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 8 | 9 | import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; |
| 9 | 10 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 11 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 10 | 12 | import org.apache.ibatis.annotations.Mapper; |
| 11 | 13 | |
| 12 | 14 | import java.util.List; |
| ... | ... | @@ -62,4 +64,33 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> { |
| 62 | 64 | .apply("FIND_IN_SET({0}, tag_ids)", tagId)); |
| 63 | 65 | } |
| 64 | 66 | |
| 67 | + /** | |
| 68 | + * 更新用户积分(增加) | |
| 69 | + * | |
| 70 | + * @param id 用户编号 | |
| 71 | + * @param incrCount 增加积分(正数) | |
| 72 | + */ | |
| 73 | + default void updatePointIncr(Long id, Integer incrCount) { | |
| 74 | + Assert.isTrue(incrCount > 0); | |
| 75 | + LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>() | |
| 76 | + .setSql(" point = point + " + incrCount) | |
| 77 | + .eq(MemberUserDO::getId, id); | |
| 78 | + update(null, lambdaUpdateWrapper); | |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * 更新用户积分(减少) | |
| 83 | + * | |
| 84 | + * @param id 用户编号 | |
| 85 | + * @param incrCount 增加积分(负数) | |
| 86 | + * @return 更新行数 | |
| 87 | + */ | |
| 88 | + default int updatePointDecr(Long id, Integer incrCount) { | |
| 89 | + Assert.isTrue(incrCount < 0); | |
| 90 | + LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>() | |
| 91 | + .setSql(" point = point + " + incrCount) // 负数,所以使用 + 号 | |
| 92 | + .eq(MemberUserDO::getId, id); | |
| 93 | + return update(null, lambdaUpdateWrapper); | |
| 94 | + } | |
| 95 | + | |
| 65 | 96 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java
| ... | ... | @@ -5,7 +5,6 @@ import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 7 | import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; |
| 8 | -import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO; | |
| 9 | 8 | import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; |
| 10 | 9 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 11 | 10 | import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper; |
| ... | ... | @@ -14,6 +13,7 @@ import cn.iocoder.yudao.module.member.service.user.MemberUserService; |
| 14 | 13 | import lombok.extern.slf4j.Slf4j; |
| 15 | 14 | import org.apache.commons.lang3.StringUtils; |
| 16 | 15 | import org.springframework.stereotype.Service; |
| 16 | +import org.springframework.transaction.annotation.Transactional; | |
| 17 | 17 | import org.springframework.util.CollectionUtils; |
| 18 | 18 | import org.springframework.validation.annotation.Validated; |
| 19 | 19 | |
| ... | ... | @@ -21,7 +21,9 @@ import javax.annotation.Resource; |
| 21 | 21 | import java.util.List; |
| 22 | 22 | import java.util.Set; |
| 23 | 23 | |
| 24 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 24 | 25 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| 26 | +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.USER_POINT_NOT_ENOUGH; | |
| 25 | 27 | |
| 26 | 28 | |
| 27 | 29 | /** |
| ... | ... | @@ -36,8 +38,6 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService { |
| 36 | 38 | |
| 37 | 39 | @Resource |
| 38 | 40 | private MemberPointRecordMapper memberPointRecordMapper; |
| 39 | - @Resource | |
| 40 | - private MemberPointConfigService memberPointConfigService; | |
| 41 | 41 | |
| 42 | 42 | @Resource |
| 43 | 43 | private MemberUserService memberUserService; |
| ... | ... | @@ -64,32 +64,28 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService { |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | @Override |
| 67 | + @Transactional(rollbackFor = Exception.class) | |
| 67 | 68 | public void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId) { |
| 68 | - MemberPointConfigDO pointConfig = memberPointConfigService.getPointConfig(); | |
| 69 | - if (pointConfig == null || pointConfig.getTradeGivePoint() == null) { | |
| 70 | - log.error("[createPointRecord][增加积分失败:tradeGivePoint 未配置,userId({}) point({}) bizType({}) bizId({})]", | |
| 71 | - userId, point, bizType.getType(), bizId); | |
| 72 | - return; | |
| 69 | + // 1. 校验用户积分余额 | |
| 70 | + MemberUserDO user = memberUserService.getUser(userId); | |
| 71 | + Integer userPoint = ObjectUtil.defaultIfNull(user.getPoint(), 0); | |
| 72 | + int totalPoint = userPoint + point; // 用户变动后的积分 | |
| 73 | + if (totalPoint < 0) { | |
| 74 | + throw exception(USER_POINT_NOT_ENOUGH); | |
| 73 | 75 | } |
| 74 | 76 | |
| 75 | - // 1. 根据配置的比例,换算实际的积分 | |
| 76 | - point = point * pointConfig.getTradeGivePoint(); | |
| 77 | - if (!bizType.isAdd() && point > 0) { | |
| 78 | - point = -point; | |
| 77 | + // 2. 更新用户积分 | |
| 78 | + boolean success = memberUserService.updateUserPoint(userId, point); | |
| 79 | + if (!success) { | |
| 80 | + throw exception(USER_POINT_NOT_ENOUGH); | |
| 79 | 81 | } |
| 80 | 82 | |
| 81 | - // 2. 增加积分记录 | |
| 82 | - MemberUserDO user = memberUserService.getUser(userId); | |
| 83 | - Integer userPoint = ObjectUtil.defaultIfNull(user.getPoint(), 0); | |
| 84 | - Integer totalPoint = userPoint + point; // 用户变动后的积分 | |
| 83 | + // 3. 增加积分记录 | |
| 85 | 84 | MemberPointRecordDO record = new MemberPointRecordDO() |
| 86 | 85 | .setUserId(userId).setBizId(bizId).setBizType(bizType.getType()) |
| 87 | 86 | .setTitle(bizType.getName()).setDescription(StrUtil.format(bizType.getDescription(), point)) |
| 88 | 87 | .setPoint(point).setTotalPoint(totalPoint); |
| 89 | 88 | memberPointRecordMapper.insert(record); |
| 90 | - | |
| 91 | - // 3. 更新用户积分 | |
| 92 | - memberUserService.updateUserPoint(userId, totalPoint); | |
| 93 | 89 | } |
| 94 | 90 | |
| 95 | 91 | } | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
| ... | ... | @@ -260,8 +260,13 @@ public class MemberUserServiceImpl implements MemberUserService { |
| 260 | 260 | } |
| 261 | 261 | |
| 262 | 262 | @Override |
| 263 | - public void updateUserPoint(Long userId, Integer point) { | |
| 264 | - memberUserMapper.updateById(new MemberUserDO().setId(userId).setPoint(point)); | |
| 263 | + public boolean updateUserPoint(Long id, Integer point) { | |
| 264 | + if (point > 0) { | |
| 265 | + memberUserMapper.updatePointIncr(id, point); | |
| 266 | + } else if (point < 0) { | |
| 267 | + return memberUserMapper.updatePointDecr(id, point) > 0; | |
| 268 | + } | |
| 269 | + return true; | |
| 265 | 270 | } |
| 266 | 271 | |
| 267 | 272 | } | ... | ... |