尚品汇-秒杀成功下单接口、秒杀结束定时任务-清空缓存数据(五十四)

news2024/11/14 15:10:11

目录:

(1)下单页面

(2)service-activity-client添加接口

(3)web-all 编写去下单控制器

(4)service-order模块提供秒杀下单接口

 (5)service-order-client模块暴露接口

(6)service-activity模块秒杀下单

(7)秒杀结束清空redis缓存

(1)下单页面

我们已经把下单信息记录到redis缓存中,所以接下来我们要组装下单页数据

 下单页数据数据接口SeckillGoodsApiController

@Autowired
private RedisTemplate redisTemplate;
/**
 * 秒杀确认订单
 * @param request
 * @return
 */
@GetMapping("auth/trade")
public Result trade(HttpServletRequest request) {
    // 获取到用户Id
    String userId = AuthContextHolder.getUserId(request);

    // 先得到用户想要购买的商品!
    OrderRecode orderRecode = (OrderRecode) redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS).get(userId);
    if (null == orderRecode) {
        return Result.fail().message("非法操作");
    }
    //获取商品
    SeckillGoods seckillGoods = orderRecode.getSeckillGoods();

    //获取用户地址
    List<UserAddress> userAddressList = userFeignClient.findUserAddressListByUserId(userId);

    // 声明一个集合来存储订单明细
    ArrayList<OrderDetail> detailArrayList = new ArrayList<>();
    OrderDetail orderDetail = new OrderDetail();
    orderDetail.setSkuId(seckillGoods.getSkuId());
    orderDetail.setSkuName(seckillGoods.getSkuName());
    orderDetail.setImgUrl(seckillGoods.getSkuDefaultImg());
    orderDetail.setSkuNum(orderRecode.getNum());
    orderDetail.setOrderPrice(seckillGoods.getCostPrice());
    // 添加到集合
    detailArrayList.add(orderDetail);

    // 计算总金额  创建订单对象,计算订单价格
    OrderInfo orderInfo = new OrderInfo();
    orderInfo.setOrderDetailList(detailArrayList);
    orderInfo.sumTotalAmount();

    Map<String, Object> result = new HashMap<>();
    result.put("userAddressList", userAddressList);
    result.put("detailArrayList", detailArrayList);
    result.put("totalNum", 1);
    // 保存总金额
    result.put("totalAmount", orderInfo.getTotalAmount());
    return Result.ok(result);
}

(2)service-activity-client添加接口

ActivityFeignClient
/**
 * 秒杀确认订单
  * @return
 */
@GetMapping("/api/activity/seckill/auth/trade")
Result<Map<String, Object>> trade();

ActivityDegradeFeignClient 


@Override
public Result<Map<String, Object>> trade() {
    return Result.fail();
}

(3)web-all 编写去下单控制器

SeckillController

/**
 * 确认订单
 * @param model
 * @return
 */
@GetMapping("seckill/trade.html")
public String trade(Model model) {
    Result<Map<String, Object>> result = activityFeignClient.trade();
    if(result.isOk()) {
        model.addAllAttributes(result.getData());
        return "seckill/trade";
    } else {
        model.addAttribute("message",result.getMessage());

        return "seckill/fail";
    }
}

Result

package com.atguigu.gmall.common.result;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 全局统一返回结果类
 *
 */
@Data
@ApiModel(value = "全局统一返回结果")
public class Result<T> {

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private T data;

    public Result(){}

    // 返回数据
    protected static <T> Result<T> build(T data) {
        Result<T> result = new Result<T>();
        if (data != null)
            result.setData(data);
        return result;
    }

    public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
        Result<T> result = build(body);
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }

    public static<T> Result<T> ok(){
        return Result.ok(null);
    }

    /**
     * 操作成功
     * @param data
     * @param <T>
     * @return
     */
    public static<T> Result<T> ok(T data){
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.SUCCESS);
    }

    public static<T> Result<T> fail(){
        return Result.fail(null);
    }

    /**
     * 操作失败
     * @param data
     * @param <T>
     * @return
     */
    public static<T> Result<T> fail(T data){
        Result<T> result = build(data);
        return build(data, ResultCodeEnum.FAIL);
    }

    public Result<T> message(String msg){
        this.setMessage(msg);
        return this;
    }

    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }

    public boolean isOk() {
        if(this.getCode().intValue() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
            return true;
        }
        return false;
    }
}

 

