SpringBoot实战项目整合RabbitMQ+ElaticSearch实现SKU上下架功能_尚上优选整合es+mq实现商品上下架

写在最后

为了这次面试,也收集了很多的面试题!

以下是部分面试题截图

Java程序员秋招三面蚂蚁金服,我总结了所有面试题,也不过如此

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

public Category getCategory(@PathVariable("categoryId") Long categoryId);

//根据skuId获取sku信息
@GetMapping("/api/product/inner/getSkuInfo/{skuId}")
public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId);

}


### 3.3、开发service-search 模块接口


1、controller



import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.search.service.SkuService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**

  • 商品搜索列表接口

  • @author Eric

  • @date 2023-06-29 10:15
    */
    @RestController
    @RequestMapping(“api/search/sku”)
    public class SkuApiController {

    @Autowired
    private SkuService skuService;

    @ApiOperation(value = “上架商品”)
    @GetMapping(“inner/upperSku/{skuId}”)
    public Result upperGoods(@PathVariable(“skuId”) Long skuId) {
    skuService.upperSku(skuId);
    return Result.ok();
    }

    @ApiOperation(value = “下架商品”)
    @GetMapping(“inner/lowerSku/{skuId}”)
    public Result lowerGoods(@PathVariable(“skuId”) Long skuId) {
    skuService.lowerSku(skuId);
    return Result.ok();
    }
    }


2、service接口



/**

  • @author Eric

  • @date 2023-06-29 10:16
    /
    public interface SkuService {
    /
    *

    • 上架商品列表
    • @param skuId
      */
      void upperSku(Long skuId);

    /**

    • 下架商品列表
    • @param skuId
      */
      void lowerSku(Long skuId);
      }

3、impl



import com.alibaba.fastjson.JSON;
import com.atguigu.ssyx.enums.SkuType;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.model.search.SkuEs;
import com.atguigu.ssyx.product.client.ProductFeignClient;
import com.atguigu.ssyx.search.repository.SkuRepository;
import com.atguigu.ssyx.search.service.SkuService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**

  • @author Eric
  • @date 2023-06-29 10:16
    */

@Slf4j
@Service
public class SkuServiceImpl implements SkuService {

@Autowired
private ProductFeignClient productFeignClient;

@Autowired
private SkuRepository skuEsRepository;

@Autowired
private RestHighLevelClient restHighLevelClient;

/**
 * 上架商品列表
 *
 * @param skuId
 */
@Override
public void upperSku(Long skuId) {
    log.info("upperSku:" + skuId);
    SkuEs skuEs = new SkuEs();

    //查询sku信息
    SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
    if (null == skuInfo) return;

    // 查询分类
    Category category = productFeignClient.getCategory(skuInfo.getCategoryId());
    if (category != null) {
        skuEs.setCategoryId(category.getId());
        skuEs.setCategoryName(category.getName());
    }
    skuEs.setId(skuInfo.getId());
    skuEs.setKeyword(skuInfo.getSkuName() + "," + skuEs.getCategoryName());
    skuEs.setWareId(skuInfo.getWareId());
    skuEs.setIsNewPerson(skuInfo.getIsNewPerson());
    skuEs.setImgUrl(skuInfo.getImgUrl());
    skuEs.setTitle(skuInfo.getSkuName());
    if (skuInfo.getSkuType() == SkuType.COMMON.getCode()) {
        skuEs.setSkuType(0);
        skuEs.setPrice(skuInfo.getPrice().doubleValue());
        skuEs.setStock(skuInfo.getStock());
        skuEs.setSale(skuInfo.getSale());
        skuEs.setPerLimit(skuInfo.getPerLimit());
    } else {
        //TODO 待完善-秒杀商品

    }
    SkuEs save = skuEsRepository.save(skuEs);//往Es中新增数据
    log.info("upperSku:" + JSON.toJSONString(save));
}

/**
 * 下架商品列表
 *
 * @param skuId
 */
@Override
public void lowerSku(Long skuId) {
    this.skuEsRepository.deleteById(skuId);//删除Es中的数据
}

}


4、创建SkuRepository(用来操作Es,这里使用的是SpringData技术)



/**

  • @author Eric
  • @date 2023-06-29 10:19
    */
    // 参数一:泛型 参数二:类型,是由泛型中的主键类型而决定的
    public interface SkuRepository extends ElasticsearchRepository<SkuEs, Long> {
    }

## 4、RabbitMQ


1、创建mq模块  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/21f11ac0e71646e080eab39b05879230.png)


因为mq的作用类似于工具类,并且多处需要使用,所以我这里选择放在common模块下


2、引入MQ依赖



org.springframework.cloud spring-cloud-starter-bus-amqp

3、添加service方法



import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**

  • @author Eric
  • @date 2023-06-30 22:57
    */

