本文主要是介绍秒杀基本功能开发(不考虑高并发情况),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 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.秒杀进行中
2.修改db的秒杀开始时间为明天
3.出现秒杀倒计时
4.修改db的秒杀结束时间比目前要早
5.秒杀已结束
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>
2.goodsDetail.html 根据秒杀状态,控制按钮状态
2.测试
1.秒杀已结束,按钮不可用
2.秒杀进行中,按钮可用
3.秒杀未开始,按钮不可用
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 = INNODBAUTO_INCREMENT = 600DEFAULT 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 = INNODBAUTO_INCREMENT = 300DEFAULT CHARSET = utf8mb4;
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生成基础代码的小结
- 启动类配置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 {@Resourceprivate SeckillGoodsMapper seckillGoodsMapper;@Resourceprivate SeckillOrderService seckillOrderService;@Overridepublic 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{@Overridepublic 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 {@Resourceprivate GoodsService goodsService;@Resourceprivate OrderService orderService;@Resourceprivate SeckillOrderService seckillOrderService;@RequestMapping("/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {// 判断用户是否登录if (user == null) {return "login";}// 将用户信息传递到页面model.addAttribute("user", user);// 根据goodsId获取GoodsVoGoodsVo 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
@Overridepublic 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 mapperPUBLIC "-//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*FROMt_seckill_orderWHEREuser_id = #{userId}ANDgoods_id = #{goodsId}</select></mapper>
5.前端
1.goodsDetail.html 修改点击抢购按钮的请求(区分多环境)
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)
2.秒杀1号商品
3.秒杀成功
4.秒杀商品库存减1
5.普通订单新增一条记录
6.秒杀订单新增一条记录
2.当前用户再次购买
成功跳转到限购页面
3.模拟库存不足的情况
1.将1号商品的库存修改为0
2.切换一个浏览器再次秒杀
3.成功跳转到库存不足的页面
这篇关于秒杀基本功能开发(不考虑高并发情况)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!