秒杀基本功能开发(不考虑高并发情况)

文章目录

    • 1.显示秒杀状态
        • 1.controller
          • 修改GoodsController.java的toDetail方法,响应秒杀状态和秒杀剩余时间
        • 2.前端
          • 1.goodsDetail.html 图片下面添加一行秒杀开始时间
          • 2.goodsDetail.html 添加计时器js代码
        • 3.测试
          • 1.秒杀进行中
          • 2.修改db的秒杀开始时间为明天
          • 3.出现秒杀倒计时
          • 4.修改db的秒杀结束时间比目前要早
          • 5.秒杀已结束
    • 2.秒杀按钮
        • 1.前端
          • 1.goodsDetail.html 添加抢购按钮
          • 2.goodsDetail.html 根据秒杀状态,控制按钮状态
        • 2.测试
          • 1.秒杀已结束,按钮不可用
          • 2.秒杀进行中,按钮可用
          • 3.秒杀未开始,按钮不可用
    • 3.秒杀基本功能(不考虑高并发)
        • 1.数据库表设计
          • 1.普通订单表
          • 2.秒杀订单表
        • 2.MyBatis-Plus生成基础代码(以t_order表为例)
          • 1.首先 ctrl + shift + c 复制基础包名(一定要是带点的)
          • 2.右键表名选择 MybatisX-Generator
          • 3.选择模块和基础包以及实体类名字
          • 4.进行配置
          • 5.点击生成,检查代码
            • 1.整个目录概览
            • 2.把实体类移动到pojo并检查
            • 3.检查 OrderMapper.java 发现没有加@Mapper注解
            • 4.查看启动类有@MapperScan所以不用加@Mapper了
            • 5.检查OrderMapper.xml
            • 6.检查application.yml是否自动扫描了Mapper.xml
            • 7.检查OrderService.java
            • 8.检查OrderServiceImpl.java
          • 6.使用MyBatis-Plus生成基础代码的小结
          • 7.以同样的方式生成t_seckill_order
        • 3.Service层
          • 1.OrderService.java 新增秒杀方法,返回订单
          • 2.OrderServiceImpl.java
          • 3.SeckillOrderService.java 根据普通订单和商品id插入秒杀订单
          • 4.SeckillOrderServiceImpl.java
        • 4.Controller层
          • 1.SeckillController.java 完成基础版本的秒杀,简单考虑库存和复购问题
          • 2.SeckillOrderService.java 新增方法,根据用户id和商品id查找记录
          • 3.SeckillOrderServiceImpl.java
          • 4.SeckillOrderMapper.java
          • 5.SeckillOrderMapper.xml
        • 5.前端
          • 1.goodsDetail.html 修改点击抢购按钮的请求(区分多环境)
          • 2.引入orderDetail.html
          • 3.引入secKillFail.html
        • 6.测试
          • 1.正常秒杀
            • 1.初始秒杀商品表(库存为10)
            • 2.秒杀1号商品
            • 3.秒杀成功
            • 4.秒杀商品库存减1
            • 5.普通订单新增一条记录
            • 6.秒杀订单新增一条记录
          • 2.当前用户再次购买
            • 成功跳转到限购页面
          • 3.模拟库存不足的情况
            • 1.将1号商品的库存修改为0
            • 2.切换一个浏览器再次秒杀
            • 3.成功跳转到库存不足的页面

1.显示秒杀状态

