秒杀项目之秒杀商品操作

目录

一、秒杀商品显示

1、MybatisPlusGenerator生成相关数据

2、后台编写 

 3、前台编写

4、运行测试

 二、秒杀商品增加

1、增加弹出层

 2、增加秒杀商品

(1)前台编写

(2)后台编写

三、秒杀商品

1、后端编写

2、前台编写

四、秒杀商品下单(立即抢购)

1、前台js编写

2、后端编写

3、页面展示


一、秒杀商品显示

1、MybatisPlusGenerator生成相关数据

秒杀项目之秒杀商品操作_第1张图片

给所有mapper类增加注解:

@Repository

2、后台编写 

(1)编写SeckillGoodsVo类

①、因为秒杀商品的表并不能显示商品名称,因此编写Vo类,

秒杀项目之秒杀商品操作_第2张图片

②、 SeckillGoodsVo 

package com.mwy.seckill.vo;
import com.mwy.seckill.pojo.SeckillGoods;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
import java.util.Map;
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
//继承秒杀商品
public class SeckillGoodsVo extends SeckillGoods {

//    商品名称
    private String goodsName;

}

(2)增加连表查询以便显示

①、SeckillGoodsMapper

package com.mwy.seckill.mapper;
import com.mwy.seckill.pojo.SeckillGoods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mwy.seckill.vo.SeckillGoodsVo;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;

/**
 * 

* 秒杀商品信息表 Mapper 接口 *

* * @author mwy * @since 2022-03-20 */ @Repository public interface SeckillGoodsMapper extends BaseMapper { List queryAll(); }

②、SeckillGoodsMapper.xml





    


(3)编写service层

①、ISeckillGoodsService 接口

package com.mwy.seckill.service;
import com.mwy.seckill.pojo.SeckillGoods;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mwy.seckill.util.response.ResponseResult;
import com.mwy.seckill.vo.SeckillGoodsVo;
import java.util.List;
/**
 * 

* 秒杀商品信息表 服务类 *

* * @author mwy * @since 2022-03-20 */ public interface ISeckillGoodsService extends IService { ResponseResult> queryAll(); }

②、实现接口

package com.mwy.seckill.service.impl;
import com.mwy.seckill.pojo.SeckillGoods;
import com.mwy.seckill.mapper.SeckillGoodsMapper;
import com.mwy.seckill.service.ISeckillGoodsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mwy.seckill.util.response.ResponseResult;
import com.mwy.seckill.vo.SeckillGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * 

* 秒杀商品信息表 服务实现类 *

* * @author mwy * @since 2022-03-20 */ @Service public class SeckillGoodsServiceImpl extends ServiceImpl implements ISeckillGoodsService { @Autowired private SeckillGoodsMapper seckillGoodsMapper; @Override public ResponseResult> queryAll() { return ResponseResult.success(seckillGoodsMapper.queryAll()); } }

(4)controller层

①、SeckillGoodsController :

package com.mwy.seckill.controller;
import com.mwy.seckill.service.ISeckillGoodsService;
import com.mwy.seckill.util.response.ResponseResult;
import com.mwy.seckill.vo.SeckillGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 

* 秒杀商品信息表 前端控制器 *

* * @author mwy * @since 2022-03-20 */ @RestController @RequestMapping("/seckillGoods") public class SeckillGoodsController { @Autowired private ISeckillGoodsService seckillGoodsService; @ResponseBody @RequestMapping("/queryAll") public ResponseResult> queryAll() { return seckillGoodsService.queryAll(); } }

(5)访问查看是否能获得数据

秒杀项目之秒杀商品操作_第3张图片

 3、前台编写

(1)界面编写

①、goodsList.ftl

秒杀项目之秒杀商品操作_第4张图片




    <#include "../common/head.ftl">
    



  • 普通商品
  • 秒杀商品
<#--普通商品展示--> <#--搜索、新增-->
<#--普通商品展示表格-->
<#--相关操作按钮 ,工具条模板-->
<#--秒杀商品展示-->
<#--增加--> <#--秒杀商品展示表格-->

(2)js编写

①、goodsList.js增加相关内容

//秒杀商品展示
    let seckill = table.render({
        elem: '#seckill_goods',
        height: 550,
        url: '/seckillGoods/queryAll', //数据接口
        parseData(res) { //res 即为原始返回的数据
            return {
                "code": res.code === 200 ? 0 : 1, //解析接口状态
                "msg": res.message, //解析提示文本
                "count": res.total, //解析数据长度
                "data": res.data //解析数据列表
            }
        },
        cols: [[ //表头
            {field: 'id', title: '秒杀商品编号', width: 100, sort: true, fixed: 'left'},
            {field: 'goodsId', title: '商品id'},
            {field: 'seckillPrice', title: '秒杀商品价格'},
            {field: 'stockCount', title: '秒杀商品库存'},
            {field: 'startDate', title: '开始时间'},
            {field: 'endDate', title: '结束时间'},

        ]]
    });

