本文主要是介绍商城拍卖活动设计方案 瞬时并发高可用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
后端源码传送门:https://download.csdn.net/download/sun5769675/87323033
最近参加了公司内部的一个24小时编程比赛,组了个四人的小团队,设计了一个拍卖的功能,功能需求如下:
1. 登陆
2. 支付保证金
3. 商品信息展示阶段
4. 拍卖阶段叫价(最后一秒有人出价会延时5分钟)
5. 尾款支付
针对功能需求我们计划创建三个项目,一个后台拍卖活动数据维护,一个小程序做C端,一个api项目对前面两个项目做接口层的支持,业务流程走向如下:
接口层部分做了如下8个接口,当然还有8个数据维护的接口这里就不列举了,就是针对表数据的一个增删改查,后面看DB设计即可知晓,流程没有画的特别详细但是主要流程都写了
下面看下DB设计一共5张表
下面看下我们后端维护数据的页面设计:
这边设计的后台数据维护功能是一个拍卖活动下,可以配置多个单位级的拍卖商品,是一对多的关系。
然后是看下我们的小程序页面其实很简单的,就登陆成功后到列表页开始倒计时,然后点击详情,倒计时结束,进入拍卖中的倒计时,需要交保证金,交完保证金,去竞价即可,活动时间结束,谁拍到了就去付尾款。
竞拍部分代码预览:
/*** * 竞拍,单次加价竞拍或一口价竞拍<br>* 1、校验单次加价,要大于默认加价<br>* 2、currentPrice与redis中价格比较<br>* 3、判断锁是否存在,存在返回,不存在加锁<br>* 4、加锁失败返回<br>* 5、currentPrice与redis中价格比较,小于解锁后返回<br>* 6、执行拍卖<br>* 7、有异常解锁<br>* * @param auctionRequestParamCommand * @return */
@Overridepublic ResultCommand singleOrOnceAuctionProcess(AuctionRequestParamCommand auctionRequestParamCommand) {String code = auctionRequestParamCommand.getCode();Integer memberId = auctionRequestParamCommand.getMemberId();Integer skuId = auctionRequestParamCommand.getSkuId();BigDecimal addPrice = auctionRequestParamCommand.getAddPrice();BigDecimal currentPrice = auctionRequestParamCommand.getCurrentPrice();//查询定金记录AuctionRequestParamCommand depositQuery = new AuctionRequestParamCommand();depositQuery.setCode(code);depositQuery.setMemberId(memberId);depositQuery.setSkuId(skuId);ResultCommand depositLog = auctionActivityDepositManager.findMemberDeposit(depositQuery);if (null == depositLog || depositLog.getData() ==null) {LOGGER.info("该用户没有交拍卖定金:memberId:{},活动code{},拍卖商品ID{}", memberId, code, skuId);return new ResultCommand(ErrorCode.ERROR_CODE_FIND_DEPOSIT_ERROR);}Date now = new Date();// 当前拍卖商品commandStoAuctionProductCommand auctionProductCommand = null;// 当前拍卖价格String redisPrice = null;// 参数校验if(StringUtils.isEmpty(code) || memberId == null || skuId == null || currentPrice == null) {LOGGER.info("竞拍参数校验为空:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_PARAM_EMPTY);}// 获取活动信息ResultCommand resultCommand = auctionActivityDetail(code);if (resultCommand == null|| !ErrorCode.ERROR_CODE_OK.getCode().equals(resultCommand.getCode())|| null == resultCommand.getData()) {LOGGER.info("竞拍获取活动信息为空!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_DATA_EMPTY);}AuctionActivityCommand auctionActivityCommand = (AuctionActivityCommand) resultCommand.getData();// 判断有效期// 判断竞拍商品的有效时间List<StoAuctionProductCommand> auctionProducts = auctionActivityCommand.getAuctionProducts();for (StoAuctionProductCommand stoAuctionProductCommand : auctionProducts) {if(!skuId.equals(stoAuctionProductCommand.getId())) {continue;}// 商品的有效期是否在有效范围内if (stoAuctionProductCommand.getStartTime() != null && stoAuctionProductCommand.getEndTime() != null&& now.after(stoAuctionProductCommand.getStartTime()) && now.before(stoAuctionProductCommand.getEndTime())) {// 校验通过auctionProductCommand = stoAuctionProductCommand;break;}LOGGER.info("竞拍活动商品不在有效时间范围内!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_OVER_EXPIRE);}// 当前竞拍价格缓存key:plugin_auction_活动编码_sku编码_拍卖商品表idString redisPriceKey = CacheConstants.PLUGIN_AUCTION_ACCOUNT_PRICE_KEY.concat(code).concat(JOIN_SYMBOL).concat(auctionProductCommand.getSkuCode().concat(JOIN_SYMBOL).concat(String.valueOf(auctionProductCommand.getId())));// 单次叫价拍卖用户加的价不能比默认的小if(addPrice != null && addPrice.compareTo(auctionProductCommand.getSinglePrice()) == -1) {return new ResultCommand(ErrorCode.ERROR_CODE_ADDPIRCE_LESS);}if(addPrice == null){// 自定义加价为空,使用默认单次加价addPrice = auctionProductCommand.getSinglePrice();}// 这个try/catch块中的内容是 在加锁成功之前的一些校验,这个通过说明加锁成功了try {redisPrice = redisUtils.getString(redisPriceKey);/*** 当前竞拍价格的设置* @see CacheConstant.PLUGIN_AUCTION_ACCOUNT_PRICE_KEY*/if(StringUtils.isEmpty(redisPrice)) {redisUtils.setString(redisPriceKey,String.valueOf(auctionProductCommand.getBasePrice()));redisPrice = String.valueOf(auctionProductCommand.getBasePrice());}// 比较用户看到的竞拍价与缓存中的价格if(currentPrice.compareTo(new BigDecimal(redisPrice)) == -1) {// 价格比缓存中的小return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}// 为当前竞拍设置锁if not exits,锁期间其他进入的竞拍会被拦截// 锁设置5秒Long setNXResult = redisUtils.setnx(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY,CacheConstants.LOCK_FOR_CURRENT_PRICE_VALUE,5,TimeUnit.SECONDS);if(!Status.REDIS_SETNX_SUCCESS.getCode().equals(Integer.valueOf(String.valueOf(setNXResult)))) {// 设置锁失败LOGGER.info("竞拍设置锁失败!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}} catch (Exception e) {LOGGER.error("在加锁成功之前的校验出错", e);return new ResultCommand(ErrorCode.ERROR_CODE_SYSTEM_ERROR);}StoAuctionRecord auctionRecord = new StoAuctionRecord();try {// 锁设置成功redisPrice = redisUtils.getString(redisPriceKey);// 再次比较用户看到的竞拍价与缓存中的价格if(currentPrice.compareTo(new BigDecimal(redisPrice)) == -1) {// 价格比缓存中的小,解锁redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}// 这个是会员竞拍到的价格BigDecimal auctionPrice = null;// 下面开始允许会员竞拍 1、保存竞拍记录;2、刷缓存// 单次叫价的竞拍价为 当前竞拍价加上addPriceauctionPrice = new BigDecimal(redisPrice).add(addPrice);auctionRecord.setAuctionActivityId(auctionActivityCommand.getId());auctionRecord.setAuctionPrice(auctionPrice);auctionRecord.setAuctionProductId(skuId);auctionRecord.setCreateBy(String.valueOf(memberId));auctionRecord.setCreateTime(new Date());auctionRecord.setLifecycle(Status.ACTIVE.getCode());auctionRecord.setMemberId(memberId);auctionRecord.setModifyBy(String.valueOf(memberId));auctionRecord.setModifyTime(new Date());// 保存拍卖纪录auctionRecord.setId(baseMysqlCRUDManager.save(auctionRecord));// 刷新当前竞拍价格缓存redisUtils.setString(redisPriceKey, String.valueOf(auctionPrice));// 拍卖顺延auctionExtendDate(code,skuId);// 竞拍成功解锁redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_OK, auctionRecord);} catch (Exception e) {// 这里抓取到的异常要手动回滚、执行解锁操作if(auctionRecord != null && auctionRecord.getId() != null) {LOGGER.error("锁期间竞拍异常!回滚拍卖纪录");if(auctionRecord.getId() != null){baseMysqlCRUDManager.delete(auctionRecord);}}String newRedisPrice = redisUtils.getString(redisPriceKey);if(!redisPrice.equals(newRedisPrice)) {LOGGER.error("锁期间竞拍异常!商品当前拍卖价格缓存回滚");redisUtils.setString(redisPriceKey, redisPrice);}LOGGER.error("锁期间竞拍异常!解锁");redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_SYSTEM_ERROR);}}
这篇关于商城拍卖活动设计方案 瞬时并发高可用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!