后端源码传送门: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);}}

商城拍卖活动设计方案 瞬时并发高可用相关推荐

  1. 高并发高可用系统的常见应对策略 秒杀等-(阿里)

    对于一个需要处理高并发的系统而言,可以从多个层面去解决这个问题. 1.数据库系统:数据库系统可以采取集群策略以保证某台数据库服务器的宕机不会影响整个系统,并且通过负载均衡策略来降低每一台数据库服务器的 ...

  2. 构建高并发高可用的电商平台架构实践 转载

    2019独角兽企业重金招聘Python工程师标准>>> 构建高并发高可用的电商平台架构实践 转载 博客分类: java 架构 [-] 一 设计理念 空间换时间 多级缓存静态化 索引 ...

  3. 如何设计一个高并发高可用的秒杀或抢券系统

    一个大型网站应用一般都是从最初小规模网站甚至是单机应用发展而来的,为了让系统能够支持足够大的业务量,从前端到后端也采用了各种各样技术,前端静态资源压缩整合.使用CDN.分布式SOA架构.缓存.数据库加 ...

  4. php小程序秒抢高并发,PHP 如何设计一个高并发高可用的秒杀或抢券系统

    一个大型网站应用一般都是从最初小规模网站甚至是单机应用发展而来的,为了让系统能够支持足够大的业务量,从前端到后端也采用了各种各样技术,前端静态资源压缩整合.使用CDN.分布式SOA架构.缓存.数据库加 ...

  5. 高并发-高可用-高性能

    文章目录 参考 分布式事务 高可用 高并发,高可用,高性能 简介 高并发 高性能 高可用 方案设计 架构图 参考 网站视频:亿级流量网站架构核心技术:https://www.bilibili.com/ ...

  6. 高并发高可用的秒杀或抢券系统设计思考

    对于一个秒杀系统来说,瞬时的大量请求会对后台服务造成冲击,需要保证服务的可用性以及业务的正确性. 设计了一个高并发高可用的系统简要流程架构如下图: 1.将商品(或券)的信息等静态数据放到cdn节点,实 ...

  7. 【5. Redis的高并发高可用】

    Redis的高并发高可用 复制 ​ 在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求.Redis也是如此,它为我们提供了复制功能,实现了相同数据的多 ...

  8. 什么是高并发高可用一致性?| 现代网站架构发展 | C 语言实现布隆过滤器

    大话高并发高可用一致性|网站架构发展|网络编程缓存|C 语言实现布隆过滤器 Bloom Filter 编程练习 | GTest 教程 两个部分分为本文章,一部分是布隆过滤器的实现指引. 一个提供的前置 ...

  9. 微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-任亮-专题视频课程...

    微服务Springboot实战大揭秘/高并发/高可用/高负载/互联网技术-320人已学习 课程介绍         Java架构师系列课程是针对有志向架构师发展的广大学员而设置,不管你是工作一到三年, ...

最新文章

  1. 【剑指offer】10A--求裴波那切数列的第n项,C++实现
  2. 括号,逻辑与,逻辑或--运算符重载
  3. 从尾到头打印链表---剑指Offer
  4. CSS 学习-文本 段落
  5. ARM中的ldr指令与adr、ldr伪指令之间的区别
  6. 安装SQL2005只有配置工具或 错误码是29506 解决方案
  7. java if 并列_Java 并列if语句,一个判断失败后,后面的if就不执行了,为什么啊?...
  8. 项目Beta冲刺(团队)总结
  9. 关于WM下创建和删除GPRS接入点
  10. centos7中使用LVM管理磁盘和挂载磁盘
  11. day-60Django
  12. vision画流程图的软件_这个可以代替Visio的流程图绘制软件,你值得拥有,还有网页版的~...
  13. 项目实习(三)操作系统设计
  14. 手机计算机键盘技巧,【盲打计算器】看似简单,你不一定会的小技巧
  15. creator 生成bmfont字体文件
  16. C# AHP层次分析法:一致性校验
  17. 重积分 | 第二类曲面积分投影法正负判断
  18. NPOI设置Excel下拉选项
  19. css3魔方3乘3每层旋转_CSS3旋转魔方
  20. 台式计算机电功率一般多大,笔记本电脑耗电吗?功率一般多大

热门文章

  1. weblogic登录控制台加载缓慢
  2. 统计英文字母、空格、数字和其它字符的个数 C语言
  3. 【Hexo】静态博客设置文章加密访问
  4. 考研线代---pdf文档 百度网盘自取
  5. SAP ABAP 根据物料工厂查询工作中心(ARBPL)的两种方式
  6. 麒麟 vsftp 搭建
  7. 解决git问题:fatal: Need to specify how to reconcile divergent branches.
  8. 过河问题(贪心算法)
  9. GBASE 8s删除数据库
  10. 实战分享:从京东618数据井喷看大数据平台峰值处理制胜关键