03 订单微服务与商品微服务之间的调用

      应用之间的通行的主要两种方式:

  • RPC – 代表 Dubbo (可以基于TCP协议,也可以基于HTTP协议)
  • HTTP --代表 Spring Cloud (基于HTTP协议)

    HTTP方式之RestTemplate

    在order微服务调用product微服务,product作为服务端,先对外暴露个测试接口

    @GetMapping("showInfo")
    public String  showInfo(){
        return "info from product server";
    }

   方式一:直接使用RestTemplate访问url,url写死

    @GetMapping("requestServer")
    public String requestServer(){
        RestTemplate restTemplate = new RestTemplate();
        String message = restTemplate.getForObject("http://localhost:8080/product/showInfo", String.class);
        log.info("message from server:"+message);
        return message;
    }

  写死不够灵活

  方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)

   @GetMapping("requestServer")
    public String requestServer(){
        RestTemplate restTemplate = new RestTemplate();
        ServiceInstance serviceInstance = loadBalancerClient.choose("production");

        String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/showInfo");
        log.info("请求的url:{}",url);
        String message = restTemplate.getForObject(url, String.class);
        log.info("message from server:"+message);
        return message;
    }

  loadBalancerClient.choose("PRODUCT"); 通过loadBalancerClient 选择 注册到Eurek Server上的ip . 需要填写注册到注册中心的名字PRODUCT。

  方式三:使用@LoadBalanced注解

  先初始化RestTemplate , 标注 @LoadBalanced 注解

    @GetMapping("requestServer")
    public String requestServer(){
        String message = restTemplate.getForObject("http://production/product/showInfo", String.class);
        log.info("message from server:"+message);
        return message;
    }

  Fegin 的使用

  在作为客户端的order微服务中, 步骤如下

  1. 添加依赖
  2. 添加注解@EnableFeignClients
  3. 开发接口
  4. 使用

  添加依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

  添加@EnableFeignClients

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderApplication {

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

}

  编写接口

package com.smart.order.controller;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name="production")
public interface ProductClient {

    @GetMapping("/product/showInfo")
    String getServerInfo();
}

  使用

@RestController
@Slf4j
@RequestMapping("/order")
public class FeginClientController {

    @Autowired
    private ProductClient productClient;

    @GetMapping("/getServerInfoByFeign")
    public String requestServer(){

        String msg = productClient.getServerInfo();

        log.info("msg from server : {}", msg);
        return msg;
    }
}

  Product微服务查询商品列表功能开发

 *   {
 *       "name":"xxx",
 *       "phone":"xxx",
 *       "address":"xxx",
 *       "openid":"xxx",
 *       "items":[
 *           "productId":"xxxx",
 *           "productQuantity":"2"
 *       ]
 *   }

   关于商品的信息,productId是个集合,那么就需要提供一个根据传入的productId列表来返回product集合的功能。

  DAO

public interface ProductRepository  extends JpaRepository<Product,String>{
    List<Product> findByProductStatus(Integer productStatus);

    /**
     * 根据productId查询商品列表
     * @param productIds
     * @return
     */
    List<Product> findByProductId(List<String> productIds);
}

  Service层

public interface ProductService {
    List<Product> getAllUpProduct();

    List<Product> getProductList(List<String> productIds);
}

  实现类

    @Override
    public List<Product> getProductList(List<String> productIds) {
        return productRepository.findByProductId(productIds);
    }

  Controller

    @PostMapping("productListForOrder")
    public List<Product> productListForOrder(@RequestBody List<String> productIds){
        return productService.getProductList(productIds);
    }

  Order微服务调用接口查询商品列表

增加接口方法

@FeignClient(name="production")
public interface ProductClient {

