美文网首页
高并发秒杀系统API之Web层

高并发秒杀系统API之Web层

作者: 意浅离殇 | 来源:发表于2017-10-15 20:52 被阅读0次

    实现完dao 和service 层后,接下来自然就是web层了。首先创建一个通用的ajax返回工具类。返回json 数据 具体代码如下。

    //封装json结果
    public class SeckillResult<T> {
    
        private boolean success;
    
        private T data;
    
        private String error;
    
        public SeckillResult(boolean success, String error) {
            this.success = success;
            this.error = error;
        }
    
        public SeckillResult(boolean success, T data) {
            this.success = success;
            this.data = data;
        }
    
        public boolean isSuccess() {
            return success;
        }
    
        public void setSuccess(boolean success) {
            this.success = success;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        public String getError() {
            return error;
        }
    
        public void setError(String error) {
            this.error = error;
        }
    
        @Override
        public String toString() {
            return "SeckillResult [success=" + success + ", data=" + data + ", error=" + error + "]";
        }
    
    }
    

    下面分析一下流程。
    首先获取标准时间,以服务器的时间为准,然后比对开始时间和结束时间如果状态为结束,则秒杀结束,如果正在秒杀中暴露秒杀地址执行秒杀。如果没有开始则等待开始时间。具体流程图如下


    这里写图片描述

    由上述流程。则在web层设计如下api完成功能。


    这里写图片描述
    下面用具体的代码描述上述的url
    
    @Controller 
    @RequestMapping("/seckill") 
    public class SeckillController {
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private SeckillService seckillService;
    
        @RequestMapping(value = "/list", method = RequestMethod.GET)
        public String list(Model model) {
            // 获取列表页
            List<Seckill> list = seckillService.getSeckillList();
            model.addAttribute("list", list);
            return "list";
        }
    
        @RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET)
        public String detail(@PathVariable("seckillId") Long seckillId, Model model) {
            if (seckillId == null) {
                return "redirect:/seckill/list";
            }
            Seckill seckill = seckillService.getSeckillById(seckillId);
            if (seckill == null) {
                return "forward:/seckill/list";
            }
            model.addAttribute("seckill", seckill);
            return "detail";
        }
    
        // ajax json
        @RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.POST, produces = {
                "application/json; charset=utf-8" })
        @ResponseBody
        public SeckillResult<Exposer> exposer(@PathVariable("seckillId") Long seckillId) {
            SeckillResult<Exposer> result;
            try {
                Exposer exposer = seckillService.exportSeckillUrl(seckillId);
                result = new SeckillResult<Exposer>(true, exposer);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                result = new SeckillResult<Exposer>(false, e.getMessage());
            }
            return result;
        }
    
        @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST, produces = {
                "application/json; charset=utf-8" })
        @ResponseBody
        public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId,
                @PathVariable("md5") String md5, @CookieValue(value = "killPhone", required = false) Long phone) {
            // springmvc valid
            if (phone == null) {
                return new SeckillResult(false, "未注册");
            }
            try {
                // 存储过程调用
                SeckillExecution execution = seckillService.executeSeckill(seckillId, phone, md5);
                return new SeckillResult<SeckillExecution>(true, execution);
            } catch (RepeatKillException e) {
                SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL);
                return new SeckillResult<SeckillExecution>(true, execution);
            } catch (SeckillCloseException e) {
                SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.END);
                return new SeckillResult<SeckillExecution>(true, execution);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);
                return new SeckillResult<SeckillExecution>(true, execution);
            }
        }
    
        @RequestMapping(value = "/time/now", method = RequestMethod.GET)
        @ResponseBody
        public SeckillResult<Long> time() {
            Date now = new Date();
            return new SeckillResult<Long>(true, now.getTime());
        }
    
    }
    

    接下来需要两个页面一个列表页面展示商品列表,一个详情页面做秒杀操作。
    列表页面

    <!DOCTYPE HTML>
    <html xmlns:th="http://www.thymeleaf.org" lang="zh">
    <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <link rel="stylesheet"
        href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
        
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
          <script src="//cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
          <script src="//cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
        <![endif]-->
    
    </head>
    <title>秒杀列表页</title>
    
    <body>
        <!-- 页面显示部分 -->
        <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading text-center">
                    <h2>秒杀列表</h2>
                </div>
                <div class="panel-body">
                    <table class="table table-hover">
                        <thead>
                            <tr>
                                <th>名称</th>
                                <th>库存</th>
                                <th>开始时间</th>
                                <th>结束时间</th>
                                <th>创建时间</th>
                                <th>详情页</th>
                            </tr>
                        </thead>
                        <tbody>
                                <tr th:each="sk : ${list}">
                                    <td th:text="${sk.name}"></td>
                                    <td th:text="${sk.number}"></td>
                                    <td th:text="${#dates.format(sk.startTime,'yyyy-MM-dd')}">                              
                                    </td>
                                    <td th:text="${#dates.format(sk.endTime,'yyyy-MM-dd')}">                                
                                    </td>
                                    <td th:text="${#dates.format(sk.createTime,'yyyy-MM-dd')}">                             
                                    </td>
                                    <td >
                                        <a class="btn btn-info" th:href="'/seckill/'+${sk.seckillId}+'/detail/'"  target="_blank">link</a>
                                    </td>
                                </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    
        <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
        <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
        <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
        <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    </body>
    </html>
    

    页面效果如下。


    这里写图片描述

    设置一个秒杀的详情页面

    <!DOCTYPE HTML>
    <html xmlns:th="http://www.thymeleaf.org" lang="zh">
    <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <link rel="stylesheet"
        href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
        
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
          <script src="//cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
          <script src="//cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
        <![endif]-->
    <title>秒杀详情页</title>
    </head>
    <body>
    <input type="hidden"  id="basePath" th:value="@{/}"/>
    
        <div class="container">
            <div class="panel panel-default text-center">
                <div class="panel-heading">
                    <h1 th:text="${seckill.name}"></h1>
                </div>
                <div class="panel-body">
                    <h2 class="text-danger">
                        <!-- 显示time图标 -->
                        <span class="glyphicon glyphicon-time"></span>
                        <!-- 展示倒计时 -->
                        <span class="glyphicon" id="seckillBox"></span>
                    </h2>
                </div>
            </div>
        </div>
    
        <!-- 登录弹出层,输入电话 -->
        <div id="killPhoneModal" class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h3 class="modal-title text-center">
                            <span class="glyphicon glyphicon-phone"></span>秒杀电话:
                        </h3>
                    </div>
                    <div class="modal-body">
                        <div class="row">
                            <div class="col-xs-8 col-xs-offset-2">
                                <input type="text" name="killphone" id="killphoneKey"
                                    placeholder="填手机号^O^" class="form-control" />
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <span id="killphoneMessage" class="glyphicon"></span>
                        <button type="button" id="killPhoneBtn" class="btn btn-success">
                            <span class="glyphicon glyphicon-phone"></span> Submit
                        </button>
                    </div>
                </div>
            </div>
        </div>
    
        <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
        <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
        <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
        <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <!-- jQuery cookie操作插件 -->
        <script src="//cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
        <!-- jQery countDonw倒计时插件  -->
        <script src="//cdn.bootcss.com/jquery.countdown/2.1.0/jquery.countdown.min.js"></script>
        <!-- 开始编写交互逻辑 -->
        <script src="/js/seckill.js"  type="text/javascript"></script>
        <script type="text/javascript">
            $(function(){
                //使用EL表达式传入参数
                seckill.detail.init({
                    seckillId :[[${seckill.seckillId}]] ,
                    startTime : [[${seckill.startTime.time}]],//毫秒
                    endTime : [[${seckill.endTime.time}]]
                });
            });
        </script>
    </body>
    </html>
    

    效果如下


    这里写图片描述

    下面实现js 与后台进行交互完成秒杀。
    首先初始化传入相依的参数判断当前的状态

    seckill.detail.init({
                    seckillId :[[${seckill.seckillId}]] ,
                    startTime : [[${seckill.startTime.time}]],//毫秒
                    endTime : [[${seckill.endTime.time}]]
                });
    

    全局定义三个url的地址

        URL : {
            basePath : function() {
                   var curWwwPath=window.document.location.href;
                  var pathName=window.document.location.pathname;
                  var pos=curWwwPath.indexOf(pathName);
                  var localhostPaht=curWwwPath.substring(0,pos);
                return localhostPaht;
            },
            now : function() {
                return seckill.URL.basePath() + '/seckill/time/now';
            },
            exposer : function(seckillId) {
                return seckill.URL.basePath() + '/seckill/' + seckillId + '/exposer';
            },
            execution : function(seckillId, md5) {
                return seckill.URL.basePath() + '/seckill/' + seckillId + '/' + md5 + '/execution';
            }
        },
    

    通过当前时间和秒杀时间判断是否开启秒杀。

    $.get(seckill.URL.now(), {}, function(result) {
                    if (result && result['success']) {
                        var nowTime = result['data'];
                        // 时间判断,计时交互
                        seckill.countdown(seckillId, nowTime, startTime, endTime);
                    } else {
                        console.log(result['reult:'] + result);
                    }
                });
                countdown : function(seckillId, nowTime, startTime, endTime) {
            // 时间判断
            var seckillBox = $('#seckillBox');
            if (nowTime > endTime) {
                // 秒杀结束
                seckillBox.html('秒杀结束!');
            } else if (nowTime < startTime) {
                // 秒杀未开始,计时事件绑定
                var killTime = new Date(startTime + 1000);
                seckillBox.countdown(killTime, function(event) {
                    // 时间格式
                    var format = event.strftime('秒杀倒计时:%D天 %H时 %M分 %S秒');
                    seckillBox.html(format);
                    // 时间完成后回调事件
                }).on('finish.countdown', function() {
                    // 获取秒杀地址,控制显示逻辑,执行秒杀
                    seckill.handleSeckill(seckillId, seckillBox);
                });
            } else {
                // 秒杀开始
                seckill.handleSeckill(seckillId ,seckillBox);
            }
        },
    

    当计时结束时获取秒杀md5暴露秒杀接口,开放秒杀按钮绑定一次的点击时间 ,当按钮被电击后按钮失效同时发送秒杀请求返回秒杀结果。如图

    这里写图片描述
    文章地址:http://www.haha174.top/article/details/255548
    源码地址:https://github.com/haha174/seckill.git
    教程地址 :http://www.imooc.com/learn/11737

    相关文章

      网友评论

          本文标题:高并发秒杀系统API之Web层

          本文链接:https://www.haomeiwen.com/subject/dbthuxtx.html