Explorar o código

promotion:增加助力砍价的接口

YunaiV hai 1 ano
pai
achega
16e436a0f5
Modificáronse 12 ficheiros con 238 adicións e 9 borrados
  1. 7 0
      yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
  2. 9 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http
  3. 10 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.java
  4. 9 7
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainHelpDO.java
  5. 4 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainRecordDO.java
  6. 27 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainHelpMapper.java
  7. 6 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainRecordMapper.java
  8. 4 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
  9. 22 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpService.java
  10. 102 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpServiceImpl.java
  11. 23 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordService.java
  12. 15 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordServiceImpl.java

+ 7 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java

@@ -100,4 +100,11 @@ public interface ErrorCodeConstants {
     ErrorCode BARGAIN_RECORD_CREATE_FAIL_LIMIT = new ErrorCode(1_013_013_002, "参与失败,您已达到当前砍价活动的参与上限");
     ErrorCode BARGAIN_JOIN_RECORD_NOT_SUCCESS = new ErrorCode(1_013_013_004, "下单失败,砍价未成功");
 
+    // ========== 砍价助力 1-013-014-000 ==========
+    ErrorCode BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS = new ErrorCode(1_013_014_000, "助力失败,砍价记录不处于进行中");
+    ErrorCode BARGAIN_HELP_CREATE_FAIL_RECORD_SELF = new ErrorCode(1_013_014_001, "助力失败,不能助力自己");
+    ErrorCode BARGAIN_HELP_CREATE_FAIL_LIMIT = new ErrorCode(1_013_014_002, "助力失败,您已达到当前砍价活动的助力上限");
+    ErrorCode BARGAIN_HELP_CREATE_FAIL_CONFLICT = new ErrorCode(1_013_014_003, "助力失败,请重试");
+    ErrorCode BARGAIN_HELP_CREATE_FAIL_HELP_EXISTS = new ErrorCode(1_013_014_004, "助力失败,您已经助力过了");
+
 }

+ 9 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http

@@ -0,0 +1,9 @@
+### /promotion/bargain-record/create 创建砍价助力
+POST {{appApi}}/promotion/bargain-help/create
+Authorization: Bearer test248
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+
+{
+  "recordId": 26
+}

+ 10 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.java

@@ -3,16 +3,20 @@ package cn.iocoder.yudao.module.promotion.controller.app.bargain;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpRespVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;
+import cn.iocoder.yudao.module.promotion.service.bargain.BargainHelpService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 @Tag(name = "用户 App - 砍价助力")
 @RestController
