目录
一、秒杀商品显示
1、MybatisPlusGenerator生成相关数据
2、后台编写
3、前台编写
4、运行测试
二、秒杀商品增加
1、增加弹出层
2、增加秒杀商品
(1)前台编写
(2)后台编写
三、秒杀商品
1、后端编写
2、前台编写
四、秒杀商品下单(立即抢购)
1、前台js编写
2、后端编写
3、页面展示
给所有mapper类增加注解:
@Repository
(1)编写SeckillGoodsVo类
①、因为秒杀商品的表并不能显示商品名称,因此编写Vo类,
②、 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)访问查看是否能获得数据
(1)界面编写
①、goodsList.ftl
<#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: '结束时间'},
]]
});
(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'] }) })
(4)、界面展示
①、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({})
①、SeckillGoodsVo增加相关内容
private List
②、SeckillGoods
修改开始时间和结束时间的类型
③、连表增加秒杀商品
Ⅰ、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);
}
⑥、页面展示
增加界面:
成功后数据:
(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;
}
(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>
活动已结束
#if>
②、goodsList.ftl填写跳转方式
{ field: '', title: '操作', width: 140, templet: function (d) { return ``; } }
(2)页面展示
(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)
}
})
});
})
(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);
}
}
(1)购买成功:
(2)重复购买:
本章结束!!!!!