京东秒杀之秒杀实现

1 登录判断

用户在未登录状态下可以查看商品列别以及秒杀商品详情,但不可以在未登录状态进行秒杀商品的操作,当用户点击开始秒杀时,进行登陆验证

<!DOCTYPE html>
<head>
    <title>商品详情</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" /><!-- bootstrap -->
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script> <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <script type="text/javascript" src="/layer/layer.js"></script><!-- layer -->
    <script type="text/javascript" src="/js/md5.min.js"></script><!-- md5.js -->
    <script type="text/javascript" src="/js/common.js"></script><!-- common.js -->
    <script type="text/javascript" src="/js/socket.js"></script><!-- common.js -->
</head>
<body>
<div class="panel panel-default">
    <div class="panel-heading">秒杀商品详情</div>
    <div class="panel-body">
        <div id="userTip" style="display: none">
            <span> 您还没有登录,请<a href="/login.html">登陆</a>后再操作<br/></span>
        </div>
        <span>没有收货地址的提示。。。</span>
    </div>
    <table class="table">
        <tr>
            <td>商品名称</td>
            <td colspan="3" id="goodName"></td>
        </tr>
        <tr>
            <td>商品图片</td>
            <td colspan="3"><img id="goodImg"  width="200" height="200" /></td>
        </tr>
        <tr>
            <td>秒杀开始时间</td>
            <td id="startDate"></td>
            <td id="seckillTip">
            </td>
            <td>
                <img id="verifyCodeImg" width="80" height="32"  onclick="initVerifyCodeImg()" style="display: none">
                <input id="verifyCode" style="display: none">
                <button class="btn btn-primary btn-block" type="button" id="buyButton" onclick="">立即秒杀</button>
            </td>
        </tr>
        <tr>
            <td>商品原价</td>
            <td colspan="3" id="goodPrice"></td>
        </tr>
        <tr>
            <td>秒杀价</td>
            <td colspan="3" id="seckillPrice"></td>
        </tr>
        <tr>
            <td>库存数量</td>
            <td colspan="3" id="stockCount"></td>
        </tr>
    </table>
</div>
<script type="text/javascript">


    var seckillId;





    $(function () {
        seckillId = getQueryString("seckillId");
        initGood();
        initUser();
    });




    function initGood(){
        $(function () {
            $.ajax({
                url: "http://localhost:9000/seckill/seckillGood/find?seckillId="+seckillId,
                type: "get",
                xhrFields: {withCredentials: true}, //启用cookie
                success:function (data) {
                    if(data.code==200){
                        //填充表格中的数据
                        renderGood(data.data);
                    }else{
                        layer.msg(data.msg)
                    }
                }
            });
        });

    }

    function renderGood(good) {
        $("#goodName").html(good.goodName);
        $("#goodImg").prop("src",good.goodImg);
        $("#startDate").html(good.startDate);
        $("#goodPrice").html(good.goodPrice);
        $("#stockCount").html(good.stockCount);
        $("#seckillPrice").html(good.seckillPrice);
        //调用时间
        renderDate(good.startDate,good.endDate);
    }

    //定义秒杀的三个阶段
    var timer;  //计时器
    //距离抢购开始还有多久
    var remainStartSeconds;
    //距离结束还有多久
    var remainEndSeconds;

    function renderDate(sDate,eDate) {
        var startTime = new Date(sDate);  // 2023-11-25 16:00
        var endTime = new Date(eDate);   // 2023-11-25 18:00
        var now = new Date();        // 2023-11-25 14:37
        remainStartSeconds=parseInt((startTime.getTime()-now.getTime())/1000);//秒
        remainEndSeconds=parseInt((endTime.getTime()-now.getTime())/1000);//秒
        timer=window.setInterval(showSeckillTip,1000);
    }


    function showSeckillTip() {
        remainStartSeconds--;
        remainEndSeconds--;

        if(remainStartSeconds>0){
            $("#seckillTip").html("距离本场秒杀开始还有"+remainStartSeconds+"秒");
            //禁用按钮
            $("#buyButton").prop("disabled",true);
        }else{
            if(remainEndSeconds>0){
                //秒杀中
                $("#seckillTip").html("秒杀进行中....");
                //禁用按钮
                $("#buyButton").prop("disabled",false);
            }else{
                $("#seckillTip").html("秒杀结束了");
                //禁用按钮
                $("#buyButton").prop("disabled",true);
                window.clearInterval(timer);//取消计时器
            }


        }
    }



    function initUser(){
        $(function () {
            $.ajax({
                url: "http://localhost:9000/member/token/getCurrent",
                type: "get",
                xhrFields: {withCredentials: true}, //启用cookie
                success:function (data) {
                    if(data.code==200){
                        //填充表格中的数据
                        renderUser(data.data);
                    }else{
                        layer.msg(data.msg)
                    }
                }
            });
        });

    }

    var user;

    function renderUser(u){
        if(u){
            user=u;
        }else{
            //没有数据
            $("#userTip").show();
        }
    }




