Spring Cloud【Finchley】实战-02订单微服务(下)

简介: Spring Cloud【Finchley】实战-02订单微服务(下)
package com.artisan.order.repository;
import com.artisan.order.ArtisanOrderApplicationTests;
import com.artisan.order.domain.OrderDetail;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component
public class OrderDetailRepositoryTest extends ArtisanOrderApplicationTests {
    @Autowired
    private OrderDetailRepository orderDetailRepository;
    @Test
    public void testSave() {
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setDetailId("1111");
        orderDetail.setOrderId("111111");
        orderDetail.setProductIcon("http://xxx.com");
        orderDetail.setProductId("22222");
        orderDetail.setProductName("拿铁");
        orderDetail.setProductPrice(new BigDecimal(0.01));
        orderDetail.setProductQuantity(2);
        OrderDetail result = orderDetailRepository.save(orderDetail);
        Assert.assertTrue(result != null);
    }
}


20190323200008446.png


20190323200140822.png

单元测试 通过。


Service层


分析下,我们要往artisan_order 和 order_detail中写入数据,肯定要传入Order和OrderDetail实体类,类似于 createOrder(Order order , OrderDetail orderDetail) ,根据业务规则,一个Order中可能有多个OrderDetail, 所以入参OrderDetail 必须是个集合,并且返回结果也不好定义。 因此我们将这俩合并一下,封装成DTO来使用,作为入参和返回结果。


Order 和OrderDetail 合并为一个DTO对象

下图中类上少儿个注解 @Data,注意补上


20190323204244218.png


OrderService接口和实现类

package com.artisan.order.service;
import com.artisan.order.dto.OrderDTO;
public interface OrderService {
    OrderDTO createOrder(OrderDTO orderDTO);
}


我们来分析下前端的请求

    "name": "xxx",
    "phone": "xxxx",
    "address": "xxxx",
    "openid": "xxxx", //用户的微信openid
    "items": [
        {
            "productId": "xxxxxx",
            "productQuantity": 2   //购买数量
        }
    ]


结合业务逻辑

  • 校验前台入参
  • 查询商品信息(调用商品微服务)
  • 计算订单总价
  • 扣减库存(调用商品微服务)
  • 订单入库

逐一分析下目前的可行性

  • 参数校验,我们放在Controller层校验,所以Service层这里不写
  • 调用微服务的,我们目前还不具备,没法做
  • 计算订单总价,前台入参仅仅传了ProductId, 而Product的数据需要调用商品微服务,目前没法做
  • 订单入库,其实分两部分,第一个是artisan_order表,第二个是Order_detail表。 order_detail表包含了Product的内容,目前也是做不了。


综合分析,目前在Service层能做的仅仅是 入库artisan_order表

那在实现类里,我们先实现部分吧

package com.artisan.order.service.impl;
import com.artisan.order.domain.Order;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.OrderStatusEnum;
import com.artisan.order.enums.PayStatusEnum;
import com.artisan.order.repository.OrderDetailRepository;
import com.artisan.order.repository.OrderRepository;
import com.artisan.order.service.OrderService;
import com.artisan.order.utils.KeyUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderRepository orderRepository;
    @Autowired
    OrderDetailRepository orderDetailRepository;
    @Override
    public OrderDTO createOrder(OrderDTO orderDTO) {
        // TODO 查询商品信息(调用商品微服务)
        // TODO  计算订单总价
        // TODO  扣减库存(调用商品微服务)
        //订单入库
        Order order = new Order();
        orderDTO.setOrderId(KeyUtil.genUniqueKey());
        // 复制属性
        BeanUtils.copyProperties(orderDTO, order);
        // 设置其他属性
        order.setOrderAmount(new BigDecimal("100")); // TODO 后需要修改
        order.setOrderStatus(OrderStatusEnum.NEW.getCode());
        order.setPayStatus(PayStatusEnum.WAIT.getCode());
        orderRepository.save(order);
        return orderDTO;
    }
}


Controller层

这里仅列出关键代码,其余请参考github

package com.artisan.order.form;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import java.util.List;
@Data
public class OrderForm {
    /**
     * 对应
     *
     * {
     *     "name": "xxx",
     *     "phone": "xxxx",
     *     "address": "xxxx",
     *     "openid": "xxxx", //用户的微信openid
     *     "items": [
     *         {
     *             "productId": "xxxxxx",
     *             "productQuantity": 2   //购买数量
     *         }
     *     ]
     * }
     *
     *
     *
     */
    /**
     * 买家姓名
     */
    @NotEmpty(message = "姓名必填")
    private String name;
    /**
     * 买家手机号
     */
    @NotEmpty(message = "手机号必填")
    private String phone;
    /**
     * 买家地址
     */
    @NotEmpty(message = "地址必填")
    private String address;
    /**
     * 买家微信openid
     */
    @NotEmpty(message = "openid必填")
    private String openid;
    /**
     * 购物车
     */
    @NotEmpty(message = "购物车不能为空")
    private String items;
}


