One, foreword

More articles to -> WeChat small program broadcast room development draw red envelope function

WeChat small program live broadcast is an official business tool provided by WeChat. Through live broadcast in small program, merchants can realize the closed loop of user interaction and product sales, without any jump, and improve the conversion rate of orders. Live broadcast has become an important sales channel linking merchants and consumers!

Small program live broadcast has rich interactive functions such as comment, thumb up, lian mai, pai yi pai, efficient marketing functions such as lottery, coupons, as well as member management, comment management, push stream live broadcast, data billboard and other perfect tools for merchants. By introducing small program live broadcast components, businesses can quickly have live broadcast capability with their own small programs and improve operating efficiency.

Although there are marketing functions of lottery and coupons, there is no red envelope function. If there is a red envelope function, the interaction with users will be increased and users will be more attracted to stay and watch the live broadcast. In fact, we can develop the red envelope function in the broadcast room. Of course, in order to realize this function, the small program should first open the live broadcast permission, which should satisfy the payment behavior of the small program within the past 90 days. If it cannot be opened because of this, contact me, you can open it quickly.

Second, the train of thought

Said the thinking of this function, the background do first a menu, record the red envelopes fields including the host name, anchor head, the slogan (have a thriving and prosperous), number of effective time, amount of red envelopes, red packets, surplus cash bonus amount, number of surplus cash bonus, creation time, the version number (optimistic locking), also have a grab a red envelope form, The fields include the red envelope ID, the ID of the user who got the red envelope, the name of the user who got the red envelope, the picture of the user who got the red envelope, the amount of the red envelope and the creation time. Then go to the background of small program live broadcast to record goods, commodity path field to fill in the small program red envelope page path to jump, need to join the red envelope ID parameter, such as this,

pages/redPacket/redPacket.html? redPacketId=123456





When the user clicks the product on the live broadcast page and enters the red envelope page, the front end can get the red envelope ID and send it to the background interface to find out the relevant information of the red envelope and conduct various operations, such as generating random amount, deducting the amount and number of the red envelope and so on. This requires the anchor to guide the user to get ready to grab the red envelope, and then the assistant of the broadcast room will display the red envelope products by putting the goods on the shelves.

The idea is simple, and the code is simple to implement, but we need to think about a couple of things,

1, grab red envelopes like seconds to kill goods, is to fight hand speed, to consider concurrent, can not appear oversold (here is super rob) phenomenon, otherwise the loss is the boss 💰, should find you chat. Here we use a simple optimistic lock to solve this problem.

2, If the optimistic lock update fails, it is also possible to directly return to the user to grab an empty red envelope or other friendly prompts, but if we add a retry mechanism, the experience will be better. Here we retry using annotations. By default, we retry 3 times.

Three, implementation,

Let’s implement the annotation retry function first, and look at the code below

*/ @Retention(RetentionPolicy.Runtime) @Target(elementType.method) public @Interface ApiRetry {/** * * @return */ int value() default 3; }
/** * Define a retryAspect class */ @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. ); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); Throw new ApiretryException (" There are more users currently, please try again later "); } else {log.info("===== is trying again, {} time ",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 = private Logger LoggerFactory.getLogger(getClass()); */ @ExceptionHandler(ApiretryException.class) public JsonModel HandLeapIretryException (ApiretryException) public JsonModel HandLeapIretryException (ApiretryException e){ JsonModel jsonModel = new JsonModel(); jsonModel.setStatus(e.getStatus()); jsonModel.setMsg(e.getMsg()); return jsonModel; }}

About the red envelope interface, there are three,

1, to the front judgment is the pop-up grab red envelope window or pop up other prompt window (you have grabbed the red envelope, hand is slow, red envelope has expired, hand is slow, red envelope sent over).

/** * @Override public JSONModel IntoRedPacket (String); /** * @Override public JSONModel IntoRedPacket (String) redPacketId, String userId) { ChatroomRedPacketEntity redPacket = baseMapper.selectById(redPacketId); If (RedPacket == null) {throw new JSONModelException (" redPacket with ID ["+ RedPacket +"] does not exist "); } ChatroomRedPacketRecordEntity hasRecord = chatroomRedPacketRecordService.getOne(Wrappers.<ChatroomRedPacketRecordEntity>lambdaQuery() .eq(ChatroomRedPacketRecordEntity::getRedPacketId, redPacketId) .eq(ChatroomRedPacketRecordEntity::getUserId, userId)); if (hasRecord ! = null) {return jsonModel.tofail (10001," You have stolen the red bag "); } 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," redPacket is over "); } if (RedPacket. GetCashNum ()+ RedPacket. GetCouponNum () <= 0) {return JSONModel.tofail (10003," "); } return jsonModel.toSuccess (200," popup ");} return jsonModel.toSuccess (200," popup "); } @apiImplicitParams ({@apiImplicitParam (name = "redPacEtid ", value = "red envelope ID ") @apiImplicitParams ({@apiImplicitParam (name =" redPacEtid ", value = "red envelope ID ", Required = true, paramType = "body", dataType = "String"), @apiimplicitParam (name = "userId", value = "userId", 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. This is the most important, is click and grab

/ * * * take a red envelope * @ 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 (" redPacket with ID ["+ RedPacket +"] does not exist "); } ChatroomRedPacketRecordEntity hasRecord = chatroomRedPacketRecordService.getOne(Wrappers.<ChatroomRedPacketRecordEntity>lambdaQuery() .eq(ChatroomRedPacketRecordEntity::getRedPacketId, redPacketId) .eq(ChatroomRedPacketRecordEntity::getUserId, userId)); if (hasRecord ! = null) {return jsonModel.tofail (10001," You have stolen the red bag "); } 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," redPacket is over "); } 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); / / update the red envelopes remaining number and the amount of residual 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 (" Update failed, start retry "); } 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); // add the amount of the red envelope to the user's account, and you can withdraw it.}}

3. The last one is the record list of grabbing red envelopes.

Public JsonModel RedPacketRecord (String RedPacketRecord) {public JsonModel RedPacketRecord (String RedPacketRecord) { ChatroomRedPacketEntity redPacket = baseMapper.selectById(redPacketId); If (RedPacket == null) {throw new JSONModelException (" redPacket with ID ["+ RedPacket +"] does not exist "); } 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); } @apiImplicitParams ({@apiImplicitParam (name = "redPacEtid ", value = "red envelope ID ") @apiImplicitParams ({@apiImplicitParam (name =" redPacEtid ", value = "red envelope ID ", Required = true, paramType = "body", dataType = "String"), @apiimplicitParam (name = "userId", value = "userId", required = true, paramType = "body", dataType = "String") }) @PostMapping("/redPacketRecord/{redPacketId}") public JsonModel redPacketRecord(@PathVariable("redPacketId")String redPacketId) { return chatroomRedPacketService.redPacketRecord(redPacketId) }

Four,

The idea is that way of thinking, as far as the implementation of the code logic, there are many kinds of, you can implement. Those who have other plans can comment in the section