</script>
</body>
</html>

1.1 编写前端登录判断

1.2 编写后端登录判断方法

1 编写sevice接口及其实现类方法


sevice接口

    public User getUserByToken(String token);

实现类

    /**
     * 根据token查询用户
     * @param token
     * @return
     */
    @Override
    public User getUserByToken(String token) {
        return myRedisTemplate.get(MemberServerKeyPrefix.USER_TOKEN, token, User.class);
    }

2 编写controller层方法

    /**
     * 获取当前用户
     * @param token 利用cookie中存储的token来判断用户
     * @return
     */
    @GetMapping("/getCurrent")
    public Result<User> getCurrent(@CookieValue(value = CookieUtil.TOKEN_COOKIE_NAME, required = false) String token){
        User user = userService.getUserByToken(token);

        return Result.success(user);
    }

3 测试

2 后端登陆判断方法的优化

2.1 调整项目结构

1 添加依赖

2 将member-server下MemberServerKeyPrefix类移动至member-api下

3 将member-server下CookieUtil类移动至member-api下

4 编写getCookie方法

    /**
     * 获取cookie
     * @param request
     * @param tokenCookieName
     * @return
     */
    public static String getCookie(HttpServletRequest request, String tokenCookieName) {

        Cookie[] cookies = request.getCookies();

        if (cookies != null && cookies.length > 0){
            for (Cookie cookie : cookies) {
                //找到cookie
                if (cookie.getName().equals(tokenCookieName)){
                    return cookie.getValue();
                }
            }
        }

        return null;
    }

2.2 编写自定义SpringMVC参数解析器

public class UserMethodArgumentResolver implements HandlerMethodArgumentResolver {


    @Autowired
    private MyRedisTemplate myRedisTemplate;