1.controller
修改GoodsController.java的toDetail方法,响应秒杀状态和秒杀剩余时间
    // 进入到商品详情页
    @RequestMapping("/toDetail/{goodsId}")
    public String toDetail(Model model, User user, @PathVariable Long goodsId) {
        // 判断是否有用户信息
        if (null == user) {
            return "login";
        }

        // 查询商品详情
        GoodsVo goodsVoByGoodsId = goodsService.findGoodsVoByGoodsId(goodsId);
        model.addAttribute("goods", goodsVoByGoodsId);

        // secKillStatus:秒杀状态 0:未开始 1:进行中 2:已结束
        // remainSeconds:秒杀剩余时间 >0:未开始 0:进行中 -1:已结束
        // 获取该商品的秒杀开始时间和结束时间
        long startAt = goodsVoByGoodsId.getStartDate().getTime();
        long endAt = goodsVoByGoodsId.getEndDate().getTime();
        long now = System.currentTimeMillis();

        // 根据当前时间与秒杀开始时间和结束时间的比较,判断秒杀状态
        int secKillStatus = 0;
        int remainSeconds = 0;
        if (now < startAt) {
            // 秒杀未开始
            secKillStatus = 0;
            remainSeconds = (int) ((startAt - now) / 1000);
        } else if (now > endAt) {
            // 秒杀已结束
            secKillStatus = 2;
            remainSeconds = -1;
        } else {
            // 秒杀进行中
            secKillStatus = 1;
            remainSeconds = 0;
        }
        // 将秒杀状态和剩余时间存入model中,返回到前端
        model.addAttribute("secKillStatus", secKillStatus);
        model.addAttribute("remainSeconds", remainSeconds);
        // 将用户信息存入model中,返回到前端
        model.addAttribute("user", user);
        return "goodsDetail";
    }
2.前端
1.goodsDetail.html 图片下面添加一行秒杀开始时间
            <tr>
                <td>秒杀开始时间</td>
                <td id="startTime" th:text="${#dates.format(goods.startDate,'yyyy-MM-dd HH:mm:ss')}"></td>
                <td id="seckillTip">
                    <input type="hidden" id="remainSeconds"
                           th:value="${remainSeconds}"/>
                    <span th:if="${secKillStatus eq 0}">秒杀倒计时:<span id="countDown"
                                                                         th:text="${remainSeconds}"> </span></span>
                    <span th:if="${secKillStatus eq 1}">秒杀进行中</span>
                    <span th:if="${secKillStatus eq 2}">秒杀已结束</span>
                </td>
            </tr>
2.goodsDetail.html 添加计时器js代码
<script>
    $(function () {
        countDown();
    });

    function countDown() {
        var remainSeconds = $("#remainSeconds").val();
        var timeout;
//秒杀还未开始
        if (remainSeconds > 0) {
            timeout = setTimeout(function () {
                $("#countDown").text(remainSeconds - 1);
                $("#remainSeconds").val(remainSeconds - 1);
                countDown();
            }, 1000);
        } else if (remainSeconds == 0) {//秒杀进行中
            if (timeout) {//清空计时器
                clearTimeout(timeout);
            }
            $("#seckillTip").html("秒杀进行中");
        } else {
            $("#seckillTip").html("秒杀已结束");
        }
    }
</script>
3.测试
1.秒杀进行中

image-20240508143812867

2.修改db的秒杀开始时间为明天

image-20240508143929455

3.出现秒杀倒计时

image-20240508143950826

image-20240508144003627

4.修改db的秒杀结束时间比目前要早

image-20240508144158474

5.秒杀已结束

image-20240508144206709

2.秒杀按钮

1.前端
1.goodsDetail.html 添加抢购按钮
                <td>
                    <form id="secKillForm" method="post" action="/seckill/doSeckill">
                        <input type="hidden" id="goodsId" name="goodsId" th:value="${goods.id}">
                        <button class="btn btn-primary btn-block" type="submit" id="buyButton"> 抢 购</button>
                    </form>
                </td>

image-20240508144706778

2.goodsDetail.html 根据秒杀状态,控制按钮状态

image-20240508145140688

2.测试
1.秒杀已结束,按钮不可用

image-20240508145400768

2.秒杀进行中,按钮可用

image-20240508145447048

3.秒杀未开始,按钮不可用

image-20240508145514987

3.秒杀基本功能(不考虑高并发)

