共计 7923 个字符,预计需要花费 20 分钟才能阅读完成。
一、前言
更多文章返回 -> 微信小程序直播间开发抽红包性能
微信小程序直播是微信官网提供的商家经营工具,商家可通过在小程序内直播实现用户互动与商品销售的闭环,无需任何的跳转,进步下单转化率,直播更是成为链接商家和消费者的重要销售渠道!
小程序直播具备评论、点赞、连麦、拍一拍等丰盛的互动性能,抽奖、优惠券等高效的营销性能,以及成员治理、评论治理、推流直播、数据看板等欠缺商家工具。通过引入小程序直播组件,商家自有小程序可疾速具备直播能力,晋升经营效率。
尽管有抽奖,优惠券的营销性能,然而却没有红包性能,如果有红包性能,减少了和用户的互动,更能吸引用户留下来观看直播。其实,咱们是能够本人在直播间开发红包性能的。当然,要实现这个性能,小程序要先开明直播权限,开明直播权限需满足小程序近 90 天内有过领取行为,如果因为这个无奈开明的分割我,能够疾速开明。
二、思路
说一下这个性能实现的思路,首先后盾做一个录红包的菜单,字段包含主播名称、主播头像、标语(恭喜发财,大吉大利)、无效工夫、红包金额、红包个数、残余现金红包金额、残余现金红包个数、创立工夫、版本号(乐观锁),还要有一个抢红包记录表,字段包含红包 id、抢到红包用户的 id、抢到红包用户的名称、抢到红包用户的头像、抢到的红包金额、创立工夫。而后去小程序直播后盾录商品,商品门路字段填写要跳转的小程序红包页面门路,须要在前面拼接红包 id 参数,比方像这样,
pages/redPacket/redPacket.html?redPacketId=123456
当用户在直播页面点击该商品进入红包页面,前端就能够拿到红包 id 传给后盾接口,查到该红包的相干信息,做各种操作了,比方生成随机金额,扣减红包金额和个数等等。这个须要主播疏导用户做好抢红包的筹备,而后直播间助理通过上架商品来显示红包商品。
思路很简略,代码实现起来也很简略,然而咱们须要思考几个问题,
1、抢红包就像秒杀商品一样,是拼手速的,要思考并发,不能呈现超卖 (这里是超抢) 的景象,不然亏的是老板的💰,就该找你聊天了。这里咱们采纳简略的乐观锁来解决这个问题。
2、如果乐观锁更新失败,间接返回给用户提醒抢到空红包或其余敌对提醒也是能够的,然而如果咱们减少重试机制的话,体验会更好点。这里咱们采纳注解的形式进行重试,默认重试 3 次。
三、实现
咱们先来实现注解重试的性能,且看代码如下
/**
* 定义重试机制注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiRetry {
/**
* 默认次数
* @return
*/
int value() default 3;}
/**
* 定义一个重试机制切面类
*/
@Slf4j
@Aspect
@Component
public class RetryAspect {@Pointcut("@annotation(com.redPacket.common.apiIdempotent.annotation.ApiRetry)")
public void retryPointcut() {}
@Around("retryPointcut() && @annotation(retry)")
@Transactional(isolation = Isolation.READ_COMMITTED)
public Object tryAgain(ProceedingJoinPoint joinPoint, ApiRetry retry) throws Throwable {
int count = 0;
do {
count++;
try {return joinPoint.proceed();
} catch (ApiRetryException e) {if (count > retry.value()) {log.error("重试失败!");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new ApiRetryException("以后用户较多,请稍后重试");
} else {log.info("===== 正在重试,第 {} 次 =====",count);
}
}
} while (true);
}
}
/**
* 定义一个重试机制的异样
*/
public class ApiRetryException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer status = 0;
private String msg;
public ApiRetryException(String msg) {super(msg);
this.msg = msg;
}
public ApiRetryException(String msg, Throwable e) {super(msg, e);
this.msg = msg;
}
public ApiRetryException(String msg, Integer status) {super(msg);
this.msg = msg;
this.status = status;
}
public ApiRetryException(String msg, Integer status, Throwable e) {super(msg, e);
this.msg = msg;
this.status = status;
}
public Integer getStatus() {return status;}
public void setStatus(Integer status) {this.status = status;}
public String getMsg() {return msg;}
public void setMsg(String msg) {this.msg = msg;}
}
/**
* 定义异样处理器
*/
@RestControllerAdvice
public class RestExceptionHandler {private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 解决重试机制异样
*/
@ExceptionHandler(ApiRetryException.class)
public JsonModel handleApiRetryException(ApiRetryException e){JsonModel jsonModel = new JsonModel();
jsonModel.setStatus(e.getStatus());
jsonModel.setMsg(e.getMsg());
return jsonModel;
}
}
对于红包接口就三个,
1、给前端判断是弹出抢红包的窗口还是弹出其余提醒窗口(你已抢过该红包了、手慢了,红包已过期、手慢了,红包派完了)。
/**
* 进入红包页面判断是否能够抽红包
* @param redPacketId
* @param userId
* @return
*/
@Override
public JsonModel intoRedPacket(String redPacketId, String userId) {ChatroomRedPacketEntity redPacket = baseMapper.selectById(redPacketId);
if (redPacket == null) {throw new JsonModelException("id 为【"+redPacketId+"】的红包不存在");
}
ChatroomRedPacketRecordEntity hasRecord = chatroomRedPacketRecordService.getOne(Wrappers.<ChatroomRedPacketRecordEntity>lambdaQuery()
.eq(ChatroomRedPacketRecordEntity::getRedPacketId, redPacketId)
.eq(ChatroomRedPacketRecordEntity::getUserId, userId));
if (hasRecord != null) {return JsonModel.toFail(10001,"你已抢过该红包了");
}
redPacket = baseMapper.selectOne(Wrappers.<ChatroomRedPacketEntity>lambdaQuery()
.eq(ChatroomRedPacketEntity::getId,redPacketId)
.apply("date_add(create_date, interval valid_time hour) >= current_timestamp"));
if (redPacket == null) {return JsonModel.toFail(10002,"手慢了,红包已过期");
}
if (redPacket.getCashNum()+redPacket.getCouponNum() <= 0) {return JsonModel.toFail(10003,"手慢了,红包派完了");
}
return JsonModel.toSuccess(200,"弹出抽红包窗口");
}
@ApiOperation(value = "进入红包页面判断是否能够抽红包")
@ApiImplicitParams({@ApiImplicitParam(name = "redPacketId", value = "红包 id", required = true, paramType = "body", dataType = "String"),
@ApiImplicitParam(name = "userId", value = "用户 id", required = true, paramType = "body", dataType = "String")
})
@PostMapping("/intoRedPacket/{redPacketId}/{userId}")
public JsonModel intoRedPacket(@PathVariable("redPacketId")String redPacketId,
@PathVariable("userId")String userId) {return chatroomRedPacketService.intoRedPacket(redPacketId,userId);
}
2、这个是重中之重,就是点击抢
/**
* 抽红包
* @param redPacketId
* @param userId
* @return
*/
@Override
@ApiRetry
@Transactional(rollbackFor = Exception.class)
public JsonModel grabRedPacket(String redPacketId, String userId) {ChatroomRedPacketEntity redPacket = baseMapper.selectById(redPacketId);
if (redPacket == null) {throw new JsonModelException("id 为【"+redPacketId+"】的红包不存在");
}
ChatroomRedPacketRecordEntity hasRecord = chatroomRedPacketRecordService.getOne(Wrappers.<ChatroomRedPacketRecordEntity>lambdaQuery()
.eq(ChatroomRedPacketRecordEntity::getRedPacketId, redPacketId)
.eq(ChatroomRedPacketRecordEntity::getUserId, userId));
if (hasRecord != null) {return JsonModel.toFail(10001,"你已抢过该红包了");
}
redPacket = baseMapper.selectOne(Wrappers.<ChatroomRedPacketEntity>lambdaQuery()
.eq(ChatroomRedPacketEntity::getId,redPacketId)
.apply("date_add(create_date, interval valid_time hour) >= current_timestamp"));
if (redPacket == null) {return JsonModel.toFail(10002,"手慢了,红包已过期");
}
if (redPacket.getCashNum()+redPacket.getCouponNum() <=0) {return JsonModel.toFail(10003,"手慢了,红包派完了");
}
long money = 0;
long restCashNum = redPacket.getRestCashNum();
long restCashAmount = redPacket.getRestCashAmount().longValue();
if (restCashNum >= 1) {
restCashNum = restCashNum - 1;
if (restCashNum == 0) {money = restCashAmount;} else {money = ThreadLocalRandom.current().nextInt((int) (restCashAmount / (restCashNum+1) * 2 - 1)) + 1;
}
restCashAmount = restCashAmount - money;
}
result.put("money",money);
// 更新红包残余个数和残余金额
boolean isUpdate = chatroomRedPacketService.update(Wrappers.<ChatroomRedPacketEntity>lambdaUpdate()
.set(ChatroomRedPacketEntity::getRestCashNum,restCashNum)
.set(ChatroomRedPacketEntity::getRestCashAmount,restCashAmount)
.set(ChatroomRedPacketEntity::getVersion,redPacket.getVersion() + 1)
.eq(ChatroomRedPacketEntity::getId,redPacket.getId())
.eq(ChatroomRedPacketEntity::getVersion,redPacket.getVersion()));
optimisticHandler(redPacket,isUpdate,userId,money);
return JsonModel.toSuccess(result);
}
private void optimisticHandler(ChatroomRedPacketEntity redPacket,boolean isUpdate,
String userId,long money)
if (!isUpdate) {throw new ApiRetryException("更新失败,开始重试");
} else {
// 入库抽红包记录
UserEntity user = userService.getById(userId);
ChatroomRedPacketRecordEntity record = new ChatroomRedPacketRecordEntity();
record.setRedPacketId(redPacket.getId().toString())
.setUserId(userId)
.setAmount(new BigDecimal(money))
.setCreateDate(new Date())
.setUserName(user.getNickName())
.setUserAvatar(user.getAvatarImageId());
chatroomRedPacketRecordService.save(record);
// 这里写其余业务逻辑,比方给用户账上减少相应的红包金额,能够提现
}
}
3、最初一个是抢红包记录列表。
/**
* 抽红包记录
* @param redPacketId
* @return
*/
@Override
public JsonModel redPacketRecord(String redPacketId) {ChatroomRedPacketEntity redPacket = baseMapper.selectById(redPacketId);
if (redPacket == null) {throw new JsonModelException("id 为【"+redPacketId+"】的红包不存在");
}
Map<String,Object> result = new HashMap<>();
// 红包信息
result.put("redPacket",redPacket);
List<ChatroomRedPacketRecordEntity> recordList = chatroomRedPacketRecordService.list(Wrappers.<ChatroomRedPacketRecordEntity>lambdaQuery()
.eq(ChatroomRedPacketRecordEntity::getRedPacketId, redPacketId)
.ne(ChatroomRedPacketRecordEntity::getAmount, 0)
.orderByDesc(ChatroomRedPacketRecordEntity::getCreateDate));
// 领红包记录
result.put("recordList",recordList);
return JsonModel.toSuccess(result);
}
@ApiOperation(value = "抽红包记录")
@ApiImplicitParams({@ApiImplicitParam(name = "redPacketId", value = "红包 id", required = true, paramType = "body", dataType = "String"),
@ApiImplicitParam(name = "userId", value = "用户 id", required = true, paramType = "body", dataType = "String")
})
@PostMapping("/redPacketRecord/{redPacketId}")
public JsonModel redPacketRecord(@PathVariable("redPacketId")String redPacketId) {return chatroomRedPacketService.redPacketRecord(redPacketId)
}
四、总结
思路就是那样的思路,至于实现形式,代码逻辑有很多种,能够自行实现。有其余计划的同学能够评论区