package com.artisan.order.converter;
import com.artisan.order.domain.OrderDetail;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.ResultEnum;
import com.artisan.order.exception.OrderException;
import com.artisan.order.form.OrderForm;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class OrderForm2OrderDTOConverter {
    public static OrderDTO convert(OrderForm orderForm) {
        Gson gson = new Gson();
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setBuyerName(orderForm.getName());
        orderDTO.setBuyerPhone(orderForm.getPhone());
        orderDTO.setBuyerAddress(orderForm.getAddress());
        orderDTO.setBuyerOpenid(orderForm.getOpenid());
        List<OrderDetail> orderDetailList = new ArrayList<>();
        try {
            // fromJson 从Json相关对象到Java实体的方法 ,转换成列表类型
            orderDetailList = gson.fromJson(orderForm.getItems(),
                    new TypeToken<List<OrderDetail>>() {
                    }.getType());
        }catch(Exception e){
            log.error("【json转换】错误, string={}", orderForm.getItems());
            throw new OrderException(ResultEnum.PARAM_ERROR);
        }
        orderDTO.setOrderDetailList(orderDetailList);
        return orderDTO;
    }
}
package com.artisan.order.controller;
import com.artisan.order.converter.OrderForm2OrderDTOConverter;
import com.artisan.order.dto.OrderDTO;
import com.artisan.order.enums.ResultEnum;
import com.artisan.order.exception.OrderException;
import com.artisan.order.form.OrderForm;
import com.artisan.order.service.OrderService;
import com.artisan.order.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController {
    @Autowired
    OrderService orderService;
    @PostMapping("/create")
    public Result create(@Valid  OrderForm orderForm,
                         BindingResult bindingResult) {
        if (bindingResult.hasErrors()){
            log.error("【Create Order】参数不正确, orderForm={}", orderForm);
            throw new OrderException(ResultEnum.PARAM_ERROR.getCode(),
                    bindingResult.getFieldError().getDefaultMessage());
        }
        // orderForm -> orderDTO
        OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
        if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
            log.error("【Create Order】购物车信息为空");
            throw new OrderException(ResultEnum.CART_EMPTY);
        }
        OrderDTO result = orderService.createOrder(orderDTO);
        Map<String, String> map = new HashMap<>();
        map.put("orderId", result.getOrderId());
        return Result.success(map);
    }
}

测试

使用PostMan


20190324001633919.png

查看数据库


20190324001954874.png

OK

说明下: x-www-form-urlencoded 这种格式 就是application/x-www-from-urlencoded,会将表单内的数据转换为键值对,比如:name=artisan&phone=123


20190324001914565.png


知识点总结

Gson库

谷歌提供的 JSON – Java Object 相互转换的 Java序列化/反序列化库。

将Json转换为对象

2019032400325051.png

解析json数组

orderDetailList = gson.fromJson(orderForm.getItems(),
                    new TypeToken<List<OrderDetail>>() {
                    }.getType());


更多示例参考


将Java对象转换为Json


20190324003431482.png


Github


https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan-product

相关文章
|
28天前
|
XML Java 数据库连接
spring boot 参数的过滤注解与实战
在Spring Boot应用中,对于入参的过滤,通常会涉及到对Web层的数据验证和处理。Spring Boot借助Spring框架提供了强大的验证框架支持,主要基于JSR-303/JSR-380(Bean Validation API)规范,以及Spring自身的@Valid或@Validated注解来实现请求参数的验证。以下是一些常见的使用案例来展示如何对参数进行过滤和验证。
24 1
|
21天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
44 1
|
21天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
42 0
|
3天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
7天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
23 0
切面编程的艺术:Spring动态代理解析与实战
|
11天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
11天前
|
SpringCloudAlibaba Java API
SpringCloud Alibaba微服务工程搭建(保姆级)
SpringCloud Alibaba微服务工程搭建(保姆级)
|
11天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
|
18天前
|
负载均衡 网络协议 Java
构建高效可扩展的微服务架构:利用Spring Cloud实现服务发现与负载均衡
本文将探讨如何利用Spring Cloud技术实现微服务架构中的服务发现与负载均衡,通过注册中心来管理服务的注册与发现,并通过负载均衡策略实现请求的分发,从而构建高效可扩展的微服务系统。
|
21天前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
38 0