一、创建实体类
在entity包中创建和数据库字段对应的实体类,一共有四个实体类
@Data public class Item { private Integer id; private String name; private String code; private Long stock; private Date purchaseTime; private Integer isActive; private Date createTime; private Date updateTime; }
itemKill,代表可以抢购的商品信息
@Data public class ItemKill { private Integer id; private Integer itemId; private Integer total; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date startTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date endTime; private Byte isActive; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date createTime; private String itemName; //采用服务器时间控制是否可以进行抢购 private Integer canKill; }
itemKillSuccess,代表抢购成功的商品实体类
@Data @ToString public class ItemKillSuccess { private String code; private Integer itemId; private Integer killId; private String userId; private Byte status; private Date createTime; private Integer diffTime; }
User,存储用户的信息
@Data @ToString @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String userName; private String password; private String phone; private String email; private Byte isActive; private Date createTime; private Date updateTime; }
二、主页面后端逻辑的编写
在正式开始业务前先创建一个BaseController,用于处理统一的错误:在controller文件夹中创建BaseController:
@Controller @RequestMapping("/base") public class BaseController { @RequestMapping(value = "/error",method = RequestMethod.GET) public String error(){ return "error"; } }
我打算在主页展示所有商品的信息,如果该商品的余量大于0并且在抢购时间内,就展示出抢购按钮。如果不符合上面的条件,则展示此商品无法被抢购的提示。首先在controller文件夹中新建ItemController
@Controller public class ItemController { private static final Logger log= LoggerFactory.getLogger(ItemController.class); @Autowired private ItemServiceImpl itemServiceImpl; //获取商品列表 @RequestMapping(value = "/item",method = RequestMethod.GET) public String list(Model model){ try{ List<ItemKill> itemKills =itemServiceImpl.getKillItems(); model.addAttribute("itemkills",itemKills); }catch (Exception e){ log.error("获取商品列表异常",e.fillInStackTrace()); return "redirect:/base/error"; } return "item"; } @RequestMapping(value = "/detail/{id}",method = RequestMethod.GET) public String detail(@PathVariable Integer id, Model model){ if (id==null||id<0){ return "redirect:/base/error"; } try{ ItemKill itemKill=itemServiceImpl.getKillDetail(id); model.addAttribute("itemkill",itemKill); }catch (Exception e){ log.error("获取详情发生异常:id={}"+id); } return "detail"; } }
在itemController中,自动注入ItemServiceImpl ,所有的业务都在Service中处理。在service文件夹中编写itemService接口和itemServiceImpl实现类:
public interface ItemService { List<ItemKill> getKillItems(); ItemKill getKillDetail(Integer id) throws Exception; }
ItemServiceImpl:这一段代码实现的是获取秒杀商品的列表和获取秒杀商品的详情
@Service public class ItemServiceImpl implements ItemService { @Autowired private ItemKillMapper itemKillMapper; //获取待秒杀商品的列表 @Override public List<ItemKill> getKillItems() { List<ItemKill> list = itemKillMapper.selectAll(); return list; } //获取秒杀详情 @Override public ItemKill getKillDetail(Integer id) throws Exception { ItemKill itemKill=itemKillMapper.selectByid(id); if (itemKill==null){ throw new Exception("秒杀详情记录不存在"); } return itemKill; } }
在上述的代码中自动注入ItemKillMapper,用来操作数据库,这里采用注解的方式:在mapper下新建ItemKillMapper
@Mapper public interface ItemKillMapper { @Select("select \n" + "a.*,\n" + "b.name as itemName,\n" + "(\n" + "\tcase when(now() BETWEEN a.start_time and a.end_time and a.total>0)\n" + "\t\tthen 1\n" + "\telse 0\n" + "\tend\n" + ")as cankill\n" + "from item_kill as a left join item as b\n" + "on a.item_id = b.id\n" + "where a.is_active=1;") List<ItemKill> selectAll(); @Select("select \n" + "a.*,\n" + "b.name as itemName,\n" + "(\n" + "\tcase when(now() BETWEEN a.start_time and a.end_time and a.total>0)\n" + "\t\tthen 1\n" + "\telse 0\n" + "\tend\n" + ")as cankill\n" + "from item_kill as a left join item as b\n" + "on a.item_id = b.id\n" + "where a.is_active=1 and a.id=#{id};") ItemKill selectByid(Integer id); }
讲解一下两段sql,第一段sql的目的是筛选此时服务器时间在start_time到end_time并且总量大于0的商品,如果可以抢购另cankill字段为1,否则为0
select a.*,b.name as itemName, ( case when(now() BETWEEN a.start_time and a.end_time and a.total>0) then 1 else 0 end )as cankill from item_kill as a left join item as b on a.item_id=b.id where a.is_active=1
第二段sql的作用是根据id查询,逻辑和前一段相同。
三、前端页面编写
前端页面不是本次项目的重点,基于BootStrap编写,Bootstrap的依赖直接使用CDN获取,不需要下载相关的css和js代码:因为到这里为止用户登陆还未做,因此默认用户id为10,后续会做修改
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"><!--引入thymeleaf--> <html lang="en"> <head> <meta charset="UTF-8"> <title>商品秒杀列表</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!-- 可选的 Bootstrap 主题文件(一般不用引入) --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> </head> <body> <div> <table class="table table-hover"> <thead> <tr> <th>产品名称</th> <th>剩余数量</th> <th>抢购开始时间</th> <th>抢购结束时间</th> <th>详情</th> </tr> </thead> <tbody> <tr th:each="itemkill:${itemkills}"> <td th:text="${itemkill.itemName}"></td> <td th:text="${itemkill.total}"></td> <td th:text="${#dates.format(itemkill.startTime,'yyyy-MM-dd HH:mm:ss')}" th:pattern="${'yyyy-MM-dd HH:mm:ss'}"></td> <td th:text="${#dates.format(itemkill.endTime,'yyyy-MM-dd HH:mm:ss')}" th:pattern="${'yyyy-MM-dd HH:mm:ss'}"></td> <td th:if="${itemkill.canKill} eq 1"> <a th:href="@{'/detail/'+${itemkill.id}}"> 详情 </a> </td> <td th:if="${itemkill.canKill} eq 0"> 未在时间段内或容量为0 </td> </tr> </tbody> </table> </div> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> </body> </html>
detail.html
由于字数限制,detail.html中的代码可以到下面两个路劲中查看
https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FOliverLiy%2FSecondKill%2Ftree%2Fversion2.0
最后是错误页面,error.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>异常</title> </head> <body> <h1>出现了异常</h1> </body> </html>
四、实现效果
运行项目,输入https://link.juejin.cn/?target=http%3A%2F%2Flocalhost%3A8080%2Fitem,可以看到首页展示如下:只有当剩余数量和抢购在时间内才会展示详情,点击详情:可以看到具体的信息,当点击抢购后完成抢购。这个功能将在下一节介绍。
到目前为止的代码放在https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FOliverLiy%2FSecondKill%2Ftree%2Fversion2.0中
我搭建了一个微信公众号《Java鱼仔》,如果你对本项目有任何疑问,欢迎在公众号中联系我,我会尽自己所能为大家解答。