    @GetMapping("/product/productListForOrder")
    List<Product> getProductForOrder(List<String> productIds);
}

  Controller层

    @GetMapping("productList")
    public String getProductList(){
        List<Product> productList = productClient.getProductForOrder(Arrays.asList("1,2"));
        log.info("获取产品:"+productList);
        return JSON.toJSONString(productList);
    }

  Product微服务减库存功能开发

  减库存的参数 DTO封装,前台会传递什么

 *   {
 *       "name":"xxx",
 *       "phone":"xxx",
 *       "address":"xxx",
 *       "openid":"xxx",
 *       "items":[
 *           "productId":"xxxx",
 *           "productQuantity":"2"
 *       ]
 *   }

  某个产品 扣除多少个数量。 []可以传递多个,对于后台来讲是个集合 。

  Product微服务需要两个参数 productId 和 productQuantity

package com.smart.order.dto;

import lombok.Data;

@Data
public class CartDTO {
    private String productId;
    private Integer productQuantity;

    public CartDTO() {
    }

    public CartDTO(String productId, Integer productQuantity) {
        this.productId = productId;
        this.productQuantity = productQuantity;
    }
}

Service

分析下,扣减库存,直接使用JPA内置的方法即可,DAO层可以省略

  @Transactional
    public void decreaseProduct(List<CartDTO> cartDTOList) {
        for (CartDTO cartDTO : cartDTOList) {
            Optional<Product> productOptional = productRepository.findById(cartDTO.getProductId());
            if (!productOptional.isPresent()) {
                throw new ProductException(ErrorCode.PRODUCT_NOT_EXIST);
            }
            Product product = productOptional.get();
            Integer leftStock = product.getProductStock() - cartDTO.getProductQuantity();
            if (leftStock < 0) {
                throw new ProductException(ErrorCode.PRODUCT_STOCK_ERROR);
            }
            product.setProductStock(leftStock);
            productRepository.save(product);
        }
    }

  Controller层

    @PostMapping("decreaseProduct")
    public void decreaseProduct(@RequestBody List<CartDTO> cartDTOS){
         productService.decreaseProduct(cartDTOS);
    }

  Order微服务调用接口扣减库存

增加接口方法

  ProductClient接口新增方法

@FeignClient(name="production")
public interface ProductClient {

    @PostMapping("/product/decreaseProduct")
    void decreaseProduct(List<CartDTO> cartDTOS);
}

  整合业务逻辑

@Override
    public OrderDTO createOrder(OrderDTO orderDTO) {

        String orderId = KeyUtil.genUniqueKey();

        //查询商品信息
        List<String> productIds = orderDTO.getOrderDetailList().stream()
                .map(OrderDetail::getProductId)
                .collect(Collectors.toList());
        List<Product> productList = productClient.getProductForOrder(productIds);

        //计算订单总价
        BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO);
        for(OrderDetail orderDetail : orderDTO.getOrderDetailList()){
            for(Product product : productList){
                if(product.getProductId().equals(orderDetail.getProductId())){
                    orderAmount=product.getProductPrice()
                            .multiply(new BigDecimal(orderDetail.getProductQuantity()))
                            .add(orderAmount);
                    BeanUtils.copyProperties(product,orderDetail);
                    orderDetail.setOrderId(orderId);
                    orderDetail.setDetailId(KeyUtil.genUniqueKey());
                    orderDedetailRepository.save(orderDetail);
                }
            }
        }
        //减库存
        List<CartDTO> cartDTOS = orderDTO.getOrderDetailList().stream()
                .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
                .collect(Collectors.toList());
        productClient.decreaseProduct(cartDTOS);

        //订单入库
        Order order = new Order();
        orderDTO.setOrderId(orderId);
        BeanUtils.copyProperties(orderDTO,order);
        order.setOrderAmount(new BigDecimal(100));
        order.setOrderStatus(OrderStatus.NEW.getCode());
        order.setPayStatus(PayStatus.WAIT.getCode());
        orderRepository.save(order);
        return orderDTO;
    }

  测试:

 

   

 

 

   

 

  

posted on 2019-09-24 09:18  溪水静幽  阅读(962)  评论(0)    收藏  举报