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

news2024/11/28 13:48:57

登录方式调整

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGgFVLjU-1676371517002)(images\20220817224735.jpg)]

注意:
1)本次不采用spring-session方式,改用redis直接存储用户登录信息,主要是为了方便之后的jmeter压测;
2)这里只注释调用spring-session的依赖,保留redis的依赖;

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

@Configuration
public class RedisConfig {

    @Bean
    public 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;
    }
}

第3步:在zmall-common公共模块中配置redis相关服务

IRedisServcie

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 {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public void setUserToRedis(String token, User user) {
        String key="user:"+token;
        redisTemplate.boundValueOps(key).set(user,7200,TimeUnit.SECONDS);
    }

    @Override
    public User getUserByToken(String token) {
        return (User) redisTemplate.opsForValue().get("user:"+token);
    }
}

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

第4步:配置自定义参数解析UserArgumentResolver、WebConfig

UserArgumentResolver

/**
 * 自定义用户参数类
 */
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private IRedisService redisService;

    /**
     * 只有supportsParameter方法执行返回true,才能执行下面的resolveArgument方法
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> type = methodParameter.getParameterType();
        return type== User.class;
    }

    @Override
    public 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 {

    @Autowired
    private UserArgumentResolver userArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userArgumentResolver);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //添加静态资源访问映射
        //registry.addResourceHandler("/static/**")
        //        .addResourceLocations("classpath:/static/");
    }
}

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

//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);
}

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nf3XjJz0-1676371517006)(images\20220817233131.jpg)]

生成秒杀订单

绑定秒杀商品

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

 <#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.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.id}">${g.name}</a></h2>
             倒计时:<span>1200</span> 时 <span>30</span> 分 <span>28</span> 秒
             </div>
         </div>
     </#list>
 </#if>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1zL4Qix-1676371517008)(images\2022-08-18_164403.png)]

查看秒杀商品

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWNimZH4-1676371517017)(images\2022-08-18_164751.png)]

订单秒杀

移除seata相关

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

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

第3步:移除zmall-order中分布式事务案例中的@GlobalTransactional注解

第4步:删除DataSourceProxyConfig该类

第5步:移除zmall-order中启动类上的注解参数(exclude = DataSourceAutoConfiguration.class)

//更改前:
//@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//更改后:
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan({"com.zking.zmall.mapper"})
@EnableFeignClients
public class ZmallOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZmallOrderApplication.class, args);
    }

}

第6步:更换zmall-order和zmall-product中的application.yml配置中的数据库连接池

datasource:
   #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
   #更改前:
   #type: com.alibaba.druid.pool.DruidDataSource
   #更改后:
   type: com.zaxxer.hikari.HikariDataSource

生成秒杀订单

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

IOrderService

public interface IOrderService extends IService<Order> {

    JsonResponseBody<?> createKillOrder(User user, Integer pid);
}

OrderServiceImpl

@Transactional
@Override
public JsonResponseBody<?> createKillOrder(User user, Integer pid) {
    //1.判断用户是否登录
    if(null==user)
    	throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);
    //根据用户ID和秒杀商品Id判断。。。。
    //2.根据秒杀商品编号获取秒杀商品库存是否为空
    Kill kill = killService.getOne(new QueryWrapper<Kill>().eq("item_id",pid));
    if(kill.getTotal()<1)
    	throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);
    //3.根据商品ID获取商品
    Product product = productService.getProductById(pid);
    //4.秒杀商品库存减一
    killService.updateKillStockById(pid);
    //5.生成秒杀订单及订单项
    SnowFlake snowFlake=new SnowFlake(2,3);
    Long orderId=snowFlake.nextId();
    //创建订单
    Order order=new Order();
    order.setUserId(user.getId());
    order.setLoginName(user.getLoginName());
    order.setCost(product.getPrice());
    order.setSerialNumber(orderId);
    this.save(order);
    //创建订单项
    OrderDetail orderDetail=new OrderDetail();
    orderDetail.setOrderId(orderId);
    orderDetail.setProductId(product.getId());
    orderDetail.setQuantity(1);
    orderDetail.setCost(product.getPrice());
    orderDetailService.save(orderDetail);
    return new JsonResponseBody();
}

前端页面秒杀测试

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LM7Ibg8f-1676371517019)(images\2022-08-18_171847.png)]

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

utils:https://pan.baidu.com/s/1ExaC4GgEg_ofKsARkYhHXw
提取码:kq20

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

魔兽世界WOW私服架设详细教程

1. 写在前面&#xff1a;此教程是针对国服WOW3.3.5.13930版本的&#xff0c;因为目前魔兽单机在此版本下运行最正常。WOW4.0以上版本还有些许问题2. 准备文件&#xff08;1&#xff09;WOW3.3.5.13930客户端&#xff0c;没有的可以从这里下载WOW 3.3.2安装文件和WOW3.3.2-3.3.5…

【linux C】daemon函数应用之——进程守护小工具,运维仔看了都说好!并附带shell版本

最近接触到Linux C中的daemon函数&#xff0c;顾名思义&#xff0c;它和守护进程Daemon有关&#xff1b;简单来说Linux Daemon&#xff08;守护进程&#xff09;是运行在后台的一种特殊进程&#xff1b; 一般来说&#xff0c;它独立于控制终端并且周期性地执行某种任务或等待处…

认识V模型、W模型、H模型

软件测试与软件工程息息相关&#xff0c;软件测试是软件工程组成中不可或缺的一部分。 在软件工程、项目管理、质量管理得到规范化应用的企业&#xff0c;软件测试也会进行得比较顺利&#xff0c;软件测试发挥的价值也会更大。 要关注软件工程、质量管理以及配置管理与软件测试…

ChatGPT中文网尝鲜,感觉自己快下岗了

最近很火的ChatGPT之初体验 ChatGPT中文网 居然可以回答代码问题 尝试了一下, 它居然说自己是一个人 顺便问了下简单的java代码问题 “使用java语言写一个递归打印D盘中所有文件名的程序” 很流畅的回答了出来,注释还写得比我详细,感觉我离下岗不远了 这就是GPT写的代码 i…

工人不戴安全帽自动检测识别 opencv

工人不戴安全帽自动检测识别通过pythonopencv深度学习网络模型&#xff0c;工人不戴安全帽自动检测识别算法对现场人员穿戴进行全天候不间断识别检测&#xff0c;发现现场人员违规行为着装自动抓拍存档。Python是一门解释性脚本语言。解释性语言&#xff1a;解释型语言&#xf…

C语言中大小端问题

目录 一、什么是大小端 二、 举个例子 三、大小端演示 四、解释"二"中举例的问题 ​五、怎么判断是大端还是小端 六、一个题目 一、什么是大小端 大端模式&#xff08;大端字节序存储&#xff09;&#xff1a;就是高位字节数据存放在内存的低地址端&#xff…

工序排序问题--约翰逊法精讲

什么是约翰逊法?约翰逊法是作业排序中的一种排序方法。选出最短加工时间i*&#xff0c;若最短加工时间有多个&#xff0c;任选1个.若i*出现在机床1&#xff0c;它对应的工件先安排加工&#xff0c;否则放在最后安排&#xff0c;安排后划去该工件,重复上两个步骤&#xff0c;直…

python3.11下载安装详细教程

python3.11新的功能特性 1. 支持新的字符串格式化功能&#xff0c;允许使用更多的格式化选项&#xff1b; 2. 支持新的数据类型&#xff0c;如结构化数据类型&#xff1b; 3. 支持新的编程模型&#xff0c;如asyncio&#xff1b; 4. 支持新的编程语言特性&#xff0c;如f-s…

别在用scroll去做懒加载了,交叉观察器轻松搞定

Ⅰ、前言 「懒加载」是网页中非常 常见的&#xff1b;为了减少系统的压力&#xff0c;对于一些电商系统出场频率非常高&#xff1b;那么大家一般用什么方式去实现 「懒加载」 呢 &#xff1f; ① 通过 scroll 的形式&#xff1a; 通过 滚动「scroll」事件&#xff0c;然后去判…

浅析 Makefile

Makefile逻辑 Makefile就是将一系列的工作流串在一起自动执行&#xff0c;构成Makefile最基本的要素是目标、依赖、命令。也就是为了实现目标需要哪些依赖并执行什么样的命令。 target: dependences1 dependences2 ... command1 command2 ...其中&#xff0c;target表示要生…

Thinking--FastDom消除浏览器布局抖动

Thinking系列&#xff0c;旨在利用10分钟的时间传达一种可落地的编程思想。 对于大量操作 DOM 的场景&#xff0c;页面时常会出现卡顿现象&#xff0c;导致用户体验不佳。卡顿的原因是由于掉帧导致&#xff01;&#xff01; 掉帧 现在屏幕大部分的固定刷新频率为60Hz&#xf…

30个HTML+CSS前端开发案例(完结篇)

30个HTMLCSS前端开发案例&#xff08;完结篇&#xff09;flex弹性布局-今日头条首页热门视频栏代码实现效果flex弹性布局-微博热搜榜单代码实现效果grid网格布局-360图片展示代码实现效果综合实例-小米商城左侧二级菜单代码实现效果资源包flex弹性布局-今日头条首页热门视频栏 …

Pinia快速入门

Pinia学习1.做什么用的❓2.优势❓3. 介绍❓3.1、与vuex对比与 Vuex 3.x/4.x 的比较#4.实操使用&#x1f4aa;版本须知4.1创建项目4.2运行项目4.3使用pinia安装全局引入挂载使用stategettersactions示例代码分析1.做什么用的❓ 与vuex的作用一致&#xff0c;用于做网页存储的Pin…

【STC15单片机】模拟I2C操作AT24C02数据读取【更新中】

目录 I2C时序结构 I2C代码 AT24C02代码&#xff08;继承I2C底层代码&#xff09; PCF8591 PCB上线的长短可能影响数据传输的时间&#xff0c;写I2C时序可能就要加一点延时 I2C时序结构 起始条件&#xff1a;SCL高电平期间&#xff0c;SDA从高电平切换到低电平终止条件&…

什么蓝牙耳机好用性价比高?性价比最高的蓝牙耳机品牌排行

无线耳机迅速成为了电子产品中的佼佼者&#xff0c;无论是日常通勤、办公或是休闲、娱乐&#xff0c;几乎都能看到戴耳机听音乐的人&#xff0c;细心的朋友应该能够发现&#xff0c;蓝牙耳机在这几年有了很大的进步&#xff0c;下面我就分享几款当前性价比最高的蓝牙耳机。 TO…

T38,数的递归

描述 输入一棵节点数为 n 二叉树&#xff0c;判断该二叉树是否是平衡二叉树。 在这里&#xff0c;我们只需要考虑其平衡性&#xff0c;不需要考虑其是不是排序二叉树 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;&#xff0c;具有以下性质&#xff1a;它是一棵空…

分账系统逻辑

一、说明 主体与业务关系方进行相关利益和支出的分配过程 使用场景&#xff1a; 在分销业务中&#xff0c;主营商户收到用户购买分销商品所支付的款项后&#xff0c;可以通过分账逻辑&#xff0c;与分销商进行佣金结算。在零售、餐饮等行业中&#xff0c;当销售人员完零售等…

小樽 C++指针—— (壹) 指针变量

(壹) 指针变量 一、指针的概念与定义 二、给指针变量p赋值 三、指针变量的的、-运算 四、无类型指针 五、多重指针 C (壹) 指针变量 小明想把从李华家借来的书——《CCF中学生计算机程序设计》还给李华&#xff0c;但李华不在家&#xff0c;于是把书放到书架第3层的最右边…

入门介绍对ChatGPT的应用程序接口API的访问<openai模块>

首先本人建议使用国内环境安装openai模块(这是我切换环境使用国外IP之后安装有问题的建议)pip install openai -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com安装好了之后&#xff0c;我们切换成科学上网&#xff0c;然后我们来到https://platform.openai…

十大开源测试工具和框架,一定有你需要的

目录 前言 Katalon Studio Selenium Appium JMeter SOAP UI Robot Framework Watir JUnit Robotium Citrus 总结 前言 免费的开源框架和工具由于其开源特性&#xff0c;现在逐渐成为自动化测试的首选解决方案。区别在于&#xff0c;你是喜欢使用类库编写一个全新的…