京东秒杀之秒杀实现

1 登录判断

用户在未登录状态下可以查看商品列别以及秒杀商品详情,但不可以在未登录状态进行秒杀商品的操作,当用户点击开始秒杀时,进行登陆验证
京东秒杀之秒杀实现_第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" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js">script>
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js">script> 
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js">script>
    <script type="text/javascript" src="/layer/layer.js">script>
    <script type="text/javascript" src="/js/md5.min.js">script>
    <script type="text/javascript" src="/js/common.js">script>
    <script type="text/javascript" src="/js/socket.js">script>
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 编写前端登录判断

京东秒杀之秒杀实现_第2张图片

1.2 编写后端登录判断方法

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

京东秒杀之秒杀实现_第3张图片
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层方法

京东秒杀之秒杀实现_第4张图片

    /**
     * 获取当前用户
     * @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 测试

京东秒杀之秒杀实现_第5张图片

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

2.1 调整项目结构

1 添加依赖

京东秒杀之秒杀实现_第6张图片

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

京东秒杀之秒杀实现_第7张图片

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

京东秒杀之秒杀实现_第8张图片

4 编写getCookie方法

京东秒杀之秒杀实现_第9张图片

    /**
     * 获取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参数解析器

京东秒杀之秒杀实现_第10张图片

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 编写配置类

京东秒杀之秒杀实现_第11张图片

@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 编写自定义注解

京东秒杀之秒杀实现_第12张图片

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

2.5 编写登陆判断方法

京东秒杀之秒杀实现_第13张图片

    @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 编写前端秒杀动作

京东秒杀之秒杀实现_第14张图片

3.2 调整项目结构

1 添加依赖

京东秒杀之秒杀实现_第15张图片

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

将member-server下的参数解析器的配置类复制到seckill-server下
京东秒杀之秒杀实现_第16张图片

你可能感兴趣的:(微服务)