目录

一、登录方式调整

二、秒杀商品展示&秒杀商品详情&商品秒杀功能

1.生成秒杀订单

2.绑定秒杀商品

3.查看看秒商品

4.订单秒杀

① 移除seata相关

② 生成秒杀订单

③ 前端页面秒杀测试


一、登录方式调整

第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖

(注意:由于我的spring session没有了,用户以及商品和订单可能会报错注释掉就好了)

注意:

1)本章节中不采用spring-session方式,改用redis直接存储用户登录信息,主要是为了方便之后的jmeter压测;

2)这里只注释调用spring-session的依赖,保留redis的依赖;

springboot 集成redis 只要yml中添加redis相关配置,那么Spring容器就会初始化一个redisConnection对象

第2步:在zmall-common公共模块中定义ReidsConfig配置类

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object> restTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();//String类型Key序列化redisTemplate.setKeySerializer(new StringRedisSerializer());//String类型Value序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//Hash类型Key序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());//Hash类型Value序列化redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(redisConnectionFactory);return redisTemplate;}
}

这里一定要注意,最后在将RedisConnectionFactory设置到RedisTemplate中,不要在最前做该步操作,不然会导致String和Hash类型的序列化无效,将采用默认的JdkSerializationRedisSerializer进行序列化,从而导致保存的key前缀出现乱码问题。细心!!!细心!!!细心!!!o(╥﹏╥)o

参考地址:RedisTemplate写入Redis数据出现无意义乱码前缀\xac\xed\x00\x05_hunger_wang的博客-CSDN博客_\xac

第3步:在zmall-common公共模块中配置redis相关服务,建一个包叫service

IRedisService

public interface IRedisService {/*** 将登陆用户对象保存到Redis中,并以token来命名* @param token* @param user*/void setUserToRedis(String token, User user);/*** 根据token令牌从Redis中获取User对象* @param token* @return*/User getUserByToken(String token);
}

RedisServcieImple

@Service
public class RedisServiceImpl implements IRedisService {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Overridepublic void setUserToRedis(String token, User user) {String key="user:"+token;redisTemplate.boundValueOps(key).set(user,7200,TimeUnit.SECONDS);}@Overridepublic User getUserByToken(String token) {return (User) redisTemplate.opsForValue().get("user:"+token);}
}

用户登录成功后,将用户对象保存到Redis中,并设置超时时间7200秒。

第4步:在zmall-common公共模块中配置,配置类里自定义参数解析UserArgumentResolver、WebConfig

UserArgumentResolver

/*** 自定义用户参数类*/
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {@Autowiredprivate IRedisService redisService;/*** 只有supportsParameter方法执行返回true,才能执行下面的resolveArgument方法* @param methodParameter* @return*/@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Class<?> type = methodParameter.getParameterType();return type== User.class;}@Overridepublic Object resolveArgument(MethodParameter methodParameter,ModelAndViewContainer modelAndViewContainer,NativeWebRequest nativeWebRequest,WebDataBinderFactory webDataBinderFactory) throws Exception {HttpServletRequest req= (HttpServletRequest) nativeWebRequest.getNativeRequest();//从cookie获取token令牌String token = CookieUtils.getCookieValue(req, "token");//判断cookie中的token令牌是否为空if(StringUtils.isEmpty(token))throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);//根据token令牌获取redis中存储的user对象,方便jmeter测试User user = redisService.getUserByToken(token);if(null==user)throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);return user;}
}

WebConfig

@Component
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate UserArgumentResolver userArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userArgumentResolver);}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//添加静态资源访问映射//registry.addResourceHandler("/static/**")//        .addResourceLocations("classpath:/static/");}
}

第5步:用户登录业务调整,将spring-session方式更改为redis方式存储登录用户信息。

UserServiceimpl


@Autowiredprivate RedisServiceImpl redisService;
...//5.通过UUID生成token令牌并保存到cookie中
String token= UUID.randomUUID().toString().replace("-","");
//将随机生成的Token令牌保存到Cookie中,并设置1800秒超时时间
CookieUtils.setCookie(req,resp,"token",token,7200);
//6.将token令牌与spring session进行绑定并存入redis中
//HttpSession session = req.getSession();
//session.setAttribute(token,us);
//将token令牌与user绑定后存储到redis中,方便jmeter测试
redisService.setUserToRedis(token,us);

这里采用Redis方式直接存储登录用户信息,只为后续使用Jmeter压测时提供便利。正常运行使用项目还是可以使用spring-session方式。