4、运行测试

(1)界面展示:

秒杀项目之秒杀商品操作_第5张图片

 二、秒杀商品增加

1、增加弹出层

(1)、增加SeckillGoodsOperate.ftl




    
    


(2)、seckillGoodsOperate.js

layui.define(() => {
    let table = layui.table
    let $ = layui.jquery
    let layer = layui.layer
    let laydate = layui.laydate
    //初始化日期选择器
    laydate.render({
        elem: '#dt', //指定元素
        type: "datetime",
        range: "~"//开启左右面板范围选择,~:自定义分割字符
    });

    //读取普通商品
    let tb_goods = table.render({
        elem: '#tb_goods',
        height: 550,
        url: '/goods/queryAll', //数据接口
        page: true,//开启分页
        parseData(res) { //res 即为原始返回的数据
            return {
                "code": res.code === 200 ? 0 : 1, //解析接口状态
                "msg": res.message, //解析提示文本
                "count": res.total, //解析数据长度
                "data": res.data //解析数据列表
            }
        },
        request: {
            pageName: 'page', //页码的参数名称,默认:page
            limitName: 'rows' //每页数据量的参数名,默认:limit
        },
        cols: [[ //表头
            {field: '', type: 'checkbox'},//多选框
            {field: 'gid', title: '商品编号', width: 100, sort: true},
            {field: 'goodsName', title: '商品名称'},
            {field: 'goodsTitle', title: '商品标题'},
            {field: 'goodsDetail', title: '商品详细'},
            {field: 'goodsPrice', title: '商品价格', sort: true},
            {field: 'goodsStock', title: '商品库存', sort: true}
        ]]
    });

    //设置保存按钮的点击事件
    $("#btn_save").click(() => {
        console.log("---------")
    })
})

(3)、给秒杀商品“增加按钮”增加点击事件

①、goodsList.js

$("#seckill_add").click(() => {
        layer.open({
            type: 2,
            content: "/goods/SeckillGoodsOperate",
            area: ['1000px', '600px']
        })
    })

秒杀项目之秒杀商品操作_第6张图片

(4)、界面展示

秒杀项目之秒杀商品操作_第7张图片

 2、增加秒杀商品

(1)前台编写

①、seckillGoodsOperate.js

let xx = parent
layui.define(() => {

    let table = layui.table
    let $ = layui.jquery
    let layer = layui.layer//选择时间使用
    let laydate = layui.laydate//日期选择器使用

    //初始化日期选择器
    laydate.render({
        elem: '#dt', //指定元素
        type: "datetime",
        range: "~"//开启左右面板范围选择,~:自定义分割字符
    });

    //读取普通商品
    let tb_goods = table.render({
        elem: '#tb_goods',
        height: 550,
        url: '/goods/queryAll', //数据接口
        page: true,//开启分页
        parseData(res) { //res 即为原始返回的数据
            return {
                "code": res.code === 200 ? 0 : 1, //解析接口状态
                "msg": res.message, //解析提示文本
                "count": res.total, //解析数据长度
                "data": res.data //解析数据列表
            }
        },
        request: {
            pageName: 'page', //页码的参数名称,默认:page
            limitName: 'rows' //每页数据量的参数名,默认:limit
        },
        cols: [[ //表头
            {field: '', type: 'checkbox'},//多选框
            {field: 'gid', title: '商品编号', width: 100, sort: true},
            {field: 'goodsName', title: '商品名称'},
            {field: 'goodsTitle', title: '商品标题'},
            {field: 'goodsDetail', title: '商品详细'},
            {field: 'goodsPrice', title: '商品价格', sort: true},
            {field: 'goodsStock', title: '商品库存', sort: true}
        ]]
    });

    //设置保存按钮的点击事件
    $("#btn_save").click(() => {
        //获取时间
        let time = $("#dt").val().trim()
        //获得选中的普通商品
        let rows = table.checkStatus('tb_goods').data
        if (!time) {//没有选择时间
            layer.msg("请选择时间")
            return false
        }
        //判断用户是否选中商品
        if (!rows.length) {//并没有选中商品
            layer.msg("请选择需要参加秒杀活动的商品")
            return false
        }
        //输入层
        layer.prompt(function (value, index, elem) {
            rows.forEach(e => {
                e.goodsStock = value
            })

            let params = {
                //活动开启时间
                startDate: new Date(time.split("~")[0]).getTime(),
                //活动结束时间
                endDate: new Date(time.split("~")[1]).getTime(),
                goods: rows
            }
            //访问后台秒杀商品的添加接口
            $.ajax({
                url: '/seckillGoods/add',//后台增加接口
                data: JSON.stringify(params),//将data转成json数据
                dataType: 'json',//返回json类型
                contentType: 'application/json',//指定json类型
                type: 'post',
                async: false,
                //回调函数
                success: function (res) {
                    layer.closeAll();//关闭窗口
                    parent.layer.closeAll()//关闭父元素
                    layer.msg(res.message)
                    parent.seckill_reload();//刷新
                },
                error: function (err) {
                    console.log(err);
                }
            });
        })
    })
})

