一、数据库及表设计
秒杀项目主要流程:用户浏览商品列表,然后选中了一个商品,进入商品的详情,在详情中点击商品秒杀按钮,假如秒杀成功,进入订单详情页。
1.create商品表goods:
create table goods
(
id BIGINT(20) primary key not null auto_increment,
goods_name varchar(16) DEFAULT NULL,
goods_title varchar(64) DEFAULT NULL,
goods_img varchar(64) DEFAULT NULL,
goods_detail LONGTEXT,
goods_price DECIMAL(10,2) DEFAULT '0.00',
goods_stock INT(11) DEFAULT '0'
);
2.create秒杀商品表:
(商品的id,秒杀的价格、库存(这个是可以秒杀的库存)、秒杀的开始时间和结束时间)
create table miaosha_goods
(
id BIGINT(20) primary key not null auto_increment,
goods_id BIGINT(20) DEFAULT NULL,
miaosha_price DECIMAL(10,2) DEFAULT '0.00',
stock_count INT(11) DEFAULT NULL,
start_date datetime DEFAULT NULL,
end_date datetime DEFAULT NULL
);
3.秒杀订单表:
create table miaosha_order
(
id BIGINT(20) primary key not null auto_increment,
user_id BIGINT(20) DEFAULT NULL,
order_id BIGINT(20) DEFAULT NULL,
goods_id BIGINT(20) DEFAULT NULL
);
4.订单详情表:
create table order_info
(
id BIGINT(20) primary key not null auto_increment,
user_id BIGINT(20) DEFAULT NULL,
goods_id BIGINT(20) DEFAULT NULL,
/*收获地址*/
delivery_addr_id BIGINT(20) DEFAULT NULL,
goods_name varchar(16) DEFAULT NULL,
goods_count INT(11) DEFAULT '0',
goods_price decimal(10,2) DEFAULT '0.00',
order_channel TINYINT(4) DEFAULT '0',
order_status TINYINT(4) DEFAULT '0',
create_date datetime DEFAULT NULL,
pay_date datetime DEFAULT NULL
);
5.秒杀用户表:
create table miaosha_user
(
id BIGINT(20) primary key not null auto_increment,
nickname VARCHAR(255) NOT NULL,
pwd VARCHAR(32) DEFAULT NULL,
salt VARCHAR(10) DEFAULT NULL,
head VARCHAR(128) DEFAULT NULL,
register_date DATETIME DEFAULT NULL,
last_login_time DATETIME DEFAULT NULL,
login_count INT(11) DEFAULT 0
);
二、商品列表
1.先创建商品的服务类GoodsService ,注入GoodsDao:
@Service
public class GoodsService {
public static final String COOKIE1_NAME_TOKEN="token";
@Autowired
GoodsDao goodsDao;
@Autowired
RedisService redisService;
//获取商品信息列表
public List<GoodsVo> getGoodsVoList() {
return goodsDao.getGoodsVoList();
}
//获取商品根据商品Id
public GoodsVo getGoodsVoByGoodsId(long goodsId) {
return goodsDao.getGoodsVoByGoodsId(goodsId);
}
//减少商品的库存
public void reduceStock(GoodsVo goodsvo) {
MiaoshaGoods goods=new MiaoshaGoods();
goods.setGoodsId(goodsvo.getId());
//goods.setStockCount(goodsvo.getGoodsStock()-1); sql里面去运算
//goodsDao.reduceStock(goods.getGoodsId());
goodsDao.reduceStock(goods);
}
}
2.创建GoodsDao:
注意:这里我们查数据库的时候,不只是查找的商品的信息,我们同时想把商品的秒杀信息也一起查出来,但是这两个不同数据在两个表里面,我们就想办法封装一个GoodsVo,将两张表的数据封装到一起。
下面是GoodsVo对象,因为继承Goods,拥有Goods的所有字段:
public class Goods {
private Long id;
private String goodsName;
private String goodsTitle;
private String goodsImg;
private String goodsDetail;
private Double goodsPrice;
private Integer goodsStock;
然后再自己定义MiaoshaGoods里面的字段,最终拼接成一个GoodsVo对象。
public class GoodsVo extends Goods{
private Double miaoshaPrice;
private Integer stockCount;
private Date startDate;
private Date endDate;
public Integer getStockCount() {
return stockCount;
}
public void setStockCount(Integer stockCount) {
this.stockCount = stockCount;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public Double getMiaoshaPrice() {
return miaoshaPrice;
}
public void setMiaoshaPrice(Double miaoshaPrice) {
this.miaoshaPrice = miaoshaPrice;
}
}
这里是将两个表做连接查询(miaosha_goods mg left join goods g),我们需要查找的数据库表里面的各个字段对应到我们GoodsVo里面的属性,那么就可以给其赋值。
@Mapper
public interface GoodsDao {
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id")
public List<GoodsVo> listGoodsVo();
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id where g.id = #{goodsId}")
public GoodsVo getGoodsVoByGoodsId(@Param("goodsId")long goodsId); //stock_count>0的时候才去更新,数据库本身会有锁,那么就不会在数据库中同时多个线程更新一条记录,使用数据库特性来保证超卖的问题
@Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId}") public int reduceStock(MiaoshaGoods g); }
前端goodlist:
<!DOCTYPE HTML>
<html 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.min.js}"></script>
<!-- jquery-validator -->
<script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
<script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
<!-- layer -->
<script type="text/javascript" th:src="@{/layer/layer.js}"></script>
<!-- md5.js -->
<script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
<!-- common.js -->
<script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<div class="panel panel-default">
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span th:if="${user eq null}"> 您还没有登录,请登陆后再操作<br/></span>
<span>请填写收货地址。</span>
</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td colspan="3" th:text="${goods.goodsName}"></td>
</tr>
<tr>
<td>商品图片</td>
<td colspan="3"><img th:src="@{${goods.goodsImg}}" width="200" height="200" /></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td th:text="${#dates.format(goods.startDate, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td id="miaoshaTip">
<input type="hidden" id="remainSeconds" th:value="${remainSeconds}" />
<span th:if="${miaoshaStatus eq 0}">秒杀倒计时:<span id="countDown" th:text="${remainSeconds}"></span>秒</span>
<span th:if="${miaoshaStatus eq 1}">秒杀进行中</span>
<span th:if="${miaoshaStatus eq 2}">秒杀已结束</span>
</td>
<td>
<form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
<button class="btn btn-primary btn-block" type="submit" id="buyButton" > 立即秒杀</button>
<input type="hidden" name="goodsId" th:value="${goods.id}" />
</form>
</td>
</tr>
<tr>
<td>商品原价</td>
<td colspan="3" th:text="${goods.goodsPrice}"></td>
</tr>
<tr>
<td>秒杀价</td>
<td colspan="3" th:text="${goods.miaoshaPrice}"></td>
</tr>
<tr>
<td>库存数量</td>
<td colspan="3" th:text="${goods.stockCount}"></td>
</tr>
</table>
</div>
</body>
<script>
$(function(){
countDown();
});
function countDown(){
var remainSeconds = $("#remainSeconds").val();
var timeout;
if(remainSeconds > 0){//秒杀还没开始,倒计时
$("#buyButton").attr("disabled", true);
timeout = setTimeout(function(){
$("#countDown").text(remainSeconds - 1);
$("#remainSeconds").val(remainSeconds - 1);
countDown();
},1000);
}else if(remainSeconds == 0){//秒杀进行中
$("#buyButton").attr("disabled", false);
if(timeout){
clearTimeout(timeout);
}
$("#miaoshaTip").html("秒杀进行中");
}else{//秒杀已经结束
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀已经结束");
}
}
</script>
</html>