1.数据库表设计
1.普通订单表
use seckill;
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`
(
    `id`               BIGINT(20)     NOT NULL AUTO_INCREMENT,
    `user_id`          BIGINT(20)     NOT NULL DEFAULT 0,
    `goods_id`         BIGINT(20)     NOT NULL DEFAULT 0,
    `delivery_addr_id` BIGINT(20)     NOT NULL DEFAULT 0,
    `goods_name`       VARCHAR(16)    NOT NULL DEFAULT '',
    `goods_count`      INT(11)        NOT NULL DEFAULT '0',
    `goods_price`      DECIMAL(10, 2) NOT NULL DEFAULT '0.00',
    `order_channel`    TINYINT(4)     NOT NULL DEFAULT '0' COMMENT '订单渠道 1pc,2Android,
3ios',
    `status`           TINYINT(4)     NOT NULL DEFAULT '0' COMMENT '订单状态:0 新建未支付 1 已支付
2 已发货 3 已收货 4 已退款 5 已完成',
    `create_date`      DATETIME                DEFAULT NULL,
    `pay_date`         DATETIME                DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = INNODB
  AUTO_INCREMENT = 600
  DEFAULT CHARSET = utf8mb4;
2.秒杀订单表
use seckill;
DROP TABLE IF EXISTS `t_seckill_order`;
CREATE TABLE `t_seckill_order`
(
    `id`       BIGINT(20) NOT NULL AUTO_INCREMENT,
    `user_id`  BIGINT(20) NOT NULL DEFAULT 0,
    `order_id` BIGINT(20) NOT NULL DEFAULT 0,
    `goods_id` BIGINT(20) NOT NULL DEFAULT 0,
    PRIMARY KEY (`id`),
    UNIQUE KEY `seckill_uid_gid` (`user_id`, `goods_id`) USING BTREE COMMENT ' 用户 id,商品 id 的唯一索引,解决同一个用户多次抢购'
) ENGINE = INNODB
  AUTO_INCREMENT = 300
  DEFAULT CHARSET = utf8mb4;
2.MyBatis-Plus生成基础代码(以t_order表为例)
1.首先 ctrl + shift + c 复制基础包名(一定要是带点的)

image-20240508155502412

2.右键表名选择 MybatisX-Generator

image-20240508154408364

3.选择模块和基础包以及实体类名字

image-20240508160422478

4.进行配置

image-20240508160551731

5.点击生成,检查代码
1.整个目录概览

image-20240508160625655

2.把实体类移动到pojo并检查

image-20240508160745738

3.检查 OrderMapper.java 发现没有加@Mapper注解

image-20240508160846981

4.查看启动类有@MapperScan所以不用加@Mapper了

image-20240508160923942

5.检查OrderMapper.xml

image-20240508161252522

6.检查application.yml是否自动扫描了Mapper.xml

image-20240508161352765

7.检查OrderService.java

image-20240508161437807

8.检查OrderServiceImpl.java

image-20240508161524301

6.使用MyBatis-Plus生成基础代码的小结
  • 启动类配置MapperScan注解,扫描Mapper接口
  • application.yml配置扫描Mapper.xml
  • 复制基础包名,要带点的
  • 将Mapper.xml生成的最下面的删除即可
7.以同样的方式生成t_seckill_order
3.Service层
1.OrderService.java 新增秒杀方法,返回订单
package com.sxs.seckill.service;

import com.sxs.seckill.pojo.Order;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sxs.seckill.pojo.User;
import com.sxs.seckill.vo.GoodsVo;

/**
* @author 8615941515990
* @description 针对表【t_order】的数据库操作Service
* @createDate 2024-05-08 16:05:29
*/
public interface OrderService extends IService<Order> {
    /**
     * 方法:秒杀
     * @param user
     * @param goodsVo
     * @return
     */
    Order seckill(User user, GoodsVo goodsVo);
}

2.OrderServiceImpl.java
package com.sxs.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sxs.seckill.mapper.SeckillGoodsMapper;
import com.sxs.seckill.pojo.Order;
import com.sxs.seckill.pojo.SeckillGoods;
import com.sxs.seckill.pojo.User;
import com.sxs.seckill.service.OrderService;
import com.sxs.seckill.mapper.OrderMapper;
import com.sxs.seckill.service.SeckillOrderService;
import com.sxs.seckill.vo.GoodsVo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author 8615941515990
 * @description 针对表【t_order】的数据库操作Service实现
 * @createDate 2024-05-08 16:05:29
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order>
        implements OrderService {
    @Resource
    private SeckillGoodsMapper seckillGoodsMapper;
    @Resource
    private SeckillOrderService seckillOrderService;

    @Override
    public Order seckill(User user, GoodsVo goodsVo) {
        // 使用QueryWapper查询秒杀商品
        SeckillGoods seckillGoods = seckillGoodsMapper.selectOne(
                new QueryWrapper<SeckillGoods>()
                        .eq("goods_id", goodsVo.getId()));
        // 将库存减一
        seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
        // 更新秒杀商品库存
        seckillGoodsMapper.updateById(seckillGoods);
        // 创建订单
        Order order = new Order();
        order.setUserId(user.getId());
        order.setGoodsId(goodsVo.getId());
        order.setDeliveryAddrId(0L);
        order.setGoodsName(goodsVo.getGoodsName());
        order.setGoodsCount(1);
        order.setGoodsPrice(goodsVo.getSeckillPrice());
        order.setOrderChannel(1);
        order.setStatus(0);
        order.setCreateDate(null);
        order.setPayDate(null);
        // 插入订单
        baseMapper.insert(order);
        // 生成秒杀商品订单
        seckillOrderService.insertSeckillOrder(order, goodsVo.getId());
        // 返回订单
        return order;
    }
}

3.SeckillOrderService.java 根据普通订单和商品id插入秒杀订单
package com.sxs.seckill.service;

import com.sxs.seckill.pojo.Order;
import com.sxs.seckill.pojo.SeckillOrder;
import com.baomidou.mybatisplus.extension.service.IService;

/**
* @author 8615941515990
* @description 针对表【t_seckill_order】的数据库操作Service
* @createDate 2024-05-08 16:20:14
*/
public interface SeckillOrderService extends IService<SeckillOrder> {
    /**
     * 根据普通订单和商品id插入秒杀订单
     */
    void insertSeckillOrder(Order order, Long goodsId);
}

4.SeckillOrderServiceImpl.java
package com.sxs.seckill.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sxs.seckill.pojo.Order;
import com.sxs.seckill.pojo.SeckillOrder;
import com.sxs.seckill.service.SeckillOrderService;
import com.sxs.seckill.mapper.SeckillOrderMapper;
import org.springframework.stereotype.Service;

/**
* @author 8615941515990
* @description 针对表【t_seckill_order】的数据库操作Service实现
* @createDate 2024-05-08 16:20:14
*/
@Service
public class SeckillOrderServiceImpl extends ServiceImpl<SeckillOrderMapper, SeckillOrder>
    implements SeckillOrderService{

    @Override
    public void insertSeckillOrder(Order order, Long goodsId) {
        // 根据普通订单插入秒杀订单
        SeckillOrder seckillOrder = new SeckillOrder();
        seckillOrder.setUserId(order.getUserId());
        seckillOrder.setOrderId(order.getId());
        seckillOrder.setGoodsId(goodsId);
        baseMapper.insert(seckillOrder);
    }
}

4.Controller层
1.SeckillController.java 完成基础版本的秒杀,简单考虑库存和复购问题
package com.sxs.seckill.controller;

import com.sxs.seckill.pojo.Order;
import com.sxs.seckill.pojo.User;
import com.sxs.seckill.service.GoodsService;
import com.sxs.seckill.service.OrderService;
import com.sxs.seckill.service.SeckillOrderService;
import com.sxs.seckill.vo.GoodsVo;
import com.sxs.seckill.vo.RespBeanEnum;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/5/8 18:49
 * @Version 1.0
 */
@Controller
@RequestMapping("/seckill")
public class SeckillController {
    @Resource
    private GoodsService goodsService;
    @Resource
    private OrderService orderService;
    @Resource
    private SeckillOrderService seckillOrderService;

    @RequestMapping("/doSeckill")
    public String doSeckill(Model model, User user, Long goodsId) {
        // 判断用户是否登录
        if (user == null) {
            return "login";
        }
        // 将用户信息传递到页面
        model.addAttribute("user", user);
        // 根据goodsId获取GoodsVo
        GoodsVo goodsVoByGoodsId = goodsService.findGoodsVoByGoodsId(goodsId);
        // 判断是否有库存
        if (goodsVoByGoodsId.getStockCount() < 1) {
            // 没有库存,返回秒杀失败页面
            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return "secKillFail";
        }
        // 通过秒杀订单表判断是否复购
        if (seckillOrderService.findSeckillOrderByUserIdAndGoodsId(user.getId(), goodsId) != null) {
            // 重复购买,返回秒杀失败页面
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }

        // 秒杀
        Order seckill = orderService.seckill(user, goodsVoByGoodsId);
        // 判断秒杀是否成功
        if (seckill == null) {
            // 秒杀失败,返回秒杀失败页面
            model.addAttribute("errmsg", RespBeanEnum.ERROR.getMessage());
            return "secKillFail";
        }
        // 秒杀成功,返回订单详情页面
        model.addAttribute("order", seckill);
        model.addAttribute("goods", goodsVoByGoodsId);

        // 返回订单详情页面
        return "orderDetail";
    }
}

2.SeckillOrderService.java 新增方法,根据用户id和商品id查找记录
    /**
     * 根据用户id和商品id查询秒杀订单
     */
    SeckillOrder findSeckillOrderByUserIdAndGoodsId(Long userId, Long goodsId);
3.SeckillOrderServiceImpl.java
    @Override
    public SeckillOrder findSeckillOrderByUserIdAndGoodsId(Long userId, Long goodsId) {
        // 根据用户id和商品id查询秒杀订单
        return baseMapper.findSeckillOrderByUserIdAndGoodsId(userId, goodsId);
    }
4.SeckillOrderMapper.java
package com.sxs.seckill.mapper;

import com.sxs.seckill.pojo.SeckillOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
* @author 8615941515990
* @description 针对表【t_seckill_order】的数据库操作Mapper
* @createDate 2024-05-08 16:20:14
* @Entity com.sxs.seckill.pojo.SeckillOrder
*/
public interface SeckillOrderMapper extends BaseMapper<SeckillOrder> {
    /**
     * 根据用户id和商品id查询秒杀订单
     */
    SeckillOrder findSeckillOrderByUserIdAndGoodsId(Long userId, Long goodsId);
}

5.SeckillOrderMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sxs.seckill.mapper.SeckillOrderMapper">

    <resultMap id="BaseResultMap" type="com.sxs.seckill.pojo.SeckillOrder">
            <id property="id" column="id" jdbcType="BIGINT"/>
            <result property="userId" column="user_id" jdbcType="BIGINT"/>
            <result property="orderId" column="order_id" jdbcType="BIGINT"/>
            <result property="goodsId" column="goods_id" jdbcType="BIGINT"/>
    </resultMap>
    <select id="findSeckillOrderByUserIdAndGoodsId" resultType="com.sxs.seckill.pojo.SeckillOrder">
        SELECT
            *
        FROM
            t_seckill_order
        WHERE
            user_id = #{userId}
        AND
            goods_id = #{goodsId}
    </select>

</mapper>

5.前端
1.goodsDetail.html 修改点击抢购按钮的请求(区分多环境)

image-20240508204340828

2.引入orderDetail.html
<html lang="en"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <title>订单详情</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <!--jquery-->
    <script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}"/>
    <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.js}"></script>
    <!-- layer -->
    <script type="text/javascript" th:src="@{/layer/layer.js}"></script>
    <!-- common.js -->
    <script type="text/javascript" th:src="@{/js/common.js}"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            font-family: "Open Sans", sans-serif;
            text-transform: uppercase;
            letter-spacing: 3px;
            font-size: 11px;
        }
         body {
             background: #c9302c;
         }
        .main-header {
            width: 100%;
            height: 100px;
            background: whitesmoke;
            display: block;
        }
        .navbar {
            display: inline-block;
            float: right;
            margin-right: 50px;
            margin-top: 30px;
        }
        .logo {
            display: inline-block;
            margin-top: 30px;
            margin-left: 30px;
            text-decoration: none;
        }
        .logo-lg {
            font-size: 20px;
            font-weight: lighter;
            color: #232324;
        }
        .logo-lg > b {
            font-size: 20px;
            font-weight: lighter;
            color: #232324;
        }
        .container {
            background: #FFFFFF;
            padding-right: 15px;
            padding-left: 15px;
            margin-right: auto;
            margin-left: auto;
            width: 750px;
        }
    </style>
</head>
<body>
<header id="site-header" class="main-header">
    <!-- Logo -->
    <a class="logo" onclick="toList()">
        <span class="logo-lg"><b>商品抢购</b></span>
    </a>
    <nav class="navbar navbar-static-top">
        <!-- Sidebar toggle button-->
        <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </a>
        <div class="navbar-custom-menu">
            <ul class="nav navbar-nav">
                <li class="dropdown user user-menu">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                        <img class="user-image" src="/imgs/user.png" height="32" alt="User
Image">
                        <span class="hidden-xs"></span>
                    </a>
                    <ul class="dropdown-menu">
                        <!-- User image -->
                        <li class="user-header">
                            <img class="img-circle" alt="User Image">
                            <p>
                                Hello ABC - Hello ABC
                                <small>Hello ABC</small>
                            </p>
                        </li>
                        <!-- Menu Body -->
                        <li class="user-body">
                        </li>
                        <li class="user-footer">
                            <div class="pull-middle">
                                <a onclick="toList()" class="btn btn-lg btn-default btn-block">退
                                    出系统</a>
                            </div>
                        </li>
                    </ul>
                </li>
            </ul>
        </div>
    </nav>
</header>
<div class="panel panel-default">
    <div class="panel-heading" style="background: #c9302c;color: white">秒杀订单详情</div>
    <div class="container">
        <table class="table" id="order">
            <tr>
                <td>名称</td>
                <td id="goodName" colspan="3" th:text="${goods.goodsName}"></td>
            </tr>
            <tr>
                <td>图片</td>
                <td colspan="2"><img id="goodImg" width="200" th:src="@{${goods.goodsImg}}" height="200"/></td>
            </tr>
            <tr>
                <td>订单价格</td>
                <td colspan="2" id="goodPrice" th:text="${order.goodsPrice}"></td>
            </tr>
            <tr>
                <td>下单时间</td>
                <td id="createDate" colspan="2"
                    th:text="${#dates.format(order.createDate,'yyyy-MM-dd HH:mm:ss')}"></td>
            </tr>
            <tr>
                <td>订单状态</td>
                <td id="status" >
                    <span th:if="${order.status eq 0}">未支付</span>
                    <span th:if="${order.status eq 1}">代发货</span>
                    <span th:if="${order.status eq 2}">已发货</span>
                    <span th:if="${order.status eq 3}">已收货</span>
                    <span th:if="${order.status eq 4}">已退款</span>
                    <span th:if="${order.status eq 5}">已完成</span>
                </td>
                <td>
                    <button class="btn btn-primary btn-block" type="submit"
                            id="payButton">立即支付
                    </button>
                </td>
            </tr>
            <tr>
                <td>收货人</td>
                <td colspan="2">XXX 13300000000</td>
            </tr>
            <tr>
                <td>收货地址</td>
                <td colspan="2">北京市幸福小区 6 单元 101 号</td>
            </tr>
        </table>
    </div>
</div>
<script>
</script>
</body>
</html>
3.引入secKillFail.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            font-family: "Open Sans", sans-serif;
            text-transform: uppercase;
            letter-spacing: 3px;
            font-size: 11px;
        }
    </style>
</head>
<body>
<h1>秒杀失败 : </h1>
<p th:text="${errmsg}"></p>
</body>
</html>
6.测试
1.正常秒杀
1.初始秒杀商品表(库存为10)

image-20240508204704283

2.秒杀1号商品

image-20240508204758962

3.秒杀成功

image-20240508204812058

4.秒杀商品库存减1

image-20240508204939374

5.普通订单新增一条记录

image-20240508205006209

6.秒杀订单新增一条记录

image-20240508205031531

2.当前用户再次购买
成功跳转到限购页面

image-20240508205230614

3.模拟库存不足的情况
1.将1号商品的库存修改为0

image-20240508205330949

2.切换一个浏览器再次秒杀

image-20240508205543103

3.成功跳转到库存不足的页面

image-20240508205606440

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

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

相关文章

msvcr120.dll是干嘛的?出现找不到msvcr120.dll丢失怎样解决

msvcr120.dll是Microsoft Visual C 2012 Redistributable的核心文件&#xff0c;它是Microsoft Corporation开发的C/C运行时库文件之一。这个文件通常与应用程序一起安装&#xff0c;为应用程序提供许多基本的运行时功能&#xff0c;包括内存管理、异常处理、输入/输出操作等。…

Jenkins、GitLab部署项目

1、安装JDK 1.1、下载openJdk11 yum -y install fontconfig java-11-openjdk1.2、查看安装的版本号 java -version1.3、配置环境变量 vim /etc/profile在最底部添加即可 export JAVA_HOME/usr/lib/jvm/java-11-openjdk-11.0.23.0.9-2.el7_9.x86_64 export PATH$JAVA_HOME/…

SpringBoot注解--10--@Bean,对象注入的三种方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Bean一、如何使用方法注解注意Bean 的命名规则&#xff0c;当没有设置 name 属性时&#xff0c;那么 bean 默认的名称就是方法名&#xff0c;当设置了 name 属性之后…

【OJ】C++ | 二叉树进阶 · 合集(2)

摘要&#xff1a;根据二叉树创建字符串、二叉树的最近公共祖先、二叉树的层序遍历 前言&#xff1a;承接上文&#xff0c;本文继续提供二叉树进阶有关题目的解法。如有错误&#xff0c;烦请指正。 目录 1. 根据二叉树创建字符串 题解及代码 2. 二叉树的最近公共祖先 题解及…

PHAR反序列化

PHAR PHAR&#xff08;PHP Archive&#xff09;文件是一种归档文件格式&#xff0c;phar文件本质上是一种压缩文件&#xff0c;会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时&#xff0c;会自动反序列化meta-data内的内容,这里就是我们反序…

2024年06月编程语言流行度排名

点击查看最新编程语言流行度排名&#xff08;每月更新&#xff09; 2024年06月编程语言流行度排名 编程语言流行度排名是通过分析在谷歌上搜索语言教程的频率而创建的 一门语言教程被搜索的次数越多&#xff0c;大家就会认为该语言越受欢迎。这是一个领先指标。原始数据来自…

JAVA和爬虫,那个值得学习

如果你是初学者&#xff0c;建议先从基础的编程语言学起&#xff0c;比如Java&#xff0c;它能为你打下坚实的编程基础&#xff0c;并且在未来转学其他语言或技术时更加容易。随着编程基础的建立&#xff0c;你可以根据自己的兴趣或职业规划&#xff0c;学习爬虫技术作为补充技…

使用python下载股票数据至sqlite数据库

代码下载地址&#xff1a; https://download.csdn.net/download/weixin_44600457/89389489

961题库 北航计算机 计算机网络 附答案 选择题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 OSI 参考模型的第 5 层( 自下而上 ) 完成的主要功能是 A. 差错控制 B. 路由选择 C. 会话管理 D. 数据表示转换 100BaseT 快速以太网使用的导向传输介质是 A. 双绞线 B. …

德人合科技——@天锐绿盾 | -文档透明加密系统

天锐绿盾文档透明加密系统是一种先进的数据安全解决方案&#xff0c;旨在保护企业和组织的敏感信息&#xff0c;防止未经授权的访问和泄漏。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是该系统的一些关键特点和功…

FJSP:常春藤算法(Ivy algorithm,LVYA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

详细介绍 FJSP&#xff1a;常春藤算法&#xff08;Ivy algorithm&#xff0c;LVYA&#xff09;求解柔性作业车间调度问题&#xff08;FJSP&#xff09;&#xff0c;提供MATLAB代码-CSDN博客 完整MATLAB代码 FJSP&#xff1a;常春藤算法&#xff08;Ivy algorithm&#xff0c;…

随后记: uniapp uview u-dropdown 下拉菜单固定高度滑动不生效

使用u-dropdown 下拉组件 按照uview官网讲解使用 配置根本不生效 scroll-y"true" style"height: 200rpx;" 但是在下拉的时候&#xff0c;不能上下滑动 &#xff0c;原因是自带的遮罩层挡住了 解决办法&#xff1a;在下拉菜单打开和关闭的时候&#xff0c…

英语四级翻译练习笔记②——大学英语四级考试2023年12月真题(第二套)

目录 引言&#xff08;必看&#xff09; 四级翻译评分标准分析及真题解析 四级翻译评分标准 四级翻译真题 学生作答 错误标注 标准满分答案 提高翻译水平的建议 引言&#xff08;必看&#xff09; 这是一篇英语四级翻译的练习的专栏&#xff0c;如果相信我的话就将这…

树莓派5烧系统和ssh远程实现

1、硬件说明 树莓派5 64G micro SD卡读卡器 2、烧录系统过程记录 之前写过一篇pi4B烧录Ubuntu22.04的博客&#xff0c;这篇就简单记录备份下 2.1 去ubuntu官网在树莓派上安装Ubuntu | Ubuntu下载Ubuntu 桌面 24.04 LTS 我之前已经下好了就有个(1) 2.2 用读卡器把SD卡插到…

车辆前向碰撞预警系统性能要求和测试规程

前言 本文整理《GB/T 33577-2017 智能运输系统-车辆前向碰撞预警系统性能要求和测试规程》国标文件关键信息,FCW系统性能和测试右给深层次的认识。 术语和定义 车辆前向碰撞预警系统 forward vehicle collision warning system自车 subject vehicle(SV)目标车辆 target ve…

Lagrange ZK Coprocessor:革新区块链领域的大数据应用

1. 引言 2024年5月11日&#xff0c;Lagrange Labs宣称获得由Founders Fund领投&#xff08;Archetype Ventures, 1kx, Maven11, Fenbushi Capital, Volt Capital, CMT Digital, Mantle Ecosystem Fund和其它天使投资人跟头&#xff09;的1320万美金种子轮融资&#xff0c;致力于…

【spring】第一篇 IOC和DI入门案例

Spring到底是如何来实现IOC和DI的&#xff0c;那接下来就通过一些简单的入门案例&#xff0c;来演示下具体实现过程。 目录 前期准备 一、IOC入门案例 思路分析 代码实现 二、DI入门案例 思路分析 代码实现 总结 前期准备 使用IDEA创建Maven项目&#xff0c;首先需要配…

linux进阶的一些操作以及知识点------习题集(实践)

请创建以你姓名全拼的用户luwenhua&#xff0c;将其设置为免密登录&#xff0c;切换到luwenhua用户&#xff0c;打开终端&#xff0c;完成以下操作 &#xff08;一&#xff09;bash脚本基础练习 1&#xff09;第一题&#xff1a;请在终端里定义两个用户变量num120&#xff0c…

来自大厂硬盘的降维打击!当希捷酷玩520 1TB SSD卷到369,请问阁下该怎么应对?

来自大厂硬盘的降维打击&#xff01;当希捷酷玩520 1TB SSD卷到369&#xff0c;请问阁下该怎么应对&#xff1f; 哈喽小伙伴们好&#xff0c;我是Stark-C~ 今年4月份的时候因为电脑上的游戏盘突然挂掉&#xff0c;为了性价比选购了希捷酷玩520 1TB SSD&#xff0c;同时我也是…

中国历年肥料进口数量统计报告

数据来源于国家统计局&#xff0c;为1991年到2021年我国每年肥料进口数量统计。 2021年&#xff0c;我国进口肥料909万吨&#xff0c;比上年减少151万吨。 数据统计单位为&#xff1a;万吨 数据说明&#xff1a; 数据来源于国家统计局&#xff0c;为海关进出口统计数 我国肥料…
最新文章