②、goodsList.js

//刷新秒杀表格
var seckill_reload = () => {
    seckill.reload({})

秒杀项目之秒杀商品操作_第8张图片

 秒杀项目之秒杀商品操作_第9张图片

(2)后台编写

①、SeckillGoodsVo增加相关内容

private List> goods;

秒杀项目之秒杀商品操作_第10张图片

②、SeckillGoods

修改开始时间和结束时间的类型

秒杀项目之秒杀商品操作_第11张图片

③、连表增加秒杀商品

Ⅰ、SeckillGoodsMapper

int addGoods(SeckillGoodsVo seckillGoodsVo);

Ⅱ、SeckillGoodsMapper.xml


        insert into t_seckill_goods(goods_id, seckill_price, stock_count, start_date, end_date)
        values
        
            (#{g.gid},#{g.goodsPrice},#{g.goodsStock},#{startDate},#{endDate})
        
    

④、service层

Ⅰ、ISeckillGoodsService接口

ResponseResult addGoods(SeckillGoodsVo seckillGoodsVo);

Ⅱ、实现接口SeckillGoodsServiceImpl

 @Override
    public ResponseResult addGoods(SeckillGoodsVo seckillGoodsVo) {
        if (seckillGoodsMapper.addGoods(seckillGoodsVo) > 0) {
            return ResponseResult.success();
        }
        return ResponseResult.failure(ResponseResultCode.UNKNOWN);
    }

⑤、Controller层

Ⅰ、SeckillGoodsController

 @ResponseBody
    @RequestMapping("/add")
    public ResponseResult addGoods(@RequestBody SeckillGoodsVo seckillGoodsVo) {
        return seckillGoodsService.addGoods(seckillGoodsVo);
    }

⑥、页面展示

原始数据:
秒杀项目之秒杀商品操作_第12张图片

增加界面:

秒杀项目之秒杀商品操作_第13张图片

秒杀项目之秒杀商品操作_第14张图片

 成功后数据:

秒杀项目之秒杀商品操作_第15张图片

三、秒杀商品

1、后端编写

(1)连表秒杀商品

①、SeckillGoodsMapper

Map queryById(Long id);

②、SeckillGoodsMapper.xml

 

(2)service层

①、接口ISeckillGoodsService

 Map querySeckillGoodsById(Long id);

②、实现接口SeckillGoodsServiceImpl

 @Override
    public Map querySeckillGoodsById(Long id) {
        return seckillGoodsMapper.queryById(id);
    }

(3)Controller层

①、SeckillGoodsController

@RequestMapping("/query/{id}")
    public ModelAndView querySeckillGoodsById(@PathVariable("id") Long id) {
        ModelAndView mv = new ModelAndView("/goods/goodsSeckill");
        mv.addObject("goods", seckillGoodsService.querySeckillGoodsById(id));
        return mv;
    }

2、前台编写

(1)新增秒杀界面

①、goodsSeckill.ftl




    <#include "../common/head.ftl"/>


商品图片
商品名称 ${goods['goods_name']}
商品标题 ${goods['goods_title']}
商品价格 ${goods['seckill_price']}
开始时间
${goods['start_date']?string("yyyy-MM-dd HH:mm:ss")} - ${goods['end_date']?string("yyyy-MM-dd HH:mm:ss")} <#if goods['goods_status']==0> 活动未开始 <#elseif goods['goods_status']==1> 活动热卖中
<#else> 活动已结束

②、goodsList.ftl填写跳转方式

 {
                field: '', title: '操作', width: 140,
                templet: function (d) {
                    return ``;
                }
            }

秒杀项目之秒杀商品操作_第16张图片

(2)页面展示

秒杀项目之秒杀商品操作_第17张图片

四、秒杀商品下单(立即抢购)

1、前台js编写

(1)goodsSeckill.js

let layer, form, $;

layui.define(() => {
    layer = layui.layer//弹出模块
    form = layui.form//表单模块
    $ = layui.jquery//jQuery模块

    //立即抢购按钮加载事件
    $('#buy').click(() => {
        $.ajax({
            //ajax请求秒杀订单的增加订单方法
            url: '/seckillOrder/addOrder',
            data: {goodsId: $('#goodsId').val()},//携带goodsId
            dataType: 'json',//返回json类型
            type: 'post',//post请求
            async: false,
            success: function (rs) {
                if (rs.code === 200)
                    layer.msg(rs.message)
                else
                    layer.msg(rs.message)
            }
        })
    });

})

2、后端编写

(1)、导入雪花id工具包:SnowFlake 

package com.mwy.seckill.util;
@SuppressWarnings("all")
public class SnowFlake {

    /**
     * 起始的时间戳
     */
    private final static long START_STMP = 1480166465631L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳

    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(2, 3);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            System.out.println(snowFlake.nextId());
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    /**
     * 产生下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }
}

(2)service层

①、接口ISeckillOrderService 

package com.mwy.seckill.service;
import com.mwy.seckill.pojo.SeckillOrder;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mwy.seckill.pojo.User;
import com.mwy.seckill.util.response.ResponseResult;
/**
 * 

* 秒杀订单信息表 服务类 *

* * @author mwy * @since 2022-03-20 */ public interface ISeckillOrderService extends IService { ResponseResult addOrder(Long goodsId, User user); }

②、实现接口SeckillOrderServiceImpl 

package com.mwy.seckill.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.mwy.seckill.exception.BusinessException;
import com.mwy.seckill.mapper.GoodsMapper;
import com.mwy.seckill.mapper.OrderMapper;
import com.mwy.seckill.mapper.SeckillGoodsMapper;
import com.mwy.seckill.pojo.*;
import com.mwy.seckill.mapper.SeckillOrderMapper;
import com.mwy.seckill.service.ISeckillOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mwy.seckill.util.SnowFlake;
import com.mwy.seckill.util.response.ResponseResult;
import com.mwy.seckill.util.response.ResponseResultCode;
import com.mwy.seckill.vo.SeckillGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 * 

* 秒杀订单信息表 服务实现类 *

* * @author mwy * @since 2022-03-20 */ @Service public class SeckillOrderServiceImpl extends ServiceImpl implements ISeckillOrderService { @Autowired private SeckillGoodsMapper seckillGoodsMapper; @Autowired private GoodsMapper goodsMapper; @Autowired private OrderMapper orderMapper; @Transactional(rollbackFor = Exception.class) @Override public ResponseResult addOrder(Long goodsId, User user) { // 下单前判断库存数 SeckillGoods goods = seckillGoodsMapper.selectOne(new QueryWrapper().eq("goods_id", goodsId)); if (goods == null) { throw new BusinessException(ResponseResultCode.SECKILL_ORDER_ERROR); } if (goods.getStockCount() < 1) { throw new BusinessException(ResponseResultCode.SECKILL_ORDER_ERROR); } // 限购 SeckillOrder one = this.getOne(new QueryWrapper().eq("user_id", user.getId()).eq("goods_id", goodsId)); if (one != null) { throw new BusinessException(ResponseResultCode.SECKILL_ORDER_EXISTS_ERROR); } // 库存减一 int i = seckillGoodsMapper.update(null, new UpdateWrapper().eq("goods_id", goodsId).setSql("stock_count=stock_count-1")); // 根据商品编号查询对应的商品(拿名字) Goods goodsInfo = goodsMapper.selectOne(new QueryWrapper().eq("gid", goodsId)); // 生成订单(生成雪花id) SnowFlake snowFlake = new SnowFlake(5, 9); long id = snowFlake.nextId(); //生成对应的订单 Order normalOrder = new Order(); normalOrder.setOid(id); normalOrder.setUserId(user.getId()); normalOrder.setGoodsId(goodsId); normalOrder.setGoodsName(goodsInfo.getGoodsName()); normalOrder.setGoodsCount(1); normalOrder.setGoodsPrice(goods.getSeckillPrice()); orderMapper.insert(normalOrder); // 生成秒杀订单 SeckillOrder seckillOrder = new SeckillOrder(); seckillOrder.setUserId(user.getId()); seckillOrder.setOrderId(normalOrder.getOid()); seckillOrder.setGoodsId(goodsId); this.save(seckillOrder); return ResponseResult.success(); } }

(3)Controller 层

①、SeckillOrderController 

package com.mwy.seckill.controller;
import com.mwy.seckill.pojo.User;
import com.mwy.seckill.service.ISeckillOrderService;
import com.mwy.seckill.util.response.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 

* 秒杀订单信息表 前端控制器 *

* * @author mwy * @since 2022-03-20 */ @RestController @RequestMapping("/seckillOrder") public class SeckillOrderController { @Autowired private ISeckillOrderService seckillOrderService; @RequestMapping("/addOrder") public ResponseResult addOrder(Long goodsId, User user) { return seckillOrderService.addOrder(goodsId, user); } }

3、页面展示

(1)购买成功:

秒杀项目之秒杀商品操作_第18张图片

(2)重复购买:

秒杀项目之秒杀商品操作_第19张图片

本章结束!!!!! 

你可能感兴趣的:(java,spring,boot,idea,layui)