秒杀项目之秒杀商品操作

一、秒杀商品显示

1、使用生成器生成对应的表

记得在每个mapper类加入注解供spring扫描:

@Repository

2、后端写得到秒杀商品的方法

①、建实体类vo

用于连表查询,得到商品名字

package com.example.seckill.vo;

import com.example.seckill.pojo.SeckillGoods;
import lombok.Data;

@Data
public class SeckillGoodsVo extends SeckillGoods {
    
    private String goodsName;
}

②、在SexkillGoodsMapper.xml文件中定义sql

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.seckill.mapper.SeckillGoodsMapper">

    <select id="queryAll" resultType="com.example.seckill.vo.SeckillGoodsVo">
        select sg.*,
               g.goods_name
        from t_seckill_goods sg,
             t_goods g
        where sg.goods_id = g.gid;
    </select>
</mapper>

③、在mapper中定义

package com.example.seckill.mapper;

import com.example.seckill.pojo.SeckillGoods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.seckill.vo.SeckillGoodsVo;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * <p>
 * 秒杀商品信息表 Mapper 接口
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@Repository
public interface SeckillGoodsMapper extends BaseMapper<SeckillGoods> {

    List<SeckillGoodsVo> queryAll();
}

④、service层与controller层

service:

ISeckillGoodsService:

package com.example.seckill.service;

import com.example.seckill.pojo.SeckillGoods;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.vo.SeckillGoodsVo;

import java.util.List;

