javascript
Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用
文章目录
- Spring Cloud【Finchley】专栏
- 概述
- HTTP方式之RestTemplate
- 方式一 (直接使用restTemplate访问URL,url写死)
- 方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)
- 方式三 (使用@LoadBalanced注解)
- Fegin 的使用
- 商品微服务获取商品列表功能开发
- Product微服务查询商品列表功能开发
- DAO层
- Service层
- Controller层
- Order微服务调用接口查询商品列表
- 增加接口方法
- 调用商品微服务扣库存功能开发
- Product微服务减库存功能开发
- 减库存的参数 DTO封装
- Service
- Controller层
- Order微服务调用接口扣减库存
- 增加接口方法
- 整合
- 测试
- 知识点小结
- 点1
- 点2
- Github
Spring Cloud【Finchley】专栏
如果还没有系统的学过Spring Cloud ,先到我的专栏去逛逛吧
Spring Cloud 【Finchley】手札
概述
还记得上篇博文的TODO吧
这里我们先循序渐进的了解下,微服务之间调用的几种方式
先了解下应用之间的通行的主要两种方式
- RPC – 代表 Dubbo (可以基于TCP协议,也可以基于HTTP协议)
- HTTP —代表 Spring Cloud (基于HTTP协议)
HTTP方式之RestTemplate
我们在order微服务调用product微服务。
product作为服务端,先对外暴露个测试接口
order作为客户端调用该接口
方式一 (直接使用restTemplate访问URL,url写死)
访问 http://localhost:8081/order/getServerInfoFromClient
写死的地址,并且只能请求一个,如果有多个地址就比较麻烦了。而且还是IP地址。
方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)
package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate LoadBalancerClient loadBalancerClient;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式二 (使用LoadBalancerClient通过应用名获取url,拼装请求地址,然后再使用restTemplate)RestTemplate restTemplate2 = new RestTemplate();ServiceInstance serviceInstance = loadBalancerClient.choose("ARTISAN-PRODUCT");// 获取ip port 组装urlString url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort() + "/product/serverMsg");log.info("url:{}",url);String msg = restTemplate2.getForObject(url,String.class);log.info("msg from server : {}", msg);return msg;} }loadBalancerClient.choose("ARTISAN-PRODUCT"); 通过loadBalancerClient 选择 注册到Eurek Server上的ip . 需要填写注册到注册中心的名字ARTISAN-PRODUCT。
访问 http://localhost:8081/order/getServerInfoFromClient
方式三 (使用@LoadBalanced注解)
先初始化RestTemplate , 标注 @LoadBalanced 注解
package com.artisan.order.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;@Configuration public class RestTemplateConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();} } package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式三 (使用@LoadBalanced注解)String msg = restTemplate.getForObject("http://ARTISAN-PRODUCT/product/serverMsg",String.class);log.info("msg from server : {}", msg);return msg;} }请求的地址 http://ARTISAN-PRODUCT/product/serverMsg 注册到服务中心上的服务
访问 http://localhost:8081/order/getServerInfoFromClient
Fegin 的使用
Spring Cloud【Finchley】-06服务消费者整合Feign
总体来说,在作为客户端的order微服务中, 步骤如下
pom.xml 添加依赖
添加注解@EnableFeignClients
编写client接口
package com.artisan.order.client;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping;// name为注册在注册中心上的名称 @FeignClient(name="ARTISAN-PRODUCT") public interface ProductClient {// ARTISAN-PRODUCT微服务接口的访问路径@GetMapping("/product/serverMsg")String getServerInfo(); }调用
package com.artisan.order.controller;import com.artisan.order.client.ProductClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;@RestController @Slf4j @RequestMapping("/order") public class FeginClientController {@Autowiredprivate ProductClient productClient;@GetMapping("/getServerInfoByFeign")public String requestServer(){String msg = productClient.getServerInfo();log.info("msg from server : {}", msg);return msg;} }访问 http://localhost:8081/order/getServerInfoByFeign
OK。
商品微服务获取商品列表功能开发
熟悉了基本使用后,刚开始说了,我们有几个TODO要做,那开始吧
Product微服务查询商品列表功能开发
我们看下前台会传递什么给我们
关于商品的信息,productId是个集合,那么我们就需要提供一个根据传入的productId列表来返回product集合的功能。
DAO层
老规矩,DAO层先
单元测试下,
结合库表中的数据
单元测试通过
Service层
紧接着Service层
实现类
单元测试
单元测试通过
Controller层
/*** 根据productIdList 查询商品列表* 提供给Order微服务用* @param productIdList* @return*/@PostMapping("/productListForOrder")private List<Product> getProductForOrder(@RequestBody List<String> productIdList){return productService.getProductList(productIdList);}Order微服务调用接口查询商品列表
增加接口方法
返回的类型是个Product集合,我们先从product微服务那边将Product copy一份过来。 后续会优化这些地方。
我们写个方法来测试下这个功能, 那就在刚才的用作测试的FeginClientController类中写个方法吧
当参数中标注了@RequestBody , 则必须使用POST方法
启动服务,测试下 http://localhost:8081/order/getProductList
可见功能是OK的。
调用商品微服务扣库存功能开发
Product微服务减库存功能开发
减库存的参数 DTO封装
我们看下前台会传递什么给我们
肯定是 某个产品 扣除多少个数量。 []可以传递多个,对于后台来讲是个集合 。
Product微服务需要两个参数 productId 和 productQuantity
Service
分析下,扣减库存,直接使用JPA内置的方法即可,DAO层可以省略了。
直接来Service吧 ,直接写实现类中的方法你把
@Override// 因为是对List操作,所以加个事务控制@Transactionalpublic void decreaseProduct(List<CartDTO> cartDTOList) {// 遍历CartDTOfor (CartDTO cartDTO : cartDTOList) {// 根据productId查询ProductOptional<Product> productOptional = productRepository.findById(cartDTO.getProductId());// 商品是否存在if (!productOptional.isPresent()) {throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);}// 是否库存充足Product product = productOptional.get();int leftStock = product.getProductStock() - cartDTO.getProductQuantity();if (leftStock < 0 ){throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);}// 将剩余库存设置到product,并更新数据库product.setProductStock(leftStock);productRepository.save(product);}}因为是对List操作,所以加个事务控制 @Transactional
单元测试
@Testpublic void decreaseProductTest() {CartDTO cartDTO = new CartDTO();cartDTO.setProductId("3");cartDTO.setProductQuantity(2);productService.decreaseProduct(Arrays.asList(cartDTO));}测试前数据
Controller层
Order微服务调用接口扣减库存
增加接口方法
ProductClient接口新增方法
测试下 ,在 FeginClientController 新增个方法 (这个Controller和工程无关哈,仅仅是用来测试用的)
访问 http://localhost:8081/order/decreseProduct
整合
Product微服务要提供的功能及Order微服务调用都开发完了,那整合到业务逻辑中吧
@Overridepublic OrderDTO createOrder(OrderDTO orderDTO) {String orderId = KeyUtil.genUniqueKey();// 查询商品信息(调用商品微服务)List<String> productIdList = orderDTO.getOrderDetailList().stream().map(OrderDetail::getProductId).collect(Collectors.toList());List<Product> productList = productClient.getProductForOrder(productIdList);// 计算订单总价BigDecimal orderAmout = new BigDecimal(BigInteger.ZERO);for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {for (Product product: productList) {if (product.getProductId().equals(orderDetail.getProductId())) {//单价*数量orderAmout = product.getProductPrice().multiply(new BigDecimal(orderDetail.getProductQuantity())).add(orderAmout);BeanUtils.copyProperties(product, orderDetail);orderDetail.setOrderId(orderId);orderDetail.setDetailId(KeyUtil.genUniqueKey());//订单详情入库orderDetailRepository.save(orderDetail);}}}// 扣减库存(调用商品微服务)List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())).collect(Collectors.toList());productClient.decreseProduct(cartDTOList);//订单入库Order order = new Order();orderDTO.setOrderId(orderId);// 复制属性BeanUtils.copyProperties(orderDTO, order);// 设置其他属性order.setOrderAmount(orderAmout);order.setOrderStatus(OrderStatusEnum.NEW.getCode());order.setPayStatus(PayStatusEnum.WAIT.getCode());orderRepository.save(order);return orderDTO;}测试
[{"productId": "1","productQuantity": 2 }, {"productId": "2","productQuantity": 5 }, {"productId": "3","productQuantity": 10 }]买 1号商品 2个 ,金额 20.99乘以2 = 41.98
买 2号商品 5个 ,金额 7.5乘以5 = 37.5
买 3号商品 10个 ,金额 15乘以10 = 150
总金额 229.48
原始库存:
使用POSTMAN测试一把
检查下总金额,库存扣减,及order_detail中的数据
artisan_order
order_detail 3条记录
库存:
OK
知识点小结
点1
Spring MVC在接收集合请求参数时,需要在Controller方法的集合参数里前添加@RequestBody
List<Product> getProductForOrder(@RequestBody List<String> productIdList)点2
当参数中标注了@RequestBody , 则必须使用POST方法
Github
artisan-product: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan_order
artisan_order: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan-product
总结
以上是生活随笔为你收集整理的Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Spring Cloud【Finchle
- 下一篇: Spring Cloud【Finchle