    /**
     * 判断参数类型是否为User
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType() == User.class &&
                methodParameter.getParameterAnnotation(RedisValue.class) != null;
    }

    /**
     * 自定义参数解析器
     * @param parameter
     * @param mavContainer
     * @param webRequest
     * @param binderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {

        //获取请求对象
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        //获取cookie
        String token = CookieUtil.getCookie(request, CookieUtil.TOKEN_COOKIE_NAME);

        if (StringUtils.isEmpty(token)){
            return null;
        }

        return myRedisTemplate.get(MemberServerKeyPrefix.USER_TOKEN, token, User.class);

    }

}

2.3 编写配置类

@Configuration
public class WebConfig implements WebMvcConfigurer {

    //注入参数解析器
    @Autowired
    private UserMethodArgumentResolver userMethodArgumentResolver;

    /**
     * 添加参数解析器
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userMethodArgumentResolver);
    }

    @Bean
    public UserMethodArgumentResolver userMethodArgumentResolver() {
        return new UserMethodArgumentResolver();
    }
}

2.4 编写自定义注解

@Target(ElementType.PARAMETER) //定义该注解应用于方法参数上
@Retention(RetentionPolicy.RUNTIME) //不仅保存在class中,JVM加载时也存在
public @interface RedisValue {
     
}

2.5 编写登陆判断方法

    @RequestMapping("/getCurrent")
    public Result<User> getCurrent(@RedisValue User user){

        return Result.success(user);
    }

3 秒杀实现

实现流程:

    1. 判断用户是否登录
    1. 判断场次是否正常
    1. 根据秒杀的场次查出当前场次对应的秒杀商品
    1. 秒杀时间的问题 处于秒杀中才能抢购
    1. 判断用户是否已经参与过当前场次的抢购
    1. 判断库存是否足够
    1. 秒杀的原子性 【 做三件事 】
    • 7.1 商品的库存 -1 t_seckill_goods(生成抢购订单)
    • 7.2 创建商品的订单表 t_order_info(参与抢购的商品表)
    • 7.3 创建秒杀订单 t_seckill_order(防止用户重复抢购的表)

3.1 编写前端秒杀动作

3.2 调整项目结构

1 添加依赖

2 添加参数解析器的配置类

将member-server下的参数解析器的配置类复制到seckill-server下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/205719.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

习题补充整理

目录 一、自己封装response 二、在响应头中放数据 HttpResponse redirect ​编辑 render JsonResponse 三、函数和方法区别 ----》绑定方法区别 四、上传图片和开启media访问 五、页面静态化(解决访问率高的问题) 一、自己封装response 第一步&#xff0c;在app01下…

使用vue脚手架创建vue项目

Vue是一个流行的前端框架&#xff0c;可以用简洁的语法和组件化的思想开发单页面应用。Vue脚手架是一个官方提供的命令行工具&#xff0c;它可以帮你快速搭建和配置vue项目的基本结构和依赖。 本文介绍如何使用vue脚手架创建一个vue2项目&#xff0c;并选择一些常用的功能和插件…

【JavaFX漏扫开发基础】JavaFx项目基础

文章目录 前言一、创建项目二、运行程序三、application的启动方式和生命周期四、生命周期前言 创建第一个javafx项目,用Idea编辑器来创建。 提示:以下是本篇文章正文内容,下面案例可供参考 一、创建项目 初创项目,第一个javafx的窗口。 依次点击,新建项目 点击Jaca…

字节的“游戏心脏”,真的停止跳动了吗?

字节跳动在游戏行业的宏大叙事&#xff0c;终究变成了一场游戏一场梦。 近日&#xff0c;朝夕光年首次公开回应此前的裁员传闻&#xff0c;明确表示会有业务方向和组织调整&#xff0c;游戏业务将迎来大规模收缩&#xff0c;未来会更加聚焦部分创新型游戏及相关技术的探索。 …

spring RedisTemplate RedisLockRegistry opsForXxx 基本使用总结以及介绍

一、基本介绍 RedisTemplate 为 spring 对 redis 操作的高度封装&#xff0c;基本已经满足所有使用场景。 若存在其他拓展使用我们可以自行封装工具类对基本操作进行组装。 RedisLockRegistry 对 redis 锁的一些封装 二、不同环境下依赖以及基本配置 2.1 spring-boot 下依赖…

MySQL之 InnoDB逻辑存储结构

InnoDB逻辑存储结构 InnoDB将所有数据都存放在表空间中&#xff0c;表空间又由段&#xff08;segment&#xff09;、区&#xff08;extent&#xff09;、页&#xff08;page&#xff09;组成。InnoDB存储引擎的逻辑存储结构大致如下图。下面我们就一个个来看看。 页&#xff08…

WPF实战项目十八(客户端):添加新增、查询、编辑功能

1、ToDoView.xmal添加引用&#xff0c;添加微软的行为类 xmlns:i"http://schemas.microsoft.com/xaml/behaviors" 2、给项目添加行为 <i:Interaction.Triggers><i:EventTrigger EventName"MouseLeftButtonUp"><i:InvokeCommandAction Com…

【漏洞复现】熊海cms 存在sql注入 附poc

漏洞描述 熊海CMS 是由熊海开发的一款可广泛应用于个人博客,个人网站,企业网站的一套网站综合管理系统。 其采用前后端整合设计思路,php,Apache,mysql,前端使用Bootstrap和少许jquery前端框架开发; 网站样式设计简洁大方,整体功能点并不多,但功能正好够用;拥有一个…

【计网 面向连接的传输TCP】 中科大笔记 (十 二)

目录 0 引言1 TCP 的特性1.1 拓展&#xff1a;全双工、单工、半双工通信 2 TCP报文段结构3 TCP如何实现RDT4 TCP 流量控制4.1 题外话&#xff1a;算法感悟 5 TCP连接3次握手、断开连接4次握手5.1 连接5.2 断开连接 6 拥塞控制6.1 拥塞控制原理6.2 TCP拥塞控制 &#x1f64b;‍♂…

Docker容器网络模式

1.none网络 1&#xff09;使用默认网络模式创建一个BusyBox容器&#xff0c;用于对比none网络模式&#xff1b; 测试网络&#xff0c;可以正常连接外网。 2&#xff09;再创建一个none网络模式的BusyBox容器&#xff1b; 测试网络连接&#xff0c;无法连接外网。 总结&#x…

六:Day01_Spring Boot01

一、Spring Boot简介 1. 概念简介 Spring Boot是Spring公司的一个顶级项目&#xff0c;和Spring Framework是一个级别的。 Spring Boot实际上是利用Spring Framework 4 自动配置特性完成。编写项目时不需要编写xml文件。 2. 启动器介绍 Spring Boot的启动器实际上就是一个依赖…

TSINGSEE青犀AI视频智能分析系统的视频接入能力解析

视频智能分析技术是一种先进的人工智能技术&#xff0c;它能够对视频内容进行自动化的分析和理解。这种技术的主要特点包括实时性、自动化、准确性、可解释性等。 1&#xff09;实时性。视频智能分析技术能够在短时间内对大量的视频数据进行快速处理和分析&#xff0c;从而提供…

【网络BSP开发经验】网络流量应用识别技术

文章目录 网络流量应用识别技术背景应用识别基本原理应用识别主流技术方向特征识别技术单报文解析流特征解析 关联识别技术DNS关联识别 行为识别技术 应用识别框架介绍应用特征提取经验tcpdump 抓包方式默认启动监视指定网络接口的数据包监视指定主机的数据包 禁用特征提取加速…

Small Data Transmission (二)具体过程

这篇是SDT相关的具体过程,包括RRC层初始化SDT 的条件,MAC 层初始化SDT过程的判断,CG-SDT的具体过程,TA Validation for CG-SDT, CG-SDT TA 验证的路径损耗参考推导,SDT Uplink Time Alignment,RRC inactive中的CG based PUSCH传输和RA based PUSCH传输,依次看下。 初始…

简历上的工作经历怎么写

通过了简历筛选&#xff0c;后续的面试官会仔细阅读你的简历内容。他们在找什么呢&#xff1f;他们希望搞清楚你在某一段经历中具体干了什么&#xff0c;并且判断你的能力具体达到了什么水平。 简历在线制作下载&#xff1a;百度幻主简历 面试官喜欢具体的经历 越具体&#x…

计算机网络(二)

&#xff08;八&#xff09;客户端软件设计的细节 A、解析协议号 客户端可能会需要通过协议名指定协议&#xff0c;但是Socket接口是用协议号指定的&#xff0c;这时候我们就需要使用getprotobyname()函数实现协议名到协议号的转换&#xff0c;该函数会返回一个指向protoent的…

第三方实验室LIMS管理系统源码,asp.net LIMS源码

LIMS实验室信息管理系统源码 LIMS系统的功能根据实验室的规模和任务而有所不同&#xff0c;其系统主要功能包括:系统维护、基础数据编码管理&#xff0c;样品管理、数据管理、报告管理、报表打印、实验材料管理、设备管理等。它可以取代传统的手工管理模式而给检测实验室带来巨…

基于SSM框架的餐馆点餐系统的设计

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

什么是Anaconda?作用是?使用python必须要安装嘛?

一、什么是Anaconda以及其作用&#xff1f; 通俗来讲&#xff0c;Anaconda算是一个环境容器&#xff0c;也可以叫环境管理器。 作用&#xff1a;可以在Anaconda容器中为python项目创建不同的环境。在各个不同环境中可以安装不同版本的包并且各个环境互不影响。可以在使用不同项…

唯品会年度特卖大会㊙内购清单㊙

唯品会年度特卖大会㊙内购清单㊙ 内部员工亲友专享&#xff0c;实实在在省钱&#xff0c;❌抢完不补! 今晚8点开抢&#xff0c;提前收藏>> https://t.vip.com/Im3KlTnDSJ8 2023年唯品会年度特卖大会热门会场推荐 1.唯品会年度特卖大会 限时加码!瓜分百万津贴!抢海量…