@@ -20,10 +24,14 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 @Validated
 public class AppBargainHelpController {
 
+    @Resource
+    private BargainHelpService bargainHelpService;
+
     @PostMapping("/create")
     @Operation(summary = "创建砍价助力", description = "给拼团记录砍一刀") // 返回结果为砍价金额,单位:分
-    public CommonResult<Long> createBargainHelp(@RequestBody AppBargainHelpCreateReqVO reqVO) {
-         return success(20L);
+    public CommonResult<Integer> createBargainHelp(@RequestBody AppBargainHelpCreateReqVO reqVO) {
+        BargainHelpDO help = bargainHelpService.createBargainHelp(getLoginUserId(), reqVO);
+        return success(help.getReducePrice());
     }
 
     @GetMapping("/list")

+ 9 - 7
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainAssistDO.java → yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainHelpDO.java

@@ -7,19 +7,19 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
 
 /**
- * 砍价助力 DO TODO 芋艿:表结构
+ * 砍价助力 DO
  *
  * @author HUIHUI
  */
-@TableName("promotion_bargain_assist")
-@KeySequence("promotion_bargain_assist_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("promotion_bargain_help")
+@KeySequence("promotion_bargain_help_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class BargainAssistDO extends BaseDO {
+public class BargainHelpDO extends BaseDO {
 
     /**
      * 编号
@@ -29,11 +29,14 @@ public class BargainAssistDO extends BaseDO {
 
     /**
      * 砍价活动编号
+     *
+     * 关联 {@link BargainActivityDO#getId()} 字段
      */
     private Long activityId;
-
     /**
      * 砍价记录编号
+     *
+     * 关联 {@link BargainRecordDO#getId()} 字段
      */
     private Long recordId;
 
@@ -41,9 +44,8 @@ public class BargainAssistDO extends BaseDO {
      * 用户编号
      */
     private Long userId;
-
     /**
-     * 减少价格。单位
+     * 减少价格,单位:
      */
     private Integer reducePrice;
 

+ 4 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/bargain/BargainRecordDO.java

@@ -61,6 +61,10 @@ public class BargainRecordDO extends BaseDO {
     /**
      * 砍价状态
      *
+     * 砍价成功的条件是:(2 选 1)
+     *  1. 砍价到 {@link BargainActivityDO#getBargainMinPrice()} 底价
+     *  2. 助力人数到达 {@link BargainActivityDO#getUserSize()} 人
+     *
      * 枚举 {@link BargainRecordStatusEnum}
      */
     private Integer status;

+ 27 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainHelpMapper.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.promotion.dal.mysql.bargain;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface BargainHelpMapper extends BaseMapperX<BargainHelpDO> {
+
+    default Long selectCountByUserIdAndActivityId(Long userId, Long activityId) {
+        return selectCount(new LambdaQueryWrapper<>(BargainHelpDO.class)
+                .eq(BargainHelpDO::getUserId, userId)
+                .eq(BargainHelpDO::getActivityId, activityId));
+    }
+
+    default Long selectCountByRecordId(Long recordId) {
+        return selectCount(BargainHelpDO::getRecordId, recordId);
+    }
+
+    default BargainHelpDO selectByUserIdAndRecordId(Long userId, Long recordId) {
+        return selectOne(new LambdaQueryWrapper<>(BargainHelpDO.class)
+                .eq(BargainHelpDO::getUserId, userId)
+                .eq(BargainHelpDO::getRecordId, recordId));
+    }
+
+}

+ 6 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainRecordMapper.java

@@ -36,4 +36,10 @@ public interface BargainRecordMapper extends BaseMapperX<BargainRecordDO> {
                 .eq(BargainRecordDO::getStatus, status));
     }
 
+    default int updateByIdAndBargainPrice(Long id, Integer whereBargainPrice, BargainRecordDO updateObj) {
+        return update(updateObj, new LambdaQueryWrapper<>(BargainRecordDO.class)
+                .eq(BargainRecordDO::getId, id)
+                .eq(BargainRecordDO::getBargainPrice, whereBargainPrice));
+    }
+
 }

+ 4 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.BargainActivityCreateReqVO;
@@ -151,6 +152,9 @@ public class BargainActivityServiceImpl implements BargainActivityService {
         if (activity.getStock() <= 0) {
             throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH);
         }
+        if (LocalDateTimeUtils.isBetween(activity.getStartTime(), activity.getEndTime())) {
+            throw exception(BARGAIN_ACTIVITY_TIME_END);
+        }
         return activity;
     }
 

+ 22 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpService.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.promotion.service.bargain;
+
+import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;
+
+/**
+ * 砍价助力 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BargainHelpService {
+
+    /**
+     * 创建砍价助力(帮人砍价)
+     *
+     * @param userId 用户编号
+     * @param reqVO 请求信息
+     * @return 砍价助力记录
+     */
+    BargainHelpDO createBargainHelp(Long userId, AppBargainHelpCreateReqVO reqVO);
+
+}

+ 102 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainHelpServiceImpl.java

@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.promotion.service.bargain;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
+import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainHelpMapper;
+import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
+import jodd.util.MathUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
+
+/**
+ * 砍价助力 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class BargainHelpServiceImpl implements BargainHelpService {
+
+    @Resource
+    private BargainHelpMapper bargainHelpMapper;
+
+    @Resource
+    private BargainRecordService bargainRecordService;
+    @Resource
+    private BargainActivityService bargainActivityService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public BargainHelpDO createBargainHelp(Long userId, AppBargainHelpCreateReqVO reqVO) {
+        // 1.1 校验砍价记录存在,并且处于进行中
+        BargainRecordDO record = bargainRecordService.getBargainRecord(reqVO.getRecordId());
+        if (record == null) {
+            throw exception(BARGAIN_RECORD_NOT_EXISTS);
+        }
+        if (ObjUtil.notEqual(record.getStatus(), BargainRecordStatusEnum.IN_PROGRESS.getStatus())) {
+            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS);
+        }
+        // 1.2 不能自己给自己砍价
+        if (ObjUtil.equal(record.getUserId(), userId)) {
+            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_SELF);
+        }
+
+        // 2.1 校验砍价活动
+        BargainActivityDO activity = bargainActivityService.getBargainActivity(record.getActivityId());
+        // 2.2 校验自己是否助力次数上限
+        if (bargainHelpMapper.selectCountByUserIdAndActivityId(userId, activity.getId())
+                >= activity.getBargainCount()) {
+            throw exception(BARGAIN_HELP_CREATE_FAIL_LIMIT);
+        }
+        // 2.3 特殊情况:砍价已经砍到最低价,不能再砍了
+        if (record.getBargainPrice() <= activity.getBargainMinPrice()) {
+            throw exception(BARGAIN_HELP_CREATE_FAIL_RECORD_NOT_IN_PROCESS);
+        }
+
+        // 3. 已经助力
+        if (bargainHelpMapper.selectByUserIdAndRecordId(userId, record.getId()) != null) {
+            throw exception(BARGAIN_HELP_CREATE_FAIL_HELP_EXISTS);
+        }
+
+        // 4.1 计算砍价金额
+        Integer reducePrice = calculateReducePrice(activity, record);
+        Assert.isTrue(reducePrice > 0, "砍价金额必须大于 0 元");
+        // 4.2 创建助力记录
+        BargainHelpDO help = BargainHelpDO.builder().userId(userId).activityId(activity.getId())
+                .recordId(record.getId()).reducePrice(reducePrice).build();
+        bargainHelpMapper.insert(help);
+
+        // 5. 判断砍价记录是否完成
+        Boolean success = record.getBargainPrice() - reducePrice <= activity.getBargainMinPrice() // 情况一:砍价已经砍到最低价
+                || bargainHelpMapper.selectCountByRecordId(reqVO.getRecordId()) >= activity.getTotalLimitCount(); // 情况二:砍价助力已经达到上限
+        if (!bargainRecordService.updateBargainRecordBargainPrice(
+                record.getId(), record.getBargainPrice(), reducePrice, success)) {
+            // 多人一起砍价,需要重试
+            throw exception(BARGAIN_HELP_CREATE_FAIL_CONFLICT);
+        }
+        return help;
+    }
+
+    // TODO 芋艿:优化点:实现一个更随机的逻辑,可以按照你自己的业务;
+    private Integer calculateReducePrice(BargainActivityDO activity, BargainRecordDO record) {
+        // 1. 随机金额
+        Integer reducePrice = MathUtil.randomInt(activity.getBargainMinPrice(),
+                activity.getRandomMaxPrice() + 1); // + 1 的原因是,randomInt 默认不包含第二个参数
+        // 2. 校验是否超过砍价上限
+        if (record.getBargainPrice() - reducePrice < activity.getBargainMinPrice()) {
+            reducePrice = record.getBargainPrice() - activity.getBargainMinPrice();
+        }
+        return reducePrice;
+    }
+
+}

+ 23 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordService.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.service.bargain;
 
 import cn.iocoder.yudao.module.promotion.api.bargain.dto.BargainValidateJoinRespDTO;
 import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordCreateReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
 
 /**
  * 砍价记录 service 接口
@@ -20,6 +21,20 @@ public interface BargainRecordService {
      */
     Long createBargainRecord(Long userId, AppBargainRecordCreateReqVO reqVO);
 
+    /**
+     * 更新砍价记录的砍价金额
+     *
+     * 如果满足砍价成功的条件,则更新砍价记录的状态为成功
+     *
+     * @param id 砍价记录编号
+     * @param whereBargainPrice 当前的砍价金额
+     * @param reducePrice 减少的砍价金额
+     * @param success 是否砍价成功
+     * @return 是否更新成功。注意,如果并发更新时,会更新失败
+     */
+    Boolean updateBargainRecordBargainPrice(Long id, Integer whereBargainPrice,
+                                            Integer reducePrice, Boolean success);
+
     /**
      * 【下单前】校验是否参与砍价活动
      * <p>
@@ -32,4 +47,12 @@ public interface BargainRecordService {
      */
     BargainValidateJoinRespDTO validateJoinBargain(Long userId, Long bargainRecordId, Long skuId);
 
+    /**
+     * 获得砍价记录
+     *
+     * @param id 砍价记录编号
+     * @return 砍价记录
+     */
+    BargainRecordDO getBargainRecord(Long id);
+
 }

+ 15 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainRecordServiceImpl.java

@@ -58,6 +58,16 @@ public class BargainRecordServiceImpl implements BargainRecordService {
         return record.getId();
     }
 
+    @Override
+    public Boolean updateBargainRecordBargainPrice(Long id, Integer whereBargainPrice,
+                                                   Integer reducePrice, Boolean success) {
+        BargainRecordDO updateObj = new BargainRecordDO().setBargainPrice(whereBargainPrice - reducePrice);
+        if (success) {
+            updateObj.setStatus(BargainRecordStatusEnum.SUCCESS.getStatus());
+        }
+        return bargainRecordMapper.updateByIdAndBargainPrice(id, whereBargainPrice, updateObj) > 0;
+    }
+
     @Override
     public BargainValidateJoinRespDTO validateJoinBargain(Long userId, Long bargainRecordId, Long skuId) {
         // 1.1 拼团记录不存在
@@ -77,4 +87,9 @@ public class BargainRecordServiceImpl implements BargainRecordService {
                 .setBargainPrice(record.getBargainPrice());
     }
 
+    @Override
+    public BargainRecordDO getBargainRecord(Long id) {
+        return bargainRecordMapper.selectById(id);
+    }
+
 }