▶【SecKill】U3 实现秒杀功能
▶【SecKill】U3 实现秒杀功能
一、数据库设计
1、商品表和秒杀商品表是两个互相独立的表?
【正确方法】商品表和秒杀商品表是两个互相独立的表,其中的关联为goods_id
@Data public class GoodsVo extends Goods { private Double miaoshaPrice; private Integer stockCount; private Date startDate; private Date endDate; }
2、创建5个对应数据库的domain对象
Goods:
package com.kirin.miaosha.domain; public class Goods { private Long id; private String goodsName; private String goodsTitle; private String goodsImg; private String goodsDetail; private Double goodsPrice; private Integer goodsStock; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsTitle() { return goodsTitle; } public void setGoodsTitle(String goodsTitle) { this.goodsTitle = goodsTitle; } public String getGoodsImg() { return goodsImg; } public void setGoodsImg(String goodsImg) { this.goodsImg = goodsImg; } public String getGoodsDetail() { return goodsDetail; } public void setGoodsDetail(String goodsDetail) { this.goodsDetail = goodsDetail; } public Double getGoodsPrice() { return goodsPrice; } public void setGoodsPrice(Double goodsPrice) { this.goodsPrice = goodsPrice; } public Integer getGoodsStock() { return goodsStock; } public void setGoodsStock(Integer goodsStock) { this.goodsStock = goodsStock; } }
MiaoshaGoods:
package com.kirin.miaosha.domain; import java.util.Date; public class MiaoshaGoods { private Long id; private Long goodsId; private Integer stockCount; private Date startDate; private Date endDate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } 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; } }
MiaoshaOrder:
package com.kirin.miaosha.domain; public class MiaoshaOrder { private Long id; private Long userId; private Long orderId; private Long goodsId; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getOrderId() { return orderId; } public void setOrderId(Long orderId) { this.orderId = orderId; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } }
MiaoshaUser:
package com.kirin.miaosha.domain; import java.util.Date; public class MiaoshaUser { private Long id; private String nickname; private String password; private String salt; private String head; private Date registerDate; private Date lastLoginDate; private Integer loginCount; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } public String getHead() { return head; } public void setHead(String head) { this.head = head; } public Date getRegisterDate() { return registerDate; } public void setRegisterDate(Date registerDate) { this.registerDate = registerDate; } public Date getLastLoginDate() { return lastLoginDate; } public void setLastLoginDate(Date lastLoginDate) { this.lastLoginDate = lastLoginDate; } public Integer getLoginCount() { return loginCount; } public void setLoginCount(Integer loginCount) { this.loginCount = loginCount; } }
OrderInfo:
package com.kirin.miaosha.domain; import java.util.Date; public class OrderInfo { private Long id; private Long userId; private Long goodsId; private Long deliveryAddrId; private String goodsName; private Integer goodsCount; private Double goodsPrice; private Integer orderChannel; private Integer status; private Date createDate; private Date payDate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getGoodsId() { return goodsId; } public void setGoodsId(Long goodsId) { this.goodsId = goodsId; } public Long getDeliveryAddrId() { return deliveryAddrId; } public void setDeliveryAddrId(Long deliveryAddrId) { this.deliveryAddrId = deliveryAddrId; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public Integer getGoodsCount() { return goodsCount; } public void setGoodsCount(Integer goodsCount) { this.goodsCount = goodsCount; } public Double getGoodsPrice() { return goodsPrice; } public void setGoodsPrice(Double goodsPrice) { this.goodsPrice = goodsPrice; } public Integer getOrderChannel() { return orderChannel; } public void setOrderChannel(Integer orderChannel) { this.orderChannel = orderChannel; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public Date getPayDate() { return payDate; } public void setPayDate(Date payDate) { this.payDate = payDate; } }
二、商品列表页
1、com.kirin.miaosha.service / GoodsService.java:
package com.kirin.miaosha.service;
@Service public class GoodsService { @Autowired GoodsDao goodsDao; public List<GoodsVo> listGoodsVo(){ return goodsDao.listGoodsVo(); } }
2、com.kirin.miaosha.dao / GoodsDao.java:
package com.kirin.miaosha.dao; @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(); }
3、com.kirin.miaosha.vo / GoodsVo.java:实现联表查询:将商品表和秒杀商品表的数据拼到一起,输出商品列表页
package com.kirin.miaosha.vo; 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; } }
4、com.kirin.miaosha.controller / GoodsController.java:
package com.kirin.miaosha.controller; @Controller @RequestMapping("/goods") public class GoodsController { @Autowired MiaoshaUserService userService; @Autowired RedisService redisService; @Autowired GoodsService goodsService; @RequestMapping("/to_list") public String list(Model model,MiaoshaUser user) { model.addAttribute("user", user); //查询商品列表 List<GoodsVo> goodsList = goodsService.listGoodsVo(); model.addAttribute("goodsList", goodsList); return "goods_list"; } }
5、goods_list.html:
把照片添加在
<!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> <table class="table" id="goodslist"> <tr> <td>商品名称</td> <td>商品图片</td> <td>商品原价</td> <td>秒杀价</td> <td>库存数量</td> <td>详情</td> </tr> <!-- 循环展示goodsList的信息 --> <tr th:each="goods,goodsStat : ${goodsList}"> <td th:text="${goods.goodsName}"></td> <td> <img th:src="@{${goods.goodsImg}}" width="100" height="100" /> <!-- @{}表示从根目录开始 --> </td> <td th:text="${goods.goodsPrice}"></td> <td th:text="${goods.miaoshaPrice}"></td> <td th:text="${goods.stockCount}"></td> <td><a th:href="'/goods/to_detail/'+${goods.id}">详情</a></td> </tr> </table> </div> </body> </html>
三、商品详情页
1、com.kirin.miaosha.service / GoodsService.java:
package com.kirin.miaosha.service; @Service public class GoodsService { @Autowired GoodsDao goodsDao; public List<GoodsVo> listGoodsVo(){ return goodsDao.listGoodsVo(); } public GoodsVo getGoodsVoByGoodsId(long goodsId) { return goodsDao.getGoodsVoByGoodsId(goodsId); } }
2、com.kirin.miaosha.dao / GoodsDao.java:
package com.kirin.miaosha.dao; @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); }
3、com.kirin.miaosha.controller / GoodsController.java:
package com.kirin.miaosha.controller; @Controller @RequestMapping("/goods") public class GoodsController { @Autowired MiaoshaUserService userService; @Autowired RedisService redisService; @Autowired GoodsService goodsService; //1.用户登录后,跳转到商品列表页 @RequestMapping("/to_list") public String list(Model model,MiaoshaUser user) { model.addAttribute("user", user); //查询商品列表 List<GoodsVo> goodsList = goodsService.listGoodsVo(); model.addAttribute("goodsList", goodsList); return "goods_list"; } //2.查看商品详情 @RequestMapping("/to_detail/{goodsId}") //根据id获取商品 public String detail(Model model,MiaoshaUser user,@PathVariable("goodsId")long goodsId) { model.addAttribute("user", user); GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId); model.addAttribute("goods", goods); long startAt = goods.getStartDate().getTime(); long endAt = goods.getEndDate().getTime(); long now = System.currentTimeMillis(); int miaoshaStatus = 0; int remainSeconds = 0; if(now < startAt ) { miaoshaStatus = 0; remainSeconds = (int)((startAt - now )/1000); }else if(now > endAt){ miaoshaStatus = 2; remainSeconds = -1; } model.addAttribute("miaoshaStatus", miaoshaStatus); model.addAttribute("remainSeconds", remainSeconds); return "goods_detail"; } }
运行:
四、订单详情页
1、com.kirin.miaosha.controller / MiaoshaController.java:
package com.kirin.miaosha.controller; @Controller @RequestMapping("/miaosha") public class MiaoshaController { @Autowired MiaoshaUserService userService; @Autowired RedisService redisService; @Autowired GoodsService goodsService; @Autowired OrderService orderService; @Autowired MiaoshaService miaoshaService; @RequestMapping("/do_miaosha") public String list(Model model,MiaoshaUser user,@RequestParam("goodsId")long goodsId) { model.addAttribute("user", user); if(user == null) { return "login"; } //判断库存 GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId); int stock = goods.getStockCount(); if(stock <= 0) { model.addAttribute("errmsg", CodeMsg.MIAO_SHA_OVER.getMsg()); return "miaosha_fail"; } //判断是否已经秒杀到了 MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId); if(order != null) { model.addAttribute("errmsg", CodeMsg.REPEATE_MIAOSHA.getMsg()); return "miaosha_fail"; } //减库存 下订单 写入秒杀订单 OrderInfo orderInfo = miaoshaService.miaosha(user, goods); model.addAttribute("orderInfo", orderInfo); model.addAttribute("goods", goods); return "order_detail"; } }
2、com.kirin.miaosha.result / CodeMsg.java:
package com.kirin.miaosha.result; public class CodeMsg { private int code; private String msg; //定义通用异常 public static CodeMsg SUCCESS = new CodeMsg(0, "success"); public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常"); public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s"); //带参数 //登录模块 5002XX public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效"); public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空"); public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空"); public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误"); public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在"); public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误"); //商品模块5003XX //订单模块5004XX //秒杀模块5005XX public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕"); public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀"); private CodeMsg( ) { } private CodeMsg( int code,String msg ) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public CodeMsg fillArgs(Object... args) { int code = this.code; String message = String.format(this.msg, args); return new CodeMsg(code, message); } @Override public String toString() { return "CodeMsg [code=" + code + ", msg=" + msg + "]"; } }
3、com.kirin.miaosha.dao / OrderDao.java:
package com.kirin.miaosha.dao; @Mapper public interface OrderDao { @Select("select * from miaosha_order where user_id=#{userId} and goods_id=#{goodsId}") public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(@Param("userId")long userId, @Param("goodsId")long goodsId); @Insert("insert into order_info(user_id, goods_id, goods_name, goods_count, goods_price, order_channel, status, create_date)values(" + "#{userId}, #{goodsId}, #{goodsName}, #{goodsCount}, #{goodsPrice}, #{orderChannel},#{status},#{createDate} )") @SelectKey(keyColumn="id", keyProperty="id", resultType=long.class, before=false, statement="select last_insert_id()") public long insert(OrderInfo orderInfo); @Insert("insert into miaosha_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})") public int insertMiaoshaOrder(MiaoshaOrder miaoshaOrder); }
4、com.kirin.miaosha.service / OrderService.java:
package com.kirin.miaosha.service; @Service public class OrderService { @Autowired OrderDao orderDao; public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(long userId, long goodsId) { return orderDao.getMiaoshaOrderByUserIdGoodsId(userId, goodsId); } //生成订单 @Transactional public OrderInfo createOrder(MiaoshaUser user, GoodsVo goods) { OrderInfo orderInfo = new OrderInfo(); orderInfo.setCreateDate(new Date()); orderInfo.setDeliveryAddrId(0L); orderInfo.setGoodsCount(1); orderInfo.setGoodsId(goods.getId()); orderInfo.setGoodsName(goods.getGoodsName()); orderInfo.setGoodsPrice(goods.getMiaoshaPrice()); orderInfo.setStatus(0); orderInfo.setUserId(user.getId()); long orderId = orderDao.insert(orderInfo); MiaoshaOrder miaoshaOrder = new MiaoshaOrder(); miaoshaOrder.setGoodsId(goods.getId()); miaoshaOrder.setOrderId(orderId); miaoshaOrder.setUserId(user.getId()); orderDao.insertMiaoshaOrder(miaoshaOrder); return orderInfo; } }
5、com.kirin.miaosha.service / MiaoshaService.java:
package com.kirin.miaosha.service; @Service public class MiaoshaService { @Autowired GoodsService goodsService; @Autowired OrderService orderService; @Transactional public OrderInfo miaosha(MiaoshaUser user, GoodsVo goods) { //减库存 下订单 写入秒杀订单 goodsService.reduceStock(goods); return orderService.createOrder(user, goods); } }
6、com.kirin.miaosha.service / GoodsService.java:
package com.kirin.miaosha.service;
@Service public class GoodsService { @Autowired GoodsDao goodsDao; public List<GoodsVo> listGoodsVo(){ return goodsDao.listGoodsVo(); } public GoodsVo getGoodsVoByGoodsId(long goodsId) { return goodsDao.getGoodsVoByGoodsId(goodsId); }
public void reduceStock(GoodsVo goods) { MiaoshaGoods g = new MiaoshaGoods(); g.setGoodsId(goods.getId()); goodsDao.reduceStock(g); } }
7、com.kirin.miaosha.dao / GoodsDao.java:
package com.kirin.miaosha.dao; @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); @Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId}") public int reduceStock(MiaoshaGoods g); }
8、miaosha_fail.html:
<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>秒杀失败</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> 秒杀失败:<p th:text="${errmsg}"></p> </body> </html>