写在最后
为了这次面试,也收集了很多的面试题!
以下是部分面试题截图
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的体系是很庞大的,还有很多更高级的技能需要掌握,但不要着急,这些完全可以放到以后工作中边用别学。
学习编程就是一个由混沌到有序的过程,所以你在学习过程中,如果一时碰到理解不了的知识点,大可不必沮丧,更不要气馁,这都是正常的不能再正常的事情了,不过是“人同此心,心同此理”的暂时而已。
“道路是曲折的,前途是光明的!”
perties().getDeliveryTag(), false);
}
/**
* 商品下架
*
* @param skuId
最后
按照上面的过程,4个月的时间刚刚好。当然Java的体系是很庞大的,还有很多更高级的技能需要掌握,但不要着急,这些完全可以放到以后工作中边用别学。
学习编程就是一个由混沌到有序的过程,所以你在学习过程中,如果一时碰到理解不了的知识点,大可不必沮丧,更不要气馁,这都是正常的不能再正常的事情了,不过是“人同此心,心同此理”的暂时而已。
“道路是曲折的,前途是光明的!”
[外链图片转存中…(img-ADjyfoRv-1715468058480)]
[外链图片转存中…(img-LgcbUSJw-1715468058481)]