第6步:修改商品服务zmall-product模块中的index方法,将之前从HttpSession中获取登录用户信息改换成User对象参数方式

 @RequestMapping("/index.html")public String index(Model model, User user){System.out.println(user);
//        按照商品的销量降序排序获取销量排名Top5的商品List<Product> products = productService.list(new QueryWrapper<Product>().orderByDesc("hot").last("limit 5"));model.addAttribute("hots",products);return "index";}

在调用index方法之前,先由自定义的参数解析器进行参数解析并返回解析结果User,所以在这里可直接在方法参数中获取的User对象。

在启动项目之前要先启动我们的nacos和nginx在启动我们的项目

第7步:重启zmall-user和zmall-product模块,http://zmall.com/user-serv/login.html完成用户登录后,直接在浏览器地址栏输入:http://zmall.com/product-serv/index.html,查看zmall-product模块中的控制台是否已经获取到登录用户对象信息。

二、秒杀商品展示&秒杀商品详情&商品秒杀功能

1.生成秒杀订单

我们用的表zmall-kill,由于我们之前的表没有数据,所以我们要手动添加数据如下图所示:

/*Navicat Premium Data TransferSource Server         : 本地连接Source Server Type    : MySQLSource Server Version : 80013Source Host           : localhost:3306Source Schema         : zmallTarget Server Type    : MySQLTarget Server Version : 80013File Encoding         : 65001Date: 10/02/2023 10:09:28
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for zmall_kill
-- ----------------------------
DROP TABLE IF EXISTS `zmall_kill`;
CREATE TABLE `zmall_kill`  (`id` int(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',`item_id` int(11) NULL DEFAULT NULL COMMENT '商品id',`total` int(11) NULL DEFAULT NULL COMMENT '可被秒杀的总数',`start_time` datetime(0) NULL DEFAULT NULL COMMENT '秒杀开始时间',`end_time` datetime(0) NULL DEFAULT NULL COMMENT '秒杀结束时间',`is_active` tinyint(11) NULL DEFAULT 1 COMMENT '是否有效(1=是;0=否)',`create_time` timestamp(0) NULL DEFAULT NULL COMMENT '创建的时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '待秒杀商品表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of zmall_kill
-- ----------------------------
INSERT INTO `zmall_kill` VALUES (1, 733, 10, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 1, '2023-02-10 00:05:13');
INSERT INTO `zmall_kill` VALUES (2, 734, 9, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 1, '2023-02-10 00:05:59');
INSERT INTO `zmall_kill` VALUES (3, 735, 9, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 1, '2023-02-10 00:06:16');
INSERT INTO `zmall_kill` VALUES (4, 736, 10, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 1, '2023-02-10 00:06:34');
INSERT INTO `zmall_kill` VALUES (5, 737, 10, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 0, '2023-02-10 00:06:53');
INSERT INTO `zmall_kill` VALUES (6, 738, 10, '2023-02-09 00:04:56', '2024-02-10 00:05:01', 1, '2023-02-10 00:07:10');SET FOREIGN_KEY_CHECKS = 1;

2.绑定秒杀商品

添加sellDetail.html页面到zmall-product模块中;实现首页秒杀商品展示,必须保证秒杀商品状态为已激活、且秒杀商品的活动时间为有效时间范围之内。

  • 将资料目录中的《易买网网页素材.rar》,将其中SellDetails.html添加到zmall-product项目的templates下,最好请将SellDetails.html页面首字母改成小写

  • 将页面中的头部申明<!DOCTYPE html ....>修改成<!DOCTYPE html>(支持H5风格)

  • 在页面中通过<#include>指令引入common目录中的head.html

index.html 找到我们显示秒杀的地方修改为如下代码。

<#if kills??><#list kills as g><div class="sell_${g_index?if_exists+1}"><div class="sb_img"><a href="${ctx}/sellDetail.html?pid=${g.item_id}"><img src="${g.fileName}" width="242" height="356" /></a></div><div class="s_price">¥<span>${g.price}</span></div><div class="s_name"><h2><a href="${ctx}/sellDetail.html?pid=${g.item_id}">${g.name}</a></h2>倒计时:<span>1200</span> 时 <span>30</span> 分 <span>28</span> 秒</div></div></#list></#if>

把我们生成的mapper复制到公共模块中,如下图所示:可以把生成中的mapper删除掉了。

ProductMapper.xml

<select id="queryKillProdNews" resultType="java.util.Map">selectk.id,k.item_id,p.name,p.price,p.fileNamefromzmall_kill k,zmall_product pwhere k.item_id=p.id andk.is_active=1 and(now() between start_time and end_time)order by k.create_time desclimit 4</select><select id="queryKillProdById" resultType="java.util.Map">selectk.id,k.item_id,k.total,p.name,p.price,p.fileNamefromzmall_kill k,zmall_product pwhere k.item_id=p.id and k.is_active=1 and item_id=#{value}</select>

mapper层

@Repository
public interface ProductMapper extends BaseMapper<Product> {
//    @MapKey("queryKillProdNews")List<Map<String,Object>> queryKillProdNews();Map<String,Object> queryKillProdById(Integer pid);
}

service层

public interface IProductService extends IService<Product> {void updateStock(Integer pid,Integer num);/*** 首页显示秒杀商品查询* @return*/List<Map<String,Object>> queryKillProdNews();/*** 根据商品ID查询秒杀商品信息* @param pid 秒杀商品ID* @return*/Map<String,Object> queryKillProdById(Integer pid);
}

