diff --git a/sql/mysql/point.sql b/sql/mysql/point.sql new file mode 100644 index 000000000..44123ea1b --- /dev/null +++ b/sql/mysql/point.sql @@ -0,0 +1,6 @@ +ALTER TABLE trade_order ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price; +ALTER TABLE trade_order ADD COLUMN refund_point int NOT NULL DEFAULT 0 COMMENT '退还的使用积分' AFTER use_point; +ALTER TABLE trade_order ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER refund_point; + +ALTER TABLE trade_order_item ADD COLUMN use_point int NOT NULL DEFAULT 0 COMMENT '使用的积分' AFTER point_price; +ALTER TABLE trade_order_item ADD COLUMN give_point int NOT NULL DEFAULT 0 COMMENT '赠送的积分' AFTER use_point; diff --git a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java index ab93431f4..b6b6b996d 100644 --- a/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java +++ b/yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java @@ -110,6 +110,11 @@ public class ProductSpuRespDTO { // ========== 物流相关字段 ========= + /** + * 赠送积分 + */ + private Integer giveIntegral; + /** * 物流配置模板编号 * diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java index 874651ea3..65af63406 100644 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java +++ b/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 { REWARD_ACTIVITY(5, "满减送"), MEMBER(6, "会员折扣"), - COUPON(7, "优惠劵") + COUPON(7, "优惠劵"), + POINT(8, "积分") ; public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PromotionTypeEnum::getType).toArray(); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java index bb68fc1ac..f7a03a52b 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java +++ b/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 { * 对应 taobao 的 trade.point_fee 字段 */ private Integer pointPrice; -// /** -// * 奖励的积分 TODO 疯狂:可以使用这个字段哈; -// */ -// private Integer rewardPoint; + /** + * 赠送的积分 + */ + private Integer givePoint; + /** + * 退还的使用的积分 + */ + private Integer refundPoint; } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java index 27dd13f67..77b455344 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java +++ b/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 { * 对应 taobao 的 trade.point_fee 字段 */ private Integer pointPrice; + /** + * 使用的积分 + */ + private Integer usePoint; + /** + * 赠送的积分 + */ + private Integer givePoint; // TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice; // 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的; diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java index c4f1677e5..e2ff20c7c 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java +++ b/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; import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; @@ -48,8 +49,8 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; import cn.iocoder.yudao.module.trade.enums.order.*; import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; -import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; +import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; import cn.iocoder.yudao.module.trade.service.cart.CartService; import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; @@ -105,6 +106,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @Resource private TradeMessageService tradeMessageService; + @Resource + private ProductSpuApi productSpuApi; @Resource private ProductSkuApi productSkuApi; @Resource @@ -240,8 +243,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { /** * 订单创建前,执行前置逻辑 * - * @param userId 用户编号 - * @param createReqVO 创建订单请求 + * @param userId 用户编号 + * @param createReqVO 创建订单请求 * @param calculateRespBO 订单价格计算结果 */ private void beforeCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, @@ -260,12 +263,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { /** * 订单创建后,执行后置逻辑 - * + *

* 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 * * @param userId 用户编号 * @param createReqVO 创建订单请求 - * @param order 交易订单 + * @param order 交易订单 * @param calculateRespBO 订单价格计算结果 */ private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, @@ -283,12 +286,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { .setOrderId(order.getId())); } - // 3. 扣减积分 + // 3. 扣减积分(抵扣) // 不在前置扣减的原因,是因为积分扣减时,需要记录关联业务 - if (order.getUsePoint() != null && order.getUsePoint() > 0) { - memberPointApi.reducePoint(userId, calculateRespBO.getUsePoint(), - MemberPointBizTypeEnum.ORDER_USE.getType(), String.valueOf(order.getId())); - } + reduceUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId()); // 4. 删除购物车商品 Set cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); @@ -342,8 +342,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { // TODO 芋艿:OrderLog - // 增加用户积分 - getSelf().addUserPointAsync(order.getUserId(), order.getPayPrice(), order.getId()); + // 增加用户积分(赠送) + addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_REWARD, order.getId()); // 增加用户经验 getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId()); // 增加用户佣金 @@ -640,11 +640,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { return; } // 计算总的退款金额 - TradeOrderDO order = tradeOrderMapper.selectById(tradeOrderItemMapper.selectById(id).getOrderId()); + TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); + TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); Integer orderRefundPrice = order.getRefundPrice() + refundPrice; if (isAllOrderItemAfterSaleSuccess(order.getId())) { // 如果都售后成功,则需要取消订单 tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()) - .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice) + .setRefundStatus(TradeOrderRefundStatusEnum.ALL.getStatus()).setRefundPrice(orderRefundPrice).setRefundPoint(order.getRefundPoint() + orderItem.getUsePoint()) .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now())); // TODO 芋艿:记录订单日志 @@ -655,12 +656,17 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { .setRefundStatus(TradeOrderRefundStatusEnum.PART.getStatus()).setRefundPrice(orderRefundPrice)); } - // 扣减用户积分 - getSelf().reduceUserPointAsync(order.getUserId(), orderRefundPrice, afterSaleId); - // 扣减用户经验 - getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId); - // 更新分佣记录为已失效 - getSelf().cancelBrokerageAsync(order.getUserId(), id); + // 售后成功后,执行数据回滚逻辑 + if (Objects.equals(newAfterSaleStatus, TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus())) { + // 扣减用户积分(赠送的) + reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, afterSaleId); + // 增加用户积分(返还抵扣) + addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, afterSaleId); + // 扣减用户经验 + getSelf().reduceUserExperienceAsync(order.getUserId(), orderRefundPrice, afterSaleId); + // 更新分佣记录为已失效 + getSelf().cancelBrokerageAsync(order.getUserId(), id); + } } @Override @@ -728,8 +734,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { // 3.回滚优惠券 couponApi.returnUsedCoupon(order.getCouponId()); - // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择; - // TODO @疯狂:有赞是可配置(支付 or 确认收货),我们按照支付好列;然后这里的退积分,指的是下单时的积分抵扣。 + // 4.回滚积分(抵扣的) + addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId()); // TODO 芋艿:OrderLog @@ -760,18 +766,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); } - @Async - protected void addUserPointAsync(Long userId, Integer payPrice, Long orderId) { - // TODO @疯狂:具体多少积分,需要分成 2 不分:1. 支付金额;2. 商品金额 - int bizType = MemberPointBizTypeEnum.ORDER_REWARD.getType(); - memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId)); + protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { + if (point != null && point > 0) { + memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId)); + } } - @Async - protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) { - // TODO @疯狂:退款时,按照金额比例,退还积分;https://help.youzan.com/displaylist/detail_4_4-1-49185 - int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType(); - memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); + protected void reduceUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { + if (point != null && point > 0) { + memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId)); + } } @Async diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java index e211374bf..a6982c177 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImpl.java +++ b/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 { @Resource private List priceCalculators; - // TODO @疯狂:需要搞个 TradePriceCalculator,计算赠送积分; @Override public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) { // 1.1 获得商品 SKU 数组 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java index 67019a49a..a908a93b9 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java @@ -29,7 +29,7 @@ public class TradePriceCalculateReqBO { * 对应 CouponDO 的 id 编号 */ private Long couponId; - // TODO @疯狂:需要增加一个 PriceCalculator 实现积分扣减的计算;写回到 TradePriceCalculateRespBO 的 usePoint + /** * 是否使用积分 */ diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java index 1171cd8f2..786c478bc 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java @@ -53,6 +53,11 @@ public class TradePriceCalculateRespBO { */ private Integer usePoint; + /** + * 使用的积分 + */ + private Integer givePoint; + /** * 订单价格 */ @@ -158,6 +163,10 @@ public class TradePriceCalculateRespBO { * 对应 taobao 的 trade.point_fee 字段 */ private Integer pointPrice; + /** + * 使用的积分 + */ + private Integer usePoint; /** * 应付金额(总),单位:分 * @@ -205,6 +214,11 @@ public class TradePriceCalculateRespBO { */ private List properties; + /** + * 使用的积分 + */ + private Integer givePoint; + } /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java new file mode 100644 index 000000000..4fb2b2d7b --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointGiveCalculator.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.trade.service.price.calculator; + +import cn.hutool.core.util.BooleanUtil; +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Optional; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + +/** + * 赠送积分的 {@link TradePriceCalculator} 实现类 + * + * @author owen + */ +@Component +@Order(TradePriceCalculator.ORDER_POINT_GIVE) +@Slf4j +public class TradePointGiveCalculator implements TradePriceCalculator { + @Resource + private MemberPointApi memberPointApi; + + @Override + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { + // 1.1 校验积分功能是否开启 + int givePointPerYuan = Optional.ofNullable(memberPointApi.getConfig()) + .filter(config -> BooleanUtil.isTrue(config.getTradeDeductEnable())) + .map(MemberPointConfigRespDTO::getTradeGivePoint) + .orElse(0); + if (givePointPerYuan <= 0) { + return; + } + // 1.2 校验支付金额 + if (result.getPrice().getPayPrice() <= 0) { + return; + } + + // 2.1 计算赠送积分 + int givePoint = MoneyUtils.calculateRatePriceFloor(result.getPrice().getPayPrice(), (double) givePointPerYuan); + // 2.2 计算分摊的赠送积分 + List orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); + List dividePoints = TradePriceCalculatorHelper.dividePrice(orderItems, givePoint); + + // 3.2 更新 SKU 赠送积分 + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + // 商品可能赠送了积分,所以这里要加上 + orderItem.setGivePoint(orderItem.getGivePoint() + dividePoints.get(i)); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + } + // 3.3 更新订单赠送积分 + TradePriceCalculatorHelper.recountAllGivePoint(result); + } +} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java index 6bd479802..b2fd10572 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java @@ -1,11 +1,25 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.member.api.point.MemberPointApi; +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import javax.annotation.Resource; +import java.math.RoundingMode; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + /** * 使用积分的 {@link TradePriceCalculator} 实现类 * @@ -15,15 +29,81 @@ import org.springframework.stereotype.Component; @Order(TradePriceCalculator.ORDER_POINT_USE) @Slf4j public class TradePointUsePriceCalculator implements TradePriceCalculator { + @Resource + private MemberPointApi memberPointApi; + @Resource + private MemberUserApi memberUserApi; @Override public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { - // TODO 疯狂:待实现,嘿嘿; - if (param.getPointStatus()) { - result.setUsePoint(10); - } else { + // 1.1 校验是否使用积分 + if (!BooleanUtil.isTrue(param.getPointStatus())) { result.setUsePoint(0); + return; } + // 1.2 校验积分抵扣是否开启 + MemberPointConfigRespDTO config = memberPointApi.getConfig(); + if (!checkDeductPointEnable(config)) { + return; + } + // 1.3 校验用户积分余额 + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()); + if (user.getPoint() == null || user.getPoint() < 0) { + return; + } + + // 2.1 计算积分优惠金额 + int pointPrice = calculatePointPrice(config, user.getPoint(), result); + // 2.1 计算分摊的积分、抵扣金额 + List orderItems = filterList(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSelected); + List dividePointPrices = TradePriceCalculatorHelper.dividePrice(orderItems, pointPrice); + List divideUsePoints = TradePriceCalculatorHelper.dividePrice(orderItems, result.getUsePoint()); + + // 3.1 记录优惠明细 + TradePriceCalculatorHelper.addPromotion(result, orderItems, + param.getUserId(), "积分抵扣", PromotionTypeEnum.POINT.getType(), + StrUtil.format("积分抵扣:省 {} 元", TradePriceCalculatorHelper.formatPrice(pointPrice)), + dividePointPrices); + // 3.2 更新 SKU 优惠金额 + for (int i = 0; i < orderItems.size(); i++) { + TradePriceCalculateRespBO.OrderItem orderItem = orderItems.get(i); + orderItem.setPointPrice(dividePointPrices.get(i)); + orderItem.setUsePoint(divideUsePoints.get(i)); + TradePriceCalculatorHelper.recountPayPrice(orderItem); + } + TradePriceCalculatorHelper.recountAllPrice(result); } + private boolean checkDeductPointEnable(MemberPointConfigRespDTO config) { + if (config == null) { + return false; + } + if (!BooleanUtil.isTrue(config.getTradeDeductEnable())) { + return false; + } + + // 有没有配置:1 积分抵扣多少分 + return config.getTradeDeductUnitPrice() != null && config.getTradeDeductUnitPrice() > 0; + } + + private Integer calculatePointPrice(MemberPointConfigRespDTO config, Integer usePoint, TradePriceCalculateRespBO result) { + // 每个订单最多可以使用的积分数量 + if (config.getTradeDeductMaxPrice() != null && config.getTradeDeductMaxPrice() > 0) { + usePoint = Math.min(usePoint, config.getTradeDeductMaxPrice()); + } + // 积分优惠金额(分) + int pointPrice = usePoint * config.getTradeDeductUnitPrice(); + // 0元购!!!:用户积分比较多时,积分可以抵扣的金额要大于支付金额, 这时需要根据支付金额反推使用多少积分 + if (result.getPrice().getPayPrice() < pointPrice) { + pointPrice = result.getPrice().getPayPrice(); + // 反推需要扣除的积分 + usePoint = NumberUtil.toBigDecimal(pointPrice) + .divide(NumberUtil.toBigDecimal(config.getTradeDeductUnitPrice()), 0, RoundingMode.HALF_UP) + .intValue(); + } + // 记录使用的积分 + result.setUsePoint(usePoint); + + return pointPrice; + } } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java index cddc1ea21..e5b18e88e 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java +++ b/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 { * 放在各种营销活动、优惠劵后面 TODO */ int ORDER_DELIVERY = 50; + /** + * 赠送积分,放最后 + */ + int ORDER_POINT_GIVE = 999; void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java index b8b9e0fb9..9a0a7e1d5 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java +++ b/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 { .setWeight(sku.getWeight()).setVolume(sku.getVolume()); // spu 信息 orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId()) - .setDeliveryTemplateId(spu.getDeliveryTemplateId()); + .setDeliveryTemplateId(spu.getDeliveryTemplateId()) + .setGivePoint(spu.getGiveIntegral()).setUsePoint(0); if (orderItem.getPicUrl() == null) { orderItem.setPicUrl(spu.getPicUrl()); } @@ -67,6 +68,7 @@ public class TradePriceCalculatorHelper { // 创建它的 Price 属性 result.setPrice(new TradePriceCalculateRespBO.Price()); recountAllPrice(result); + recountAllGivePoint(result); return result; } @@ -111,13 +113,22 @@ public class TradePriceCalculatorHelper { }); } + /** + * 基于订单项,重新计算赠送积分 + * + * @param result 计算结果 + */ + public static void recountAllGivePoint(TradePriceCalculateRespBO result) { + result.setGivePoint(getSumValue(result.getItems(), item -> item.getSelected() ? item.getGivePoint() : 0, Integer::sum)); + } + /** * 重新计算单个订单项的支付金额 * * @param orderItem 订单项 */ public static void recountPayPrice(TradePriceCalculateRespBO.OrderItem orderItem) { - orderItem.setPayPrice(orderItem.getPrice()* orderItem.getCount() + orderItem.setPayPrice(orderItem.getPrice() * orderItem.getCount() - orderItem.getDiscountPrice() + orderItem.getDeliveryPrice() - orderItem.getCouponPrice() @@ -145,6 +156,12 @@ public class TradePriceCalculatorHelper { if (orderItem.getPointPrice() == null) { orderItem.setPointPrice(0); } + if (orderItem.getUsePoint() == null) { + orderItem.setUsePoint(0); + } + if (orderItem.getGivePoint() == null) { + orderItem.setGivePoint(0); + } recountPayPrice(orderItem); }); } @@ -169,7 +186,7 @@ public class TradePriceCalculatorHelper { */ public static Integer calculateTotalCount(List orderItems) { return getSumValue(orderItems, - orderItem -> orderItem.getSelected() ? orderItem.getCount() : 0, // 未选中的情况下,不计算数量 + orderItem -> orderItem.getSelected() ? orderItem.getCount() : 0, // 未选中的情况下,不计算数量 Integer::sum); } @@ -177,7 +194,7 @@ public class TradePriceCalculatorHelper { * 按照支付金额,返回每个订单项的分摊金额数组 * * @param orderItems 订单项数组 - * @param price 金额 + * @param price 金额 * @return 分摊金额数组,和传入的 orderItems 一一对应 */ public static List dividePrice(List orderItems, Integer price) { @@ -210,12 +227,12 @@ public class TradePriceCalculatorHelper { /** * 添加【匹配】单个 OrderItem 的营销明细 * - * @param result 价格计算结果 + * @param result 价格计算结果 * @param orderItem 单个订单商品 SKU - * @param id 营销编号 - * @param name 营销名字 - * @param description 满足条件的提示 - * @param type 营销类型 + * @param id 营销编号 + * @param name 营销名字 + * @param description 满足条件的提示 + * @param type 营销类型 * @param discountPrice 单个订单商品 SKU 的优惠价格(总) */ public static void addPromotion(TradePriceCalculateRespBO result, TradePriceCalculateRespBO.OrderItem orderItem, @@ -226,7 +243,7 @@ public class TradePriceCalculatorHelper { /** * 添加【匹配】多个 OrderItem 的营销明细 * - * @param result 价格计算结果 + * @param result 价格计算结果 * @param orderItems 多个订单商品 SKU * @param id 营销编号 * @param name 营销名字 @@ -235,7 +252,7 @@ public class TradePriceCalculatorHelper { * @param discountPrices 多个订单商品 SKU 的优惠价格(总),和 orderItems 一一对应 */ public static void addPromotion(TradePriceCalculateRespBO result, List orderItems, - Long id, String name, Integer type, String description, List discountPrices) { + Long id, String name, Integer type, String description, List discountPrices) { // 创建营销明细 Item List promotionItems = new ArrayList<>(discountPrices.size()); for (int i = 0; i < orderItems.size(); i++) { @@ -255,12 +272,12 @@ public class TradePriceCalculatorHelper { /** * 添加【不匹配】多个 OrderItem 的营销明细 * - * @param result 价格计算结果 - * @param orderItems 多个订单商品 SKU - * @param id 营销编号 - * @param name 营销名字 - * @param description 满足条件的提示 - * @param type 营销类型 + * @param result 价格计算结果 + * @param orderItems 多个订单商品 SKU + * @param id 营销编号 + * @param name 营销名字 + * @param description 满足条件的提示 + * @param type 营销类型 */ public static void addNotMatchPromotion(TradePriceCalculateRespBO result, List orderItems, Long id, String name, Integer type, String description) { diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java index 3eb749fb6..c6df95f7c 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.member.api.point; +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; import javax.validation.constraints.Min; @@ -11,6 +12,13 @@ import javax.validation.constraints.Min; */ public interface MemberPointApi { + /** + * 获得积分配置 + * + * @return 积分配置 + */ + MemberPointConfigRespDTO getConfig(); + /** * 增加用户积分 * diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/dto/MemberPointConfigRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/dto/MemberPointConfigRespDTO.java new file mode 100644 index 000000000..a9783352a --- /dev/null +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/dto/MemberPointConfigRespDTO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.member.api.point.dto; + +import lombok.Data; + +/** + * 用户信息 Response DTO + * + * @author 芋道源码 + */ +@Data +public class MemberPointConfigRespDTO { + /** + * 积分抵扣开关 + */ + private Boolean tradeDeductEnable; + /** + * 积分抵扣,单位:分 + *