下单确认页面

该页面与正常下单页面类似,只是下单提交接口不一样,因为秒杀下单不需要正常下单的各种判断,因此我们要在订单服务提供一个秒杀下单接口,直接下单

(4)service-order模块提供秒杀下单接口

 

OrderApiController :提供一个重载的下单方法       
/**
 * 秒杀提交订单,秒杀订单不需要做前置判断,直接下单
 * @param orderInfo
 * @return
 */
@PostMapping("inner/seckill/submitOrder")
public Long submitOrder(@RequestBody OrderInfo orderInfo) {
    Long orderId = orderService.saveOrderInfo(orderInfo);
    return orderId;
}

  (5)service-order-client模块暴露接口

OrderFeignClient
/**
 * 提交秒杀订单
 * @param orderInfo
 * @return
 */
@PostMapping("/api/order/inner/seckill/submitOrder")
Long submitOrder(@RequestBody OrderInfo orderInfo);

OrderDegradeFeignClient 


@Override
public Long submitOrder(OrderInfo orderInfo) {
    return null;
}

(6)service-activity模块秒杀下单

SeckillGoodsApiController
@Autowired
private OrderFeignClient orderFeignClient;

@PostMapping("auth/submitOrder")
public Result submitOrder(@RequestBody OrderInfo orderInfo, HttpServletRequest request) {
    String userId = AuthContextHolder.getUserId(request);

    
    orderInfo.setUserId(Long.parseLong(userId));

    Long orderId = orderFeignClient.submitOrder(orderInfo);
    if (null == orderId) {
        return Result.fail().message("下单失败,请重新操作");
    }
    //删除下单信息 临时订单
redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS).delete(userId);
    //添加一个用户订单记录  下单记录  总订单
redisTemplate.boundHashOps(RedisConst.SECKILL_ORDERS_USERS).put(userId, orderId.toString());

    return Result.ok(orderId);
}

页面提交订单代码片段

submitOrder() {
    seckill.submitOrder(this.order).then(response => {
        if (response.data.code == 200) {
            window.location.href = 'http://payment.gmall.com/pay.html?orderId=' + response.data.data
        } else {
            alert(response.data.message)
        }

    })
},

 

 

 

说明:下单成功后,后续流程与正常订单一致

(7)秒杀结束清空redis缓存

秒杀过程中我们写入了大量redis缓存,我们可以在秒杀结束或每天固定时间清楚缓存

,释放缓存空间;

实现思路:假如根据业务,我们确定每天18点所有秒杀业务结束,那么我们编写定时任务,每天18点发送mq消息,service-activity模块监听消息清理缓存

Service-task发送消息

添加常量MqConst

/**
 * 定时任务
 */
public static final String ROUTING_TASK_18 = "seckill.task.18";
//队列
public static final String QUEUE_TASK_18  = "queue.task.18";

 

编写定时任务发送消息

/**
 * 每天下午18点执行
 */
//@Scheduled(cron = "0/35 * * * * ?")
@Scheduled(cron = "0 0 18 * * ?")
public void task18() {

    rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_TASK, MqConst.ROUTING_TASK_18, "");
}

接收消息并处理

Service-activity接收消息

SeckillReceiver

//  监听删除消息!每天18点清空数据
@SneakyThrows
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = MqConst.QUEUE_TASK_18,durable = "true",autoDelete = "false"),
        exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
        key = {MqConst.ROUTING_TASK_18}
))
public void deleteRedisData(Message message, Channel channel){
    try {
        //  查询哪些商品是秒杀结束的!end_time , status = 1
        //  select * from seckill_goods where status = 1 and end_time < new Date();
        QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
        seckillGoodsQueryWrapper.eq("status",1);
        seckillGoodsQueryWrapper.le("end_time",new Date());
        List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);

        //  对应将秒杀结束缓存中的数据删除!
        for (SeckillGoods seckillGoods : seckillGoodsList) {
            //  seckill:stock:46 删除库存对应key  删除List集合
            redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId());
            //   删除预热
            // redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).delete(seckillGoods.getSkuId());
        }


        //  删除预热等数据! 主要针对于预热数据删除! 我们项目只针对一个商品的秒杀! 如果是多个秒杀商品,则不能这样直接删除预热秒杀商品的key!
              //  46 : 10:00 -- 10:30 | 47 : 18:10 -- 18:30
        redisTemplate.delete(RedisConst.SECKILL_GOODS);
        //  预下单  临时订单
        redisTemplate.delete(RedisConst.SECKILL_ORDERS);
        //  删除真正下单数据 总订单记录
        redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);




        //  修改数据库秒杀对象的状态!
        SeckillGoods seckillGoods = new SeckillGoods();
        //  1:表示审核通过 ,2:表示秒杀结束
        seckillGoods.setStatus("2");
        seckillGoodsMapper.update(seckillGoods,seckillGoodsQueryWrapper);
    } catch (Exception e) {
        e.printStackTrace();
    }
    //  手动确认消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}

