记得在每个mapper类加入注解供spring扫描:
@Repository2、后端写得到秒杀商品的方法 ①、建实体类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
③、在mapper中定义select sg.*, g.goods_name from t_seckill_goods sg, t_goods g where sg.goods_id = g.gid;
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; @Repository public interface SeckillGoodsMapper extends baseMapper④、service层与controller层 service:{ List queryAll(); }
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; public interface ISeckillGoodsService extends IService{ ResponseResult > 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; @Service public class SeckillGoodsServiceImpl extends ServiceImplcontroller:implements ISeckillGoodsService { @Autowired private SeckillGoodsMapper seckillGoodsMapper; @Override public ResponseResult > queryAll() { List
list= seckillGoodsMapper.queryAll(); return ResponseResult.success(list); } }
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;
@RestController
@RequestMapping("/seckillGoods")
public class SeckillGoodsController {
@Autowired
private ISeckillGoodsService seckillGoodsService;
@RequestMapping("/queryAll")
public ResponseResult> queryAll(){
return seckillGoodsService.queryAll();
}
}
得到秒杀商品数据:
3、前端显示数据 ①、编辑跳转秒杀界面 goodList.ftl:
<#include "../common/head.ftl">
.layui-this{
background: deepskyblue !important;
}
普通商品
秒杀商品
<#-- 普通商品-->
<#--秒杀界面-->
<#--引入js-->
②、获取数据
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:SeckillGoodsVoprivate List修改实体类时间的类型: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语句:
④、service层insert into t_seckill_goods(goods_id, seckill_price, stock_count, start_date, end_date) values (#{g.gid},#{g.goodsPrice},#{g.goodsStock},#{startDate},#{endDate})
ISeckillGoodsService:
ResponseResult> addGoods(SeckillGoodsVo seckillGoodsVo);
SeckillGoodsServiceImpl:
@Override
public ResponseResult> addGoods(SeckillGoodsVo seckillGoodsVo) {
int goods=seckillGoodsMapper.addGoods(seckillGoodsVo);
return ResponseResult.success(goods);
}
⑤、controller层
@RequestMapping("/add")
public ResponseResult> 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) => `` + goods.goodsImg + `` }
,{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(``, obj);
}
// 秒杀商品刷新
var seckill_reload = ()=> {
seckill_table.reload({
page:{
curr:1 //current
}
});
}
②、增加秒杀商品弹出页面样式
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:
MapquerySeckillGoodsById(Long id);
mapper.xml文件:SeckillGoodsMapper.xml
②、service层
ISeckillGoodsService:
MapquerySeckillGoodsById(Long id);
SeckillGoodsServiceImpl:
@Override
public Map 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;
@Controller
@RequestMapping("/seckillGoods")
public class SeckillGoodsController {
@Autowired
private ISeckillGoodsService seckillGoodsService;
// 返回json
@ResponseBody
@RequestMapping("/queryAll")
public ResponseResult> queryAll(){
return seckillGoodsService.queryAll();
}
@ResponseBody
@RequestMapping("/add")
public ResponseResult> 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 `
删除
秒杀
`;
}
}
②、添加秒杀详情界面 :goodsSkill.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> |
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工具包:SnowFlakepackage 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);
}
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; public interface ISeckillOrderService extends IService{ 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; @Service public class SeckillOrderServiceImpl extends ServiceImpl③、controller层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(); } }
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;
@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