@Service
public class RabbitService {

//  引入操作rabbitmq 的模板
@Autowired
private RabbitTemplate rabbitTemplate;

/**
 * 发送消息
 *
 * @param exchange   交换机
 * @param routingKey 路由键(路由key)
 * @param message    消息
 * @return
 */
public boolean sendMessage(String exchange, String routingKey, Object message) {
    //  调用发送数据的方法
    rabbitTemplate.convertAndSend(exchange, routingKey, message);
    return true;
}

/**
 * 发送延迟消息的方法
 *
 * @param exchange   交换机
 * @param routingKey 路由键
 * @param message    消息内容
 * @param delayTime  延迟时间
 * @return
 */
public boolean sendDelayMessage(String exchange, String routingKey, Object message, int delayTime) {

    //  在发送消息的时候设置延迟时间
    rabbitTemplate.convertAndSend(exchange, routingKey, message, new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            //  设置一个延迟时间
            message.getMessageProperties().setDelay(delayTime * 1000);
            return message;
        }
    });
    return true;
}

}


4、配置mq消息转换器



import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**

  • mq消息转换器(默认是字符串转换器)
    */

@Configuration
public class MQConfig {

@Bean
public MessageConverter messageConverter(){
    return new Jackson2JsonMessageConverter();
}

}


5、添加消息的确认配置(我这里配置的是手动确认模式)



import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class MQProducerAckConfig implements RabbitTemplate.ReturnCallback,RabbitTemplate.ConfirmCallback {

//  我们发送消息使用的是 private RabbitTemplate rabbitTemplate; 对象
//  如果不做设置的话 当前的rabbitTemplate 与当前的配置类没有任何关系!
@Autowired
private RabbitTemplate rabbitTemplate;

//  设置 表示修饰一个非静态的void方法,在服务器加载Servlet的时候运行。并且只执行一次!
@PostConstruct
public void init(){
    rabbitTemplate.setReturnCallback(this);
    rabbitTemplate.setConfirmCallback(this);
}

/**
 * 表示消息是否正确发送到了交换机上
 * @param correlationData   消息的载体
 * @param ack   判断是否发送到交换机上
 * @param cause 原因
 */
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    if(ack){
        System.out.println("消息发送成功!");
    }else {
        System.out.println("消息发送失败!"+cause);
    }
}

/**
 * 消息如果没有正确发送到队列中,则会走这个方法!如果消息被正常处理,则这个方法不会走!
 * @param message
 * @param replyCode
 * @param replyText
 * @param exchange
 * @param routingKey
 */
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
    System.out.println("消息主体: " + new String(message.getBody()));
    System.out.println("应答码: " + replyCode);
    System.out.println("描述:" + replyText);
    System.out.println("消息使用的交换器 exchange : " + exchange);
    System.out.println("消息使用的路由键 routing : " + routingKey);
}

}


6、RabbitMQ常量类



/**

  • 消息队列常量类
    /
    public class MqConst {
    /
    *

    • 消息补偿
      */
      public static final String MQ_KEY_PREFIX = “ssyx.mq:list”;
      public static final int RETRY_COUNT = 3;

    /**

    • 商品上下架
      */
      public static final String EXCHANGE_GOODS_DIRECT = “ssyx.goods.direct”;
      public static final String ROUTING_GOODS_UPPER = “ssyx.goods.upper”;
      public static final String ROUTING_GOODS_LOWER = “ssyx.goods.lower”;
      //队列
      public static final String QUEUE_GOODS_UPPER = “ssyx.goods.upper”;
      public static final String QUEUE_GOODS_LOWER = “ssyx.goods.lower”;

    /**

    • 团长上下线
      */
      public static final String EXCHANGE_LEADER_DIRECT = “ssyx.leader.direct”;
      public static final String ROUTING_LEADER_UPPER = “ssyx.leader.upper”;
      public static final String ROUTING_LEADER_LOWER = “ssyx.leader.lower”;
      //队列
      public static final String QUEUE_LEADER_UPPER = “ssyx.leader.upper”;
      public static final String QUEUE_LEADER_LOWER = “ssyx.leader.lower”;

    //订单
    public static final String EXCHANGE_ORDER_DIRECT = “ssyx.order.direct”;
    public static final String ROUTING_ROLLBACK_STOCK = “ssyx.rollback.stock”;
    public static final String ROUTING_MINUS_STOCK = “ssyx.minus.stock”;

    public static final String ROUTING_DELETE_CART = “ssyx.delete.cart”;
    //解锁普通商品库存
    public static final String QUEUE_ROLLBACK_STOCK = “ssyx.rollback.stock”;
    public static final String QUEUE_SECKILL_ROLLBACK_STOCK = “ssyx.seckill.rollback.stock”;
    public static final String QUEUE_MINUS_STOCK = “ssyx.minus.stock”;
    public static final String QUEUE_DELETE_CART = “ssyx.delete.cart”;

    //支付
    public static final String EXCHANGE_PAY_DIRECT = “ssyx.pay.direct”;
    public static final String ROUTING_PAY_SUCCESS = “ssyx.pay.success”;
    public static final String QUEUE_ORDER_PAY = “ssyx.order.pay”;
    public static final String QUEUE_LEADER_BILL = “ssyx.leader.bill”;

    //取消订单
    public static final String EXCHANGE_CANCEL_ORDER_DIRECT = “ssyx.cancel.order.direct”;
    public static final String ROUTING_CANCEL_ORDER = “ssyx.cancel.order”;
    //延迟取消订单队列
    public static final String QUEUE_CANCEL_ORDER = “ssyx.cancel.order”;

    /**

    • 定时任务
      */
      public static final String EXCHANGE_DIRECT_TASK = “ssyx.exchange.direct.task”;
      public static final String ROUTING_TASK_23 = “ssyx.task.23”;
      //队列
      public static final String QUEUE_TASK_23 = “ssyx.queue.task.23”;
      }