/**
 * <p>
 * 秒杀商品信息表 服务类
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
public interface ISeckillGoodsService extends IService<SeckillGoods> {

    ResponseResult<List<SeckillGoodsVo>> queryAll();
}

SeckillGoodsServiceImpl:

package com.example.seckill.service.impl;

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

import java.util.List;

/**
 * <p>
 * 秒杀商品信息表 服务实现类
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@Service
public class SeckillGoodsServiceImpl extends ServiceImpl<SeckillGoodsMapper, SeckillGoods> implements ISeckillGoodsService {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Override
    public ResponseResult<List<SeckillGoodsVo>> queryAll() {
       List<SeckillGoodsVo> list= seckillGoodsMapper.queryAll();
        return ResponseResult.success(list);
    }
}

controller:

SeckillGoodsController:

package com.example.seckill.controller;

import com.example.seckill.service.ISeckillGoodsService;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.vo.SeckillGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 * 秒杀商品信息表 前端控制器
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@RestController
@RequestMapping("/seckillGoods")
public class SeckillGoodsController {

    @Autowired
    private ISeckillGoodsService seckillGoodsService;

    @RequestMapping("/queryAll")
    public ResponseResult<List<SeckillGoodsVo>> queryAll(){
        return seckillGoodsService.queryAll();
    }

}

得到秒杀商品数据:

3、前端显示数据

 ①、编辑跳转秒杀界面

goodList.ftl:

<!DOCTYPE html>
<html lang="en">
<head>
    <#include "../common/head.ftl">
    <style>
        .layui-this{
            background: deepskyblue !important;
        }
    </style>

</head>
<body class="layui-container layui-bg-orange">
<div class="layui-tab">
    <ul class="layui-tab-title">
        <li class="layui-this">普通商品</li>
        <li>秒杀商品</li>
    </ul>
    
        <#--    普通商品-->
    <div class="layui-tab-content">
        <div class="layui-tab-item layui-show">
            <div class="layui-form-item">
                <label class="layui-form-label">搜索栏</label>
                <div class="layui-input-inline">
                    <input type="text" id="normal_name" name="text" placeholder="请输入搜索内容" class="layui-input">
                </div>
                <div class="layui-input-inline">
                    <button class="layui-btn layui-btn-primary" id="normal_search">🔍</button>
                    <button class="layui-btn layui-btn-primary" id="normal_add">增加</button>
                </div>
            </div>
            <table id="normal_goods" lay-filter="normal_goods"></table>
            <script type="text/html" id="button_1">
                <a class="layui-btn layui-btn-xs" lay-event="normal_del">删除</a>
                <a class="layui-btn layui-btn-xs" lay-event="normal_edit">编辑</a>
            </script>
        </div>
        
            <#--秒杀界面-->
            <div class="layui-tab-item">
                <div class="layui-form-item">
                    <label class="layui-form-label">搜索栏</label>
                    <div class="layui-input-inline">
                        <input type="text" id="seckill_name" name="text" placeholder="请输入搜索内容" class="layui-input">
                    </div>
                    <div class="layui-input-inline">
                        <button class="layui-btn layui-btn-primary" id="seckill_search">🔍</button>
                        <button class="layui-btn layui-btn-primary" id="seckill_add">增加</button>
                    </div>
                </div>
            <table id="seckill_goods" lay-filter="seckill_goods"></table>
            </div>
        </div>
    </div>
</div>
<#--引入js-->
<script src="/static/asset/js/project/goodsList.js"></script>
</body>
</html>

②、获取数据

goodList.js:

    // 秒杀商品
    let seckill_table=table.render({
        elem: '#seckill_goods'
        ,height: 500
        ,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:80, sort: true}
            ,{field: 'goodsId', title: '商品名字id'}
            ,{field: 'seckillPrice', title: '秒杀价格'}
            ,{field: 'stockCount', title: '秒杀库存'}
            ,{field: 'startDate', title: '活动开始时间'}
            ,{field: 'endDate', title: '活动结束时间'}
            ,{field: 'goodsName', title: '商品名称'}
        ]]
    });

呈现界面:

 二、秒杀商品添加

1、后端:接收前端添加秒杀商品的数据

①、实体类vo:SeckillGoodsVo

  private List<Map<String,Object>> goods;

修改实体类时间的类型:SeckillGoods

    @ApiModelProperty("秒杀开始时间")
    @TableField("start_date")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Timestamp startDate;

    @ApiModelProperty("秒杀结束时间")
    @TableField("end_date")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Timestamp endDate;

②、mapper层:SeckillGoodsMapper

    int addGoods(SeckillGoodsVo seckillGoodsVo);

③、mapper.xml层:SeckillGoodsMapper

批量插入秒杀商品的sql语句:

    <insert id="addGoods">
        insert into t_seckill_goods(goods_id, seckill_price, stock_count, start_date, end_date)
        values
        <foreach collection="goods" item="g" separator=",">
            (#{g.gid},#{g.goodsPrice},#{g.goodsStock},#{startDate},#{endDate})
        </foreach>
    </insert>

④、service层

ISeckillGoodsService:

  ResponseResult<List<SeckillGoodsVo>> addGoods(SeckillGoodsVo seckillGoodsVo);

SeckillGoodsServiceImpl:

    @Override
    public ResponseResult<List<SeckillGoodsVo>> addGoods(SeckillGoodsVo seckillGoodsVo) {
        int goods=seckillGoodsMapper.addGoods(seckillGoodsVo);
        return ResponseResult.success(goods);
    }

⑤、controller层

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

2、前端

①、定义数据与刷新、添加

goodsList.js:

var layer,row,seckill_table

    // 添加秒杀商品
    $("#seckill_add").click(()=>{
         layer.open({
             type:2,
             content: '/goods/SeckillGoodsOperate',
             area: ['800px','600px']
         })
    })

// 秒杀商品刷新
var seckill_reload = ()=> {
    seckill_table.reload({
        page:{
            curr:1 //current
        }
    });
}
var layer,row,seckill_table


layui.define(()=>{
    let table=layui.table
    layer=layui.layer
    let $=layui.jquery


    let normal_table=table.render({
        elem: '#normal_goods'
        ,height: 500
        ,url: '/goods/queryAll' //数据接口
        ,page: true //开启分页
        ,parseData(res){ //res 即为原始返回的数据
            return {
                "code": res.code===200?0:1, //解析接口状态
                "msg": res.message, //解析提示文本
                "count": res.total, //解析数据长度
                "data": res.data //解析数据列表
            };
        },
        //用于对分页请求的参数:page、limit重新设定名称
        request: {
            pageName: 'page' //页码的参数名称,默认:page
            ,limitName: 'rows' //每页数据量的参数名,默认:limit
        }
        ,cols: [[ //表头
            {field: 'gid', title: '商品编号', width:80, sort: true, fixed: 'left'}
            ,{field: 'goodsName', title: '商品名字'}
            ,{field: 'goodsTitle', title: '商品标题'}
            ,{field: 'goodsImg',
              title: '商品图片',
              width:200,
              templet: (goods)  => `<b onmouseover='showImg("${goods.goodsImg}",this)'>` + goods.goodsImg + `</b>` }
            ,{field: 'goodsDetail', title: '商品详情'}
            ,{field: 'goodsPrice', title: '商品价格', sort: true}
            ,{field: 'goodsStock', title: '商品库存', sort: true}
            ,{field: 'operate', title: '商品操作',toolbar: '#button_1'}
        ]]
    });

    // 刷新表格
    let reloadTable=()=>{
        let goodsName=$("#normal_value").val()
        // 【JS】自动化渲染的重载,重载表格
        normal_table.reload({
            where: {
                //设定异步数据接口的额外参数,height: 300
                goodsName
            },
            page:{
                curr:1 //current
            }
        });
    }
    // 搜索
    $("#normal_search").click(reloadTable)

    // 增加
    $("#normal_add").click(()=>{
        row = null
        openDialog()
    })

    //工具条事件
    table.on('tool(normal_goods)', function(obj) { //注:tool 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值"
        let data = obj.data; //获得当前行数据
        let layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值)
        let tr = obj.tr; //获得当前行 tr 的 DOM 对象(如果有的话)

        if (layEvent === 'normal_del') { //删除
            row = data//获得当前行的数据
            let url="/goods/del/"+data.gid
            layer.confirm('确定删除吗?',{title:'删除'}, function(index){
                //向服务端发送删除指令og
                $.getJSON(url,{gid:data.gid}, function(ret){
                    layer.close(index);//关闭弹窗
                    reloadTable()
                });
                layer.close(index);//关闭弹窗

            });
        }
        if (layEvent === 'normal_edit') { //编辑
            row = data
            openDialog()
        }
    })


    // 页面弹出
    let openDialog=()=>{
        // 如果是iframe层
        layer.open({
            type: 2,
            content: '/goods/goodsOperate', //这里content是一个URL,如果你不想让iframe出现滚动条,你还可以content: ['http://sentsin.com', 'no']
            area:['800px','600px'],
            btn: ['确定','取消'],
            yes(index,layero){
                let url="/goods/insert"
                // 拿到表格数据
                let data=$(layero).find("iframe")[0].contentWindow.getFormData()
                if(row) {
                    url="/goods/edit"
                }
                $.ajax({
                    url,
                    data,
                    datatype: "json",
                    success(res){
                        layer.closeAll()
                        reloadTable()
                        layer.msg(res.message)
                    }
                })

            }
        });
    }
// -------------------------秒杀商品-------------------------------------------
    seckill_table=table.render({
        elem: '#seckill_goods'
        ,height: 500
        ,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:80, sort: true}
            ,{field: 'goodsId', title: '商品名字id'}
            ,{field: 'seckillPrice', title: '秒杀价格'}
            ,{field: 'stockCount', title: '秒杀库存'}
            ,{field: 'startDate', title: '活动开始时间'}
            ,{field: 'endDate', title: '活动结束时间'}
            ,{field: 'goodsName', title: '商品名称'}
        ]]
    });
    // 添加秒杀商品
    $("#seckill_add").click(()=>{
         layer.open({
             type:2,
             content: '/goods/SeckillGoodsOperate',
             area: ['800px','600px']
         })
    })

})


// 图片显示
let showImg = (src,obj)=> {
    layer.tips(`<img src="${src}" width="100px">`, obj);
}

// 秒杀商品刷新
var seckill_reload = ()=> {
    seckill_table.reload({
        page:{
            curr:1 //current
        }
    });
}

②、增加秒杀商品弹出页面样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/static/asset/js/layui/css/layui.css" media="all">
</head>
<body>
<div style="padding:15px 0px;">
    <div class="layui-condition">
        <form id="fm" name="fm" action="/" method="post" class="layui-form">
            <div class="layui-form-item">
                <div class="layui-inline">
                    <label class="layui-form-label" style="width: 100px;text-align: left;">秒杀活动时间:</label>
                    <div class="layui-input-inline" style="width:280px;">
                        <input type="text" class="layui-input" id="dt">
                    </div>
                    <div class="layui-input-inline">
                        <button class="layui-btn" id="btn_save" type="button">
                            <i class="fa fa-search fa-right"></i>保 存
                        </button>
                    </div>
                </div>
            </div>
        </form>
    </div>
    <div class="layui-fluid" style="margin-top:-18px;">
        <table id="tb_goods" class="layui-table" lay-filter="tb_goods" style="margin-top:-5px;"></table>
    </div>
</div>
<script src="/static/asset/js/layui/layui.js"></script>
<script src="/static/asset/js/project/seckillGoodsOperate.js"></script>
</body>
</html>

③、实现增加秒杀商品

seckillGoodsOperate.js:

layui.define(()=>{
    let table=layui.table
    let laydate = layui.laydate
    let $=layui.jquery
    let layer=layui.layer

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

    // 构建时间选择器
        //执行一个laydate实例
    laydate.render({
        elem: '#dt', //指定元素
        type: "datetime",
        range: "~"
    });

        $("#btn_save").click(()=>{
            // 获取时间
            let val=$("#dt").val()
            if(!val){
                layer.msg("请选择时间")
                return
            }
            // 解析时间2022-2-2 ~2022-5-2
            let startDate=new Date(val.split("~")[0]).getTime()
            let endDate=new Date(val.split("~")[1]).getTime()
            // 获得选中的普通商品,获取选中行的数据
            let rows= table.checkStatus('tb_goods').data; //idTest 即为基础参数 id 对应的值
            if(!rows||rows.length===0){
                layer.msg("请选择数据")
                return
            }

            layer.prompt(function(value, index, elem){
                // 修改每个商品的数量
                rows.forEach(e=>{
                    e.goodsStock=value
                })
                let data={
                    startDate,
                    endDate,
                    goods:rows
                }
                // 访问后台的秒杀商品的接口
                $.ajax({
                    url: "/seckillGoods/add",
                    contentType:'application/json',
                    data: JSON.stringify(data),
                    datatype:"json",//返回类型
                    type:"post",
                    success(res){
                        parent.seckill_reload()
                        layer.closeAll()
                        parent.layer.closeAll()
                        layer.msg(res.message)
                    }
                })
            });


        })

})

④、展示结果

 增加成功:

 三、秒杀商品的操作

1、后端操作秒杀单个商品详情

①、mapper层

SeckillGoodsMapper:

    Map<String,Object> querySeckillGoodsById(Long id);

mapper.xml文件:SeckillGoodsMapper.xml

    <select id="querySeckillGoodsById" resultType="map">
        select sg.id,
               sg.goods_id,
               sg.seckill_price,
               sg.stock_count,
               sg.start_date,
               sg.end_date,
               g.goods_img,
               g.goods_title,
               g.goods_detail,
               g.goods_name,
               (
                   case
                       when current_timestamp &lt; sg.start_date then 0
                       when (current_timestamp between sg.start_date and sg.end_date) then 1
                       when current_timestamp &gt; sg.end_date then 2
                       end
                   ) goods_status
        from t_goods g,
             t_seckill_goods sg
        where g.gid = sg.goods_id
          and sg.id = #{0}
    </select>

②、service层

ISeckillGoodsService:

    Map<String,Object> querySeckillGoodsById(Long id);

SeckillGoodsServiceImpl:

    @Override
    public Map<String, Object> querySeckillGoodsById(Long id) {
        return seckillGoodsMapper.querySeckillGoodsById(id);
    }

③、controller层:SeckillGoodsController 

package com.example.seckill.controller;

import com.example.seckill.service.ISeckillGoodsService;
import com.example.seckill.util.response.ResponseResult;
import com.example.seckill.vo.SeckillGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

/**
 * <p>
 * 秒杀商品信息表 前端控制器
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@Controller
@RequestMapping("/seckillGoods")
public class SeckillGoodsController {

    @Autowired
    private ISeckillGoodsService seckillGoodsService;

//    返回json
    @ResponseBody
    @RequestMapping("/queryAll")
    public ResponseResult<List<SeckillGoodsVo>> queryAll(){
        return seckillGoodsService.queryAll();
    }

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

//    正常跳转界面
@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、前端展示

①、在goodsList.js增加列的操作

{
                field: '', title: '操作', width: 140,
                templet: function (d) {
                    return `<div>
                    <a class="layui-btn layui-btn-xs layui-btn-danger">删除</a>
                    <a href="/seckillGoods/query/${d.id}" class="layui-btn layui-btn-xs layui-btn-normal">秒杀</a>
                    </div>`;
                }
            }

②、添加秒杀详情界面 :goodsSkill.ftl

<!DOCTYPE html>
<html lang="en">
<head>
    <#include "../common/head.ftl"/>
</head>
<body>
<table style="position: absolute;top:-10px;" class="layui-table" border="1" cellpadding="0" cellspacing="0">
    <tr>
        <td style="width:120px;">商品图片</td>
        <td>
            <img src="${goods['goods_img']}" alt="">
        </td>
    </tr>
    <tr>
        <td>商品名称</td>
        <td>
            ${goods['goods_name']}
        </td>
    </tr>
    <tr>
        <td>商品标题</td>
        <td>
            ${goods['goods_title']}
        </td>
    </tr>
    <tr>
        <td>商品价格</td>
        <td>
            ${goods['seckill_price']}
        </td>
    </tr>
    <tr>
        <td>开始时间</td>
        <td>
            <div style="position: relative;${(goods['goods_status']==1)?string('top:10px;','')}">
                ${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>
                    活动热卖中
                    <div style="position:relative;top:-10px;float:right;">
                        <input type="hidden" id="goodsId" value="${goods['goods_id']}" name="goodsId"/>
                        <button class="layui-btn" id="buy">立即抢购</button>
                    </div>
                <#else>
                    活动已结束
                </#if>
            </div>
        </td>
    </tr>
</table>
<script src="/static/asset/js/project/goodsSeckill.js"></script>
</body>
</html>

③、实现:goodsSkill.js

let layer, form, $;

layui.define(() => {
    layer = layui.layer
    form = layui.form
    $ = layui.jquery

    $('#buy').click(() => {
        $.ajax({
            url: '/seckillOrder/addOrder',
            data: {goodsId: $('#goodsId').val()},
            dataType: 'json',
            type: 'post',
            async: false,
            success: function (rs) {
                if (rs.code === 200)
                    layer.msg(rs.message)
                else
                    layer.msg(rs.message)
            }
        })
    });

})

④、展示效果

点击秒杀: 

3、后端操作秒杀抢购功能

①、导入雪花id工具包:SnowFlake 

package com.example.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();
    }

}

②、service层

ISeckillOrderService :

package com.example.seckill.service;

import com.example.seckill.pojo.SeckillOrder;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.seckill.pojo.User;
import com.example.seckill.util.response.ResponseResult;

/**
 * <p>
 * 秒杀订单信息表 服务类
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
public interface ISeckillOrderService extends IService<SeckillOrder> {

    ResponseResult<?> addOrder(Long goodsId, User user);
}

SeckillOrderServiceImpl :

package com.example.seckill.service.impl;

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

/**
 * <p>
 * 秒杀订单信息表 服务实现类
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@Service
public class SeckillOrderServiceImpl extends ServiceImpl<SeckillOrderMapper, SeckillOrder> 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<SeckillGoods>().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<SeckillOrder>().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<SeckillGoods>().eq("goods_id", goodsId).setSql("stock_count=stock_count-1"));
//       根据商品编号查询对应的商品(拿名字)
        Goods goodsInfo = goodsMapper.selectOne(new QueryWrapper<Goods>().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();
    }
}

③、controller层

SeckillOrderController :

package com.example.seckill.controller;

import com.example.seckill.pojo.User;
import com.example.seckill.service.ISeckillOrderService;
import com.example.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;

/**
 * <p>
 * 秒杀订单信息表 前端控制器
 * </p>
 *
 * @author lv
 * @since 2022-03-19
 */
@RestController
@RequestMapping("/seckillOrder")
public class SeckillOrderController {

    @Autowired
    private ISeckillOrderService seckillOrderService;

    @RequestMapping("/addOrder")
    public ResponseResult<?> addOrder(Long goodsId, User user){
        return seckillOrderService.addOrder(goodsId,user);
    }


}

④、呈现结果

限购次数:

 

 本期内容结束,下期内容更完善!!!!!!!!!!!!!!!!!!!!!1

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值