promotion:增加助力砍价的接口

This commit is contained in:
YunaiV 2023-10-05 00:05:20 +08:00
parent cc087e4eca
commit 16e436a0f5
12 changed files with 238 additions and 9 deletions

View File

@ -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, "助力失败,您已经助力过了");
}

View File

@ -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
}

View File

@ -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")

View File

@ -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") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@TableName("promotion_bargain_help")
@KeySequence("promotion_bargain_help_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 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;

View File

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

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}