用户是否抢单信息我们这里在前面存储的时候可以设置超时时间就行了,这里就不用清楚了,因为这里不好获取用户id

说明:情况redis缓存,同时更改秒杀商品活动结束

行秒杀下单,提交成功,页面通过轮询后台方法查询秒杀状态

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

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

相关文章

1份可以派上用场丢失数据恢复的应用程序列表

无论如何&#xff0c;丢失您的宝贵数据是可怕的。您的 Android 或 iOS 设备可能由于事故、硬件损坏、存储卡问题等而丢失了数据。这就是为什么我们编制了一份可以派上用场以恢复丢失数据的应用程序列表。 如果您四处走动&#xff0c;您大多会随身携带手机或其他移动设备。这些…

豆包Python SDK接入流程

模型与价格 豆包的模型介绍可以看豆包大模型介绍&#xff0c;模型价格可以看豆包定价文档里的“模型推理” - “大语言模型” - “字节跳动”部分。 推荐使用以下模型&#xff1a; Doubao-lite-32k&#xff1a;每百万 token 的输入价格为 0.3 元&#xff0c;输出价格为 0.6 元…

vue组件($refs对象,动态组件,插槽,自定义指令)

一、ref 1.ref引用 每个vue组件实例上&#xff0c;都包含一个$refs对象&#xff0c;里面存储着对应dom元素或组件的引用。默认情况下&#xff0c;组件的$refs指向一个空对象。 2.使用ref获取dom元素的引用 <template><h3 ref"myh3">ref组件</h3&g…

手机切换IP简单方法:掌握技巧,轻松实现IP变换‌

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;对于网络访问、数据传输等方面都起着至关重要的作用。然而&#xff0c;在某些特定场景下&#xff0c;我们可能需要切换手机的IP地址&#xff0c;以满足不同的网络需求。本文将为大家介绍几种手机切换IP…

Linux —— 多线程

一、本篇重点 1.了解线程概念&#xff0c;理解线程与进程区别与联系 2.理解和学会线程控制相关的接口和操作 3.了解线程分离与线程安全的概念 4.学会线程同步。 5.学会互斥量&#xff0c;条件变量&#xff0c;posix信号量&#xff0c;以及读写锁 6.理解基于读写锁的读者写…

Kafka 3.0.0集群部署教程

1、集群规划 主机名 ip地址 node.id process.roles kafka1 192.168.0.29 1 broker,controller Kafka2 192.168.0.30 2 broker,controller Kafka3 192.168.0.31 3 broker,controller 将kafka包上传以上节点/app目录下 mkdir /app 解压kafka包 tar -zxvf kafka_…

BLE 协议之链路层

目录 一、前言二、状态和角色三、Air Interface Packets1、Preamble 字段2、Access Address 字段2.1 静态地址2.2 私有地址 3、PDU 字段3.1 Advertising Channel PDU3.1.1 Header 字段3.1.2 Payload 字段 3.2 Data Channel PDU3.2.1 Header 字段3.2.2 Payload 字段 4、CRC 字段…

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第一篇-原理】

如果想直接制作&#xff0c;请看【第二篇】内容 这次做一个这样的东西&#xff0c;通过在2DRT上实时绘制&#xff0c;生成动态的体积纹理&#xff0c;也就是可以runtime的VDB 设想的文章流程: 对原理进行学习制作体积渲染制作实时绘制 第一篇&#xff08;本篇&#xff09;是对“…