## 5、完善SKU管理商品上下架


### 5.1、商品服务


1、找到自己的商品服务,引入rabbit-util模块



com.atguigu rabbit_util 1.0-SNAPSHOT

2、同时在配置文件添加MQ配置



rabbitmq:
host: 192.168.56.101
port: 5672
username: guest
password: guest
publisher-confirm-type: CORRELATED #发布确认模式,消息是否被成功发送到交换机
publisher-returns: true
listener:
simple:
prefetch: 1
concurrency: 3
acknowledge-mode: manual #消费端手动确认


3、修改SkuInfoServiceImpl的publish方法(上下架方法)



@Autowired
private RabbitService rabbitService;

@Transactional(rollbackFor = {Exception.class})
@Override
public void publish(Long skuId, Integer status) {
// 更改发布状态
if(status == 1) {
SkuInfo skuInfoUp = new SkuInfo();
skuInfoUp.setId(skuId);
skuInfoUp.setPublishStatus(1);
skuInfoMapper.updateById(skuInfoUp);

    //商品上架:发送mq消息同步es
    rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT, MqConst.ROUTING_GOODS_UPPER, skuId);
} else {
    SkuInfo skuInfoUp = new SkuInfo();
    skuInfoUp.setId(skuId);
    skuInfoUp.setPublishStatus(0);
    skuInfoMapper.updateById(skuInfoUp);

    //商品下架:发送mq消息同步es
    rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT, MqConst.ROUTING_GOODS_LOWER, skuId);
}

}


### 5.2、es服务


在service-search服务也引入消息依赖



com.atguigu rabbit_util 1.0-SNAPSHOT

2、添加SkuReceiver接收MQ消息方法



import com.atguigu.ssyx.rabbit.constant.MqConst;
import com.atguigu.ssyx.search.service.SkuService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class SkuReceiver {

@Autowired
private SkuService skuService;

/**
 * 商品上架
 * RabbitListener  自动监听
 *
 * @param skuId
 * @param message
 * @param channel
 * @throws IOException
 */
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = MqConst.QUEUE_GOODS_UPPER, durable = "true"),
        exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
        key = {MqConst.ROUTING_GOODS_UPPER}
))
public void upperSku(Long skuId, Message message, Channel channel) throws IOException {
    System.out.println("上架:消息消费成功!");
    if (null != skuId) {
        skuService.upperSku(skuId);
    }
    /**
     * 第一个参数:表示收到的消息的标号
     * 第二个参数:如果为true表示可以签收多个消息
     */
    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}

/**
 * 商品下架
 *
 * @param skuId

最后

按照上面的过程,4个月的时间刚刚好。当然Java的体系是很庞大的,还有很多更高级的技能需要掌握,但不要着急,这些完全可以放到以后工作中边用别学。

学习编程就是一个由混沌到有序的过程,所以你在学习过程中,如果一时碰到理解不了的知识点,大可不必沮丧,更不要气馁,这都是正常的不能再正常的事情了,不过是“人同此心,心同此理”的暂时而已。

道路是曲折的,前途是光明的!”

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

perties().getDeliveryTag(), false);
}

/**
 * 商品下架
 *
 * @param skuId

最后

按照上面的过程,4个月的时间刚刚好。当然Java的体系是很庞大的,还有很多更高级的技能需要掌握,但不要着急,这些完全可以放到以后工作中边用别学。

学习编程就是一个由混沌到有序的过程,所以你在学习过程中,如果一时碰到理解不了的知识点,大可不必沮丧,更不要气馁,这都是正常的不能再正常的事情了,不过是“人同此心,心同此理”的暂时而已。

道路是曲折的,前途是光明的!”

[外链图片转存中…(img-ADjyfoRv-1715468058480)]

[外链图片转存中…(img-LgcbUSJw-1715468058481)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值