ProductServiceImpl实现层

 @Autowiredprivate ProductMapper productMapper;@Overridepublic List<Map<String, Object>> queryKillProdNews() {return productMapper.queryKillProdNews();}@Overridepublic Map<String, Object> queryKillProdById(Integer pid) {return productMapper.queryKillProdById(pid);}

controller层

方法一:@RequestMapping("/index.html")public String index(Model model, User user){System.out.println(user);
//        按照商品的销量降序排序获取销量排名Top5的商品List<Product> products = productService.list(new QueryWrapper<Product>().orderByDesc("hot").last("limit 5"));model.addAttribute("hots",products);//获取显示秒杀商品List<Map<String, Object>> maps = productService.queryKillProdNews();model.addAttribute("kills",maps);return "index";}方法二:
@RequestMapping("/index.html")public ModelAndView toIndex(User user){System.out.println("user:"+ JSON.toJSONString(user));ModelAndView mv=new ModelAndView();//获取热卖商品列表List<Product> hot = productService.list(new QueryWrapper<Product>().orderByDesc("hot").last("limit 4"));//获取显示秒杀商品List<Map<String, Object>> maps = productService.queryKillProdNews();mv.addObject("kills",maps);mv.addObject("hots",hot);mv.setViewName("index");return mv;}

我们已经绑定秒杀商品成功了,全是从数据库里查询出来的。

没修改之前

修改之后

3.查看看秒商品

点击限时秒杀中的秒杀商品,根据秒杀商品ID查询秒杀商品详情信息并跳转到sellDetail.html页面展示秒杀商品信息。

ProductController

@RequestMapping("/sellDetail.html")public String sellDetail(@RequestParam Integer pid, Model model){//根据商品ID查询秒杀商品信息Map<String, Object> prod = productService.queryKillProdById(pid);model.addAttribute("prod",prod);return "sellDetails";}

sellDetail.html

<table border="0" style="width:100%; margin-bottom:50px;" cellspacing="0" cellpadding="0"><tr valign="top"><td width="315"><div class="lim_name">${(prod.name)!}</div><div class="lim_price"><span class="ch_txt">¥${(prod.price)!}</span><a href="javascript:void(0);" class="ch_a" pid="${(prod.item_id)!}" price="${(prod.price)!}">抢购</a></div><div class="lim_c"><table border="0" style="width:100%; color:#888888;" cellspacing="0" cellpadding="0"><tr><td width="35%">市场价 </td><td width="65%">折扣</td></tr><tr style="font-family:'Microsoft YaHei';"><td style="text-decoration:line-through;">¥${(prod.price)!}</td><td>8.0</td></tr></table></div><div class="lim_c"><div class="des_choice"><span class="fl">型号:</span><ul><li class="checked">30ml<div class="ch_img"></div></li><li>50ml<div class="ch_img"></div></li><li>100ml<div class="ch_img"></div></li></ul></div><div class="des_choice"><span class="fl">颜色:</span><ul><li>红色<div class="ch_img"></div></li><li class="checked">白色<div class="ch_img"></div></li><li>黑色<div class="ch_img"></div></li></ul></div></div><div class="lim_c"><span class="fl">数量:</span><input type="text" value="${(prod.total)!}" class="lim_ipt" /></div><div class="lim_clock">距离团购结束还有<br /><span>1200 时 30 分 28 秒</span></div></td><td width="525" align="center" style="border-left:1px solid #eaeaea;"><img src="${(prod.fileName)!}" width="460" height="460" /></td></tr></table>

4.订单秒杀

① 移除seata相关

第1步:先注释掉zmall-order和zmall-product模块中的seata依赖

第2步:分别删掉zmall-order和zmall-product模块中resources目录下的bootstrap.xml和register.conf文件

seata分布式事务,进行jmeter压测秒杀订单接口效率太低(1000个并发请求,吞吐量为4.5/s)o(╥﹏╥)o

② 生成秒杀订单

将SnowFlake雪花ID生成工具类导入到zmall-common模块中utils,然后再生成秒杀订单时使用雪花ID来充当秒杀订单编号;在zmall-order模块中完成秒杀订单生成工作。

IOrderService

public interface IOrderService extends IService<Order> {Order createOrder(Integer pid,Integer num);/*** 生成秒杀订单* @param user 登陆用户对象* @param pid  秒杀商品ID* @param price 秒杀商品价格* @return*/JsonResponseBody<?> createKillOrder(User user, Integer pid, Float price);
}

OrderServiceImpl

@Autowired
private KillServiceImpl killService;
@Autowired
private OrderDetailServiceImpl orderDetailService;@Transactional@Overridepublic JsonResponseBody<?> createKillOrder(User user, Integer pid, Float price) {//1.根据秒杀商品编号获取秒杀商品库存是否为空Kill kill = killService.getOne(new QueryWrapper<Kill>().eq("item_id",pid));if(kill.getTotal()<1)throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);//2.秒杀商品库存减一killService.update(new UpdateWrapper<Kill>().eq("item_id",pid).setSql("total=total-1"));//3.生成秒杀订单及订单项SnowFlake snowFlake=new SnowFlake(2,3);Long orderId=snowFlake.nextId();int orderIdInt = new Long(orderId).intValue();//创建订单Order order=new Order();order.setUserId(user.getId());order.setLoginName(user.getLoginName());order.setCost(price);order.setSerialNumber(orderIdInt+"");this.save(order);//创建订单项OrderDetail orderDetail=new OrderDetail();orderDetail.setOrderId(orderIdInt);orderDetail.setProductId(pid);orderDetail.setQuantity(1);orderDetail.setCost(price);orderDetailService.save(orderDetail);return new JsonResponseBody();}

OrderController

@RequestMapping("/createKillOrder/{pid}/{price}")@ResponseBodypublic JsonResponseBody<?> createKillOrder(User user,@PathVariable("pid") Integer pid,@PathVariable("price") Float price){return orderService.createKillOrder(user,pid,price);}

把我们的启动类上的数据源删除

package com.zking.zmall;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableDiscoveryClient
@EnableFeignClients
@MapperScan({"com.zking.zmall.mapper"})
@SpringBootApplication
public class ZmallOrderApplication {public static void main(String[] args) {SpringApplication.run(ZmallOrderApplication.class, args);}}

③ 前端页面秒杀测试

在sellDetail.html页面中添加订单秒杀JS方法

<script>$(function(){$('.ch_a').click(function(){let pid=$(this).attr('pid');let price=$(this).attr('price');console.log(pid);console.log(price);$.post('http://zmall.com/order-serv/createKillOrder',{pid:pid,price:price},function(rs){console.log(rs);if(rs.code===200)alert('秒杀成功');elsealert(rs.msg);},'json');});});
</script>

启动类

这里虽然已经能正常展示秒杀效果,但是还是存在很多问题,比如:重复抢购问题等等问题。

注意:当我们是以下代码的时候

$.post('http://user.zmall.com/userLogin',{loginName:loginName,password:password},function(rs){console.log(rs);if(rs.code===200){location.href='http://product.zmall.com/index.html';}else{alert(rs.msg);}},'json');

post方式不能垮二级域名发送亲戚,location.href可以跨二级域名发送请求;

$(function(){$('.ch_a').click(function(){let pid=$(this).attr("pid");let price=$(this).attr("price");console.log("pid=%s,price=%s",pid,price);$.post('http://zmall.com/order-serv/createKillOrder/'+pid+'/'+price,{},function(rs){console.log(rs);if(rs.code===200)alert('秒杀成功');elsealert(rs.msg);},'json');});});

$.post('http://zmall.com/order-serv/createKillOrder/'+pid+'/'+price,{},function(rs){});能够正常访问;

$.post('http://order.zmall.com/createKillOrder/'+pid+'/'+price,{},function(rs){});则会出现跨域问题;

秒杀商品展示及商品秒杀相关推荐

  1. 微服务项目【秒杀商品展示及商品秒杀】

    登录方式调整 第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖 注意: 1)本次不采用spring-session方式,改用redis直接 ...

  2. 秒杀项目之秒杀商品展示及商品秒杀

    目录 前言 一.登录方式调整 二.生成秒杀订单 2.1 绑定秒杀商品 2.2 查看秒杀商品 2.3 订单秒杀 2.3.1 移除seata相关(方便测压) 2.3.2 生成秒杀订单 2.3.3 前端页面 ...

  3. 秒杀项目之商品展示及商品秒杀

    目录 登录方式调整 生成秒杀订单 绑定秒杀商品 查看秒杀商品 订单秒杀 移除seata相关 生成秒杀订单 前端页面秒杀测试 登录方式调整 第1步:从zmall-common的pom.xml中移除spr ...

  4. java秒杀系统 代码大全_Java秒杀系统:商品秒杀代码实战

    内容: "商品秒杀"功能模块是建立在"商品详情"功能模块的基础之上,对于这一功能模块而言,其主要的核心流程在于:前端发起抢购请求,该请求将携带着一些请求数据:待 ...

  5. 网站服务器时间秒杀,Javascript实现商品秒杀倒计时(时间与服务器时间同步)...

    现在有很多网站都在做秒杀商品,而这其中有一个很重要的环节就是倒计时. 关于倒计时,有下面几点需要注意: 1.应该使用服务器时间而不是本地时间(本地时间存在时区不同.用户自行设置等问题). 2.要考虑网 ...

  6. 电商项目实战之商品秒杀

    电商项目实战之商品秒杀 定时任务 corn表达式 实现方式 基于注解 基于接口 实战 秒杀系统 秒杀系统关注问题 秒杀架构设计 商品上架 获取当前秒杀商品 获取当前商品的秒杀信息 秒杀最终处理 参考链 ...

  7. php商品秒杀功能笔记

    系统环境搭建 基础:linux+php+mysql+redis 升级:CDN,只能DNS,分布式缓存,全国多节点,多线路接入(内容分发网络) 硬件升级:负载均衡LVS (高效稳定) 大型WEB集群 环 ...

  8. 高性能商品秒杀抢购系统

    完整资料进入[数字空间]查看--baidu搜索"writebug" Go+iris+rabbbitmq+mysql构建高性能商品秒杀抢购系统 一.项目介绍 1. 课程目标 应用Go ...

  9. Spring Boot + redis解决商品秒杀库存超卖,看这篇文章就够了

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:涛哥谈篮球 来源:toutiao.com/i68366119 ...

最新文章

  1. springMvc+mybatis+spring 整合 包涵整合activiti 基于maven
  2. linux-squirrel
  3. [Android] android的消息队列机制
  4. burp suite 二级域名扫描插件
  5. 重载-运算符(感觉怪怪的)
  6. Python简单的拼写检查
  7. Javascript远程加载框架 - JFO Remote Framework 1.0
  8. 解决问题--DatabaseMetaData的getTables()返回所有数据库的表信息
  9. java 静态相关内容
  10. Codeforces 627D Preorder Test(二分+树形DP)
  11. 设计师谈中望CAD2010应用心得 作者:刘国勤
  12. Coverity 配置coverity扫描python静态代码检测
  13. html5手机app抽奖页面,app H5活动抽奖转盘 前端+后台
  14. matlab 对信号抽样,matlab信号抽样与恢复
  15. Android - 跳转系统网络设置
  16. Stefan - 人生目前学到的21样事
  17. Android 5.0、6.0、7.0、8.0、9.0 新特性,DownloadManager踩坑记
  18. 基于JavaWeb的影视创作论坛的设计与实现/影视系统
  19. CC2640R2F BLE5.0 CC2640R2F UART驱动
  20. NF_Exp4_20164306

热门文章

  1. python制作gif动图_怎样用Python制作好玩的GIF动图
  2. PMBOK第六版与第五版的区别
  3. IT行业找工作容易吗?
  4. 小程序云函数调式nodemodules未安装
  5. 原声ajax发送post请求,原生JS实现ajax 发送post请求
  6. 工业互联网,走,上云去。
  7. 桌面快捷方式不见了怎么办实际解决
  8. 读取图片输出字符图案:用字符输出足球的图案
  9. 【Outlook 邮件配置】
  10. JAVA的三大特征之继承