Spring MVC 基础 : 文件、cookies的接收 ,REST响应

一、接受文件 在 Spring MVC 中&#xff0c;可以使用 RequestPart 注解来接收文件。这个注解常用于处理复杂的请求&#xff0c;如同时发送 JSON 数据和文件。RequestPart 非常适用于多部分请求&#xff08;multipart requests&#xff09;&#xff0c;这在单个请求中同时发送文…

法定退休年龄计算器,看看你还有多少年退休?

最近延迟退休上了微博热搜&#xff0c;从2025年开始实行。 分享个法定退休年龄计算器&#xff0c;看看你还有多少年退休&#xff1f; 官方出品的计算器&#xff0c;网站地址 https://si.12333.gov.cn/304647.jhtml 输入出生日期和性别就可以。 当然也可以在微信的城市服务。…

基于asp.net固定资产管理系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

JZ2440开发板——S3C2440的UART

以下内容源于韦东山课程的学习与整理&#xff0c;如有侵权请告知删除。 一、UART硬件简介 UART&#xff0c;全称是“Universal Asynchronous Receiver Transmitter”&#xff0c;即“通用异步收发器”&#xff0c;也就是我们日常说的“串口”。 它在嵌入式中用途非常广泛&…

【计算机网络篇】物理层

本文主要介绍计算机网络第二章节的物理层&#xff0c;文中的内容是我认为的重点内容&#xff0c;并非所有。参考的教材是谢希仁老师编著的《计算机网络》第8版。跟学视频课为河南科技大学郑瑞娟老师所讲计网。 文章目录 &#x1f3af;一.基本概念及公式 &#x1f383;基本概念…

力扣-1035不相交的线(Java详细题解)

题目链接&#xff1a;力扣-1035不相交的线 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 dp五部曲。 1.确定dp数组和i下标的含义。 2.确定递推公式。 3.dp初始化。 4.确定dp的遍历顺序。 5.如果没有ac打印dp数组 利于debug。 每一…

安全基础学习-AES128加密算法

前言 AES&#xff08;Advanced Encryption Standard&#xff09;是对称加密算法的一个标准&#xff0c;主要用于保护电子数据的安全。AES 支持128、192、和256位密钥长度&#xff0c;其中AES-128是最常用的一种&#xff0c;它使用128位&#xff08;16字节&#xff09;的密钥进…

充电宝什么品牌比较好?五大性价比高充电宝品牌推荐!

在这个电子设备无处不在的时代&#xff0c;充电宝已成为我们生活中不可或缺的重要配件。无论是通勤、旅行还是户外休闲&#xff0c;充电宝都能为我们的手机、平板等提供及时的电量补充&#xff0c;确保我们时刻保持在线。一个安全可靠的充电宝&#xff0c;不仅能为我们的设备提…

GRU(门控循环单元)的原理与代码实现

1.GRU的原理 1.1重置门和更新门 1.2候选隐藏状态 1.3隐状态 2. GRU的代码实现 #导包 import torch from torch import nn import dltools#加载数据 batch_size, num_steps 32, 35 train_iter, vocab dltools.load_data_time_machine(batch_size, num_steps)#封装函数&…

VScode开发GD32移植(标准库通用),保姆级!!!!!!!

VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录 VScode开发GD32移植(标准库通用)&#xff0c;保姆级&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#…

[产品管理-30]:NPDP新产品开发 - 29 - 产品生命周期管理 - 可持续产品创新

目录 一、可持续开发与可持续创新 可持续开发 可持续创新 可持续开发与可持续创新的关系 二、循环经济 1、循环经济的定义 2、循环经济的起源与发展 3、循环经济的原则 4、循环经济的实践案例 5、循环经济的意义与展望 三、三重底线 1. 财务底线 - 企业。赚钱 2. …

11. DPO 微调示例:根据人类偏好优化LLM大语言模型

在部署大模型之后&#xff0c;我们必然要和微调打交道。现在大模型的微调有非常多的方法&#xff0c;过去的文章中提到的微调方法通常依赖于问题和答案对&#xff0c;标注成本较高。 2023 年所提出的 Direct Preference Optimization&#xff08;DPO&#xff09;为我们提供了一…