+ * 1 积分抵扣多少分 + */ + private Integer tradeDeductUnitPrice; + /** + * 积分抵扣最大值 + */ + private Integer tradeDeductMaxPrice; + /** + * 1 元赠送多少分 + */ + private Integer tradeGivePoint; +} diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java index 10d96365f..0fa2c3e0b 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java @@ -33,5 +33,8 @@ public class MemberUserRespDTO { * 手机 */ private String mobile; - + /** + * 积分 + */ + private Integer point; } diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java index 933f17168..c25de2461 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1004001001, "手机号未注册用户"); ErrorCode USER_MOBILE_USED = new ErrorCode(1004001002, "修改手机失败,该手机号({})已经被使用"); + ErrorCode USER_POINT_NOT_ENOUGH = new ErrorCode(1004001003, "用户积分余额不足"); // ========== AUTH 模块 1004003000 ========== ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java index fe76cdb52..68e61cc9f 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java +++ b/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 { SIGN(1, "签到", "签到获得 {} 积分", true), ORDER_REWARD(10, "订单奖励", "下单获得 {} 积分", true), - ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false), // 退回积分 + ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 退回积分 ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 扣减积分 + AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 退回积分 + AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 扣减积分 ; /** diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java index ee407eaf6..51239388a 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.member.api.point; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; +import cn.iocoder.yudao.module.member.convert.point.MemberPointConfigConvert; import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; +import cn.iocoder.yudao.module.member.service.point.MemberPointConfigService; import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -21,9 +25,17 @@ public class MemberPointApiImpl implements MemberPointApi { @Resource private MemberPointRecordService memberPointRecordService; + @Resource + private MemberPointConfigService memberPointConfigService; + + @Override + public MemberPointConfigRespDTO getConfig() { + return MemberPointConfigConvert.INSTANCE.convert01(memberPointConfigService.getPointConfig()); + } @Override public void addPoint(Long userId, Integer point, Integer bizType, String bizId) { + Assert.isTrue(point > 0); MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); if (bizTypeEnum == null) { throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); @@ -33,11 +45,12 @@ public class MemberPointApiImpl implements MemberPointApi { @Override public void reducePoint(Long userId, Integer point, Integer bizType, String bizId) { + Assert.isTrue(point > 0); MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType); if (bizTypeEnum == null) { throw exception(POINT_RECORD_BIZ_NOT_SUPPORT); } - memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId); + memberPointRecordService.createPointRecord(userId, -point, bizTypeEnum, bizId); } } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java index e600378bb..c7f0e9881 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointConfigConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.member.convert.point; +import cn.iocoder.yudao.module.member.api.point.dto.MemberPointConfigRespDTO; import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigRespVO; import cn.iocoder.yudao.module.member.controller.admin.point.vo.config.MemberPointConfigSaveReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO; @@ -20,4 +21,5 @@ public interface MemberPointConfigConvert { MemberPointConfigDO convert(MemberPointConfigSaveReqVO bean); + MemberPointConfigRespDTO convert01(MemberPointConfigDO pointConfig); } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java index 902057272..3f871020c 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java @@ -1,12 +1,14 @@ package cn.iocoder.yudao.module.member.dal.mysql.user; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -62,4 +64,33 @@ public interface MemberUserMapper extends BaseMapperX { .apply("FIND_IN_SET({0}, tag_ids)", tagId)); } + /** + * 更新用户积分(增加) + * + * @param id 用户编号 + * @param incrCount 增加积分(正数) + */ + default void updatePointIncr(Long id, Integer incrCount) { + Assert.isTrue(incrCount > 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" point = point + " + incrCount) + .eq(MemberUserDO::getId, id); + update(null, lambdaUpdateWrapper); + } + + /** + * 更新用户积分(减少) + * + * @param id 用户编号 + * @param incrCount 增加积分(负数) + * @return 更新行数 + */ + default int updatePointDecr(Long id, Integer incrCount) { + Assert.isTrue(incrCount < 0); + LambdaUpdateWrapper lambdaUpdateWrapper = new LambdaUpdateWrapper() + .setSql(" point = point + " + incrCount) // 负数,所以使用 + 号 + .eq(MemberUserDO::getId, id); + return update(null, lambdaUpdateWrapper); + } + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java index a28e94795..da67a9ec2 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; -import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointConfigDO; import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper; @@ -14,6 +13,7 @@ import cn.iocoder.yudao.module.member.service.user.MemberUserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; @@ -21,7 +21,9 @@ import javax.annotation.Resource; import java.util.List; import java.util.Set; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.USER_POINT_NOT_ENOUGH; /** @@ -36,8 +38,6 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService { @Resource private MemberPointRecordMapper memberPointRecordMapper; - @Resource - private MemberPointConfigService memberPointConfigService; @Resource private MemberUserService memberUserService; @@ -64,32 +64,28 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService { } @Override + @Transactional(rollbackFor = Exception.class) public void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId) { - MemberPointConfigDO pointConfig = memberPointConfigService.getPointConfig(); - if (pointConfig == null || pointConfig.getTradeGivePoint() == null) { - log.error("[createPointRecord][增加积分失败:tradeGivePoint 未配置,userId({}) point({}) bizType({}) bizId({})]", - userId, point, bizType.getType(), bizId); - return; - } - - // 1. 根据配置的比例,换算实际的积分 - point = point * pointConfig.getTradeGivePoint(); - if (!bizType.isAdd() && point > 0) { - point = -point; - } - - // 2. 增加积分记录 + // 1. 校验用户积分余额 MemberUserDO user = memberUserService.getUser(userId); Integer userPoint = ObjectUtil.defaultIfNull(user.getPoint(), 0); - Integer totalPoint = userPoint + point; // 用户变动后的积分 + int totalPoint = userPoint + point; // 用户变动后的积分 + if (totalPoint < 0) { + throw exception(USER_POINT_NOT_ENOUGH); + } + + // 2. 更新用户积分 + boolean success = memberUserService.updateUserPoint(userId, point); + if (!success) { + throw exception(USER_POINT_NOT_ENOUGH); + } + + // 3. 增加积分记录 MemberPointRecordDO record = new MemberPointRecordDO() .setUserId(userId).setBizId(bizId).setBizType(bizType.getType()) .setTitle(bizType.getName()).setDescription(StrUtil.format(bizType.getDescription(), point)) .setPoint(point).setTotalPoint(totalPoint); memberPointRecordMapper.insert(record); - - // 3. 更新用户积分 - memberUserService.updateUserPoint(userId, totalPoint); } } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java index f1a0a7265..50433a559 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java @@ -164,6 +164,7 @@ public interface MemberUserService { * * @param userId 用户编号 * @param point 积分数量 + * @return 更新结果 */ - void updateUserPoint(Long userId, Integer point); + boolean updateUserPoint(Long userId, Integer point); } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java index 8381c73f1..3119c48db 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java +++ b/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 { } @Override - public void updateUserPoint(Long userId, Integer point) { - memberUserMapper.updateById(new MemberUserDO().setId(userId).setPoint(point)); + public boolean updateUserPoint(Long id, Integer point) { + if (point > 0) { + memberUserMapper.updatePointIncr(id, point); + } else if (point < 0) { + return memberUserMapper.updatePointDecr(id, point) > 0; + } + return true; } }