一、将Product服务增加到配置中心
1、添加引用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
2、修改application.yml 为bootstrap.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: product
cloud:
config:
discovery:
enabled: true
service-id: CONFIG
profile: dev
3、在码云中添加product-dev.yml文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/SpringCloud_Sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
4、访问http://localhost:8080/product-dev.yml

5、启动Product服务
二、添加消息队列到Product服务
1、添加引用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、添加MQ配置

3、在扣库存的地方发送消息
1)发送MQ消息
@Service
public class ProductServiceImpl implements ProductService{
@Autowired
private ProductInfoRepository productInfoRepository;
@Autowired
private AmqpTemplate amqpTemplate;
/**
* 扣库存
*
* @param cartDTOList
*/
@Override
@Transactional //由于扣库存是list操作,所以需要事务操作
public void decreaseStock(List<CartDTO> cartDTOList) {
for(CartDTO cartDTO : cartDTOList){
Optional<ProductInfo> productInfoOptional = productInfoRepository.findById(cartDTO.getProductId());
//判断商品是否存在
if(!productInfoOptional.isPresent()){
throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);
}
ProductInfo productInfo = productInfoOptional.get();
//库存是否足够 数据库里的库存-购物车中的数量
Integer result = productInfo.getProductStock() - cartDTO.getProductQuantity();
if(result <= 0){
throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);
}
productInfo.setProductStock(result);
productInfoRepository.save(productInfo);
// 发送MQ消息
amqpTemplate.convertAndSend("productInfo", JsonUtil.toJson(productInfo));
}
}
}
之后将发送的对象改为ProductInfoOutput
ProductInfoOutput output = new ProductInfoOutput();
BeanUtils.copyProperties(productInfo,output);
// 发送MQ消息
amqpTemplate.convertAndSend("productInfo", JsonUtil.toJson(output));
2)建立prouductInfo队列,并观察

3、调用接口
@PostMapping("/decreaseStock")
public void decreaseStock(@RequestBody List<CartDTO> cartDTOList){
productService.decreaseStock(cartDTOList);
}
使用PostMan调用

最终测试结果,发现2)中的消息队列有了消息。然后查看消息的内容

三、在Order服务中接收消息
@Component
@Slf4j
public class ProductInfoReceiver {
@RabbitListener(queuesToDeclare = @Queue("productInfo"))
public void process(String message){
//message 转换成 ProductInfoOutpur
ProductInfoOutput output = (ProductInfoOutput) JsonUtil.fromJson(message,ProductInfoOutput.class);
log.info("从队列[{}]接收到消息:{}","productInfo", output.toString());
}
}
接收到的结果为:

四、Order服务中将库存保存到Redis中
1)增加引用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2)增加配置

3)增加代码

4)调用减库存后,查看Reids的库存数据如下图

五、优化扣库存
1、Product服务扣库存
@Override
public void decreaseStock(List<CartDTO> cartDTOList) {
List<ProductInfo> productInfoList = decreaseStockProcess(cartDTOList);
List<ProductInfoOutput> productInfoOutputList = productInfoList.stream().map(e -> {
ProductInfoOutput output = new ProductInfoOutput();
BeanUtils.copyProperties(e,output);
return output;
}).collect(Collectors.toList());
// 发送MQ消息
amqpTemplate.convertAndSend("productInfo", JsonUtil.toJson(productInfoOutputList));
}
@Transactional //由于扣库存是list操作,所以需要事务操作
public List<ProductInfo> decreaseStockProcess(List<CartDTO> cartDTOList) {
List<ProductInfo> productInfoList = new ArrayList<>();
for(CartDTO cartDTO : cartDTOList){
Optional<ProductInfo> productInfoOptional = productInfoRepository.findById(cartDTO.getProductId());
//判断商品是否存在
if(!productInfoOptional.isPresent()){
throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);
}
ProductInfo productInfo = productInfoOptional.get();
//库存是否足够 数据库里的库存-购物车中的数量
Integer result = productInfo.getProductStock() - cartDTO.getProductQuantity();
if(result <= 0){
throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);
}
productInfo.setProductStock(result);
productInfoRepository.save(productInfo);
productInfoList.add(productInfo);
}
return productInfoList;
}
2)、订单服务接收mq消息
public class ProductInfoReceiver {
private static final String PRODUCT_STOCK_TEMPLATE = "product_stock_%s";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RabbitListener(queuesToDeclare = @Queue("productInfo"))
public void process(String message){
//message 转换成 ProductInfoOutpur
List<ProductInfoOutput> productInfoOutputList = ( List<ProductInfoOutput>) JsonUtil.fromJson(message,
new TypeReference<List<ProductInfoOutput>>() {});
log.info("从队列[{}]接收到消息:{}","productInfo", productInfoOutputList);
//将消息存到redis
for (ProductInfoOutput productInfoOutput : productInfoOutputList) {
stringRedisTemplate.opsForValue().set(String.format(PRODUCT_STOCK_TEMPLATE,
productInfoOutput.getProductId()), productInfoOutput.getProductStock().toString());
}
}
}