当前位置: 首页 > news >正文

从零搭建基于SpringBoot的秒杀系统(三):首页、详情页编写

在上一篇博客中,我们已经搭好了系统的主要架构,目前已经可以跑通这个项目,接下来我们就可以把注意力都集中在代码上。本次需要创建的代码目录如下:

一、创建实体类

在entity包中创建和数据库字段对应的实体类,一共有四个实体类

item,代表所有的商品信息

package com.sdxb.secondkill.entity;
import lombok.Data;
import java.util.Date;
@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,代表可以抢购的商品信息

package com.sdxb.secondkill.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@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,代表抢购成功的商品实体类

package com.sdxb.secondkill.entity;
import lombok.Data;
import lombok.ToString;
import java.util.Date;

@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,存储用户的信息

package com.sdxb.secondkill.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;

@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:

package com.sdxb.seckill.server.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/base")
public class BaseController {
    @RequestMapping(value = "/error",method = RequestMethod.GET)
    public String error(){
        return "error";
    }
}

我打算在主页展示所有商品的信息,如果该商品的余量大于0并且在抢购时间内,就展示出抢购按钮。如果不符合上面的条件,则展示此商品无法被抢购的提示。首先在controller文件夹中新建ItemController

package com.sdxb.secondkill.controller;

import com.sdxb.secondkill.entity.ItemKill;
import com.sdxb.secondkill.service.Impl.ItemServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

@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实现类:

itemService接口:

package com.sdxb.secondkill.service;
import com.sdxb.secondkill.entity.ItemKill
import java.util.List;

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

package com.sdxb.secondkill.mapper;

import com.sdxb.secondkill.entity.ItemKill;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;

@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,后续会做修改

item.html

<!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

<!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>
            <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">
                <button th:onclick="|executekill(${itemkill.id})|">抢购</button>
            </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>
<script type="text/javascript">
    function executekill(killid) {
        $.ajax({
            type:"POST",
            url:"http://localhost:8080/kill/execute",
            contentType:"application/json;charset=utf-8",
            data:JSON.stringify({"killid":killid,"userid":10}),
            dataType:"json",
            success:function (res) {
                if (res.code==0){
                    window.location.href="http://localhost:8080/kill/execute/success"
                }else {
                    window.location.href="http://localhost:8080/kill/execute/fail"
                }
            },
            error:function (msg) {
              alert("数据提交失败");
            }
        })
    }
</script>
</body>
</html>

最后是错误页面,error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>异常</title>
</head>
<body>
<h1>出现了异常</h1>
</body>
</html>

四、实现效果

运行项目,输入http://localhost:8080/item,可以看到首页展示如下:

只有当剩余数量和抢购在时间内才会展示详情,点击详情:

可以看到具体的信息,当点击抢购后完成抢购。这个功能将在下一节介绍。

到目前为止的代码放在https://github.com/OliverLiy/SecondKill/tree/version2.0

我搭建了一个微信公众号《Java鱼仔》,如果你对本项目有任何疑问,欢迎在公众号中联系我,我会尽自己所能为大家解答。

相关文章:

  • 从零搭建基于SpringBoot的秒杀系统(四):雪花算法生成订单号以及抢购功能实现
  • 操作系统实验一 命令解释程序的编写
  • 从零搭建基于SpringBoot的秒杀系统(五):基于Shiro的人员登陆认证
  • 从零搭建基于SpringBoot的秒杀系统(六):使用RabbitMQ让订单指定时间后失效
  • 从零搭建基于SpringBoot的秒杀系统(七):高并发导致超卖问题分析处理
  • 从零搭建基于SpringBoot的秒杀系统(八):通过分布式锁解决多线程导致的问题
  • 读《世界是数字的》有感
  • 面试官问我:什么是静态代理?什么是动态代理?注解、反射你会吗?
  • redis入门到精通系列(十):springboot集成redis及redis工具类的编写
  • css3延时动画
  • redis入门到精通系列(十一):redis的缓存穿透、缓存击穿以及缓存雪崩详解
  • 子数组最大值设计02
  • redis入门到精通系列(十二):看完这一篇文章别再说不懂布隆过滤器
  • 如何用SpringBoot(2.3.3版本)快速搭建一个项目?文末有小彩蛋
  • Linux上find命令详解
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 「面试题」如何实现一个圣杯布局?
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【Leetcode】104. 二叉树的最大深度
  • Angular4 模板式表单用法以及验证
  • Angular6错误 Service: No provider for Renderer2
  • Angular数据绑定机制
  • iOS 系统授权开发
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • scrapy学习之路4(itemloder的使用)
  • 阿里云前端周刊 - 第 26 期
  • 从PHP迁移至Golang - 基础篇
  • 和 || 运算
  • 技术发展面试
  • 利用jquery编写加法运算验证码
  • 前端面试总结(at, md)
  • 数组大概知多少
  • 推荐一个React的管理后台框架
  • 网络应用优化——时延与带宽
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 原生Ajax
  • 在Unity中实现一个简单的消息管理器
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (06)Hive——正则表达式
  • (1)虚拟机的安装与使用,linux系统安装
  • (4)(4.6) Triducer
  • (C语言)二分查找 超详细
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (SpringBoot)第二章:Spring创建和使用
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (分布式缓存)Redis哨兵
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .net(C#)中String.Format如何使用
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件