文章目录

  • Spring Cloud【Finchley】专栏
  • 概述
  • 数据模型-订单微服务
  • API
  • 业务逻辑分析
  • 搭建订单微服务
    • 依赖及配置文件
      • pom.xml
      • application.yml
    • 将微服务注册到注册中心
    • 实体类
      • Order
      • OrderDetail
    • Dao层
      • OrderRepository
      • OrderDetailRepository
      • 单元测试
    • Service层
      • Order 和OrderDetail 合并为一个DTO对象
      • OrderService接口和实现类
    • Controller层
    • 测试
  • 知识点总结
    • Gson库
      • 将Json转换为对象
        • 解析json数组
      • 将Java对象转换为Json
  • Github

Spring Cloud【Finchley】专栏

如果还没有系统的学过Spring Cloud ,先到我的专栏去逛逛吧

Spring Cloud 【Finchley】手札


概述

这里我们简单的说下业务相关的需求,重点是体会微服务这种理念是如何落地的。


数据模型-订单微服务

通常来讲,微服务都是分数据库的。这里我们新建个数据库给订单微服务 ,数据库实例名 o2o-order


-- ----------------------------
-- Table structure for order
-- ----------------------------
-- 订单
create table `artisan_order` (`order_id` varchar(32) not null,`buyer_name` varchar(32) not null comment '买家名字',`buyer_phone` varchar(32) not null comment '买家电话',`buyer_address` varchar(128) not null comment '买家地址',`buyer_openid` varchar(64) not null comment '买家微信openid',`order_amount` decimal(8,2) not null comment '订单总金额',`order_status` tinyint(3) not null default '0' comment '订单状态, 默认为新下单',`pay_status` tinyint(3) not null default '0' comment '支付状态, 默认未支付',`create_time` timestamp not null default current_timestamp comment '创建时间',`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间',primary key (`order_id`),key `idx_buyer_openid` (`buyer_openid`)
);-- ----------------------------
-- Table structure for order_detail
-- ----------------------------
-- 订单详情
create table `order_detail` (`detail_id` varchar(32) not null,`order_id` varchar(32) not null,`product_id` varchar(32) not null,`product_name` varchar(64) not null comment '商品名称',`product_price` decimal(8,2) not null comment '当前价格,单位分',`product_quantity` int not null comment '数量',`product_icon` varchar(512) comment '小图',`create_time` timestamp not null default current_timestamp comment '创建时间',`update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间',primary key (`detail_id`),key `idx_order_id` (`order_id`)
);

订单与订单详情是一对多的关系,一个订单中可能包含多个订单详情,比如我下一个订单,这个订单中买了1杯奶茶、2杯可乐等。

order_detail中不仅设计了product_id,同时也冗余了 product_name product_price product_icon等,主要是考虑到有些促销活动这些字段会经常更改这些因素。


API

请求:

POST方式 /order/create

内容:

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

后端尽量少依赖前端传递的数据,为了安全起见,产品相关的数据,只传递了一个productId和productQuantity,而没有将价格、描述等等一并传递,不传递就不会被篡改,也减少了交互数据的大小。

返回:

{"code": 0,"msg": "成功","data": {"orderId": "123456" }
}

业务逻辑分析

  1. 校验前台入参
  2. 查询商品信息(调用商品微服务
  3. 计算订单总价
  4. 扣减库存(调用商品微服务
  5. 订单入库

搭建订单微服务

依赖及配置文件

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.artisan</groupId><artifactId>artisan_order</artifactId><version>0.0.1-SNAPSHOT</version><name>artisan_order</name><description>Order</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

application.yml

这里我们连接到 order微服务的数据库。 我这里本地环境,就新建了个数据库实例。

server:port: 8081spring:application:name: artisan-order# datasourcedatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/o2o-order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: root#jpajpa:show-sql: true
# Eureka
eureka:client:service-url:defaultZone: http://localhost:8761/eureka/

将微服务注册到注册中心

application.yml中配置了Eureka的信息后,我们在启动类增加@EnableEurekaClient即可

启动注册中心微服务,启动该服务

访问 http://localhost:8761/

注册成功


实体类

Order

package com.artisan.order.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.util.Date;@Data
// 必不可少
@Entity
@Table(name = "artisan_order")
public class Order {/*** 订单id.*/@Idprivate String orderId;/*** 买家名字.*/private String buyerName;/*** 买家手机号.*/private String buyerPhone;/*** 买家地址.*/private String buyerAddress;/*** 买家微信Openid.*/private String buyerOpenid;/*** 订单总金额.*/private BigDecimal orderAmount;/*** 订单状态, 默认为0新下单.*/private Integer orderStatus;/*** 支付状态, 默认为0未支付.*/private Integer payStatus;/*** 创建时间.*/private Date createTime;/*** 更新时间.*/private Date updateTime;
}

OrderDetail

package com.artisan.order.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;@Data// 必不可少
@Entity
// 如果实体类是OrderDetail,表名是order_detail,则这个注解可省略
@Table(name = "order_detail")
public class OrderDetail {// 必不可少@Idprivate String detailId;/*** 订单id.*/private String orderId;/*** 商品id.*/private String productId;/*** 商品名称.*/private String productName;/*** 商品单价.*/private BigDecimal productPrice;/*** 商品数量.*/private Integer productQuantity;/*** 商品小图.*/private String productIcon;
}

Dao层

创建订单无非就是往这两个表里写入数据。直接利用jpa提供的save方法即可。

OrderRepository

空实现 ,利用jpa本身提供的save方法

package com.artisan.order.repository;import com.artisan.order.domain.Order;
import org.springframework.data.jpa.repository.JpaRepository;// JpaRepository<Order,String>  第一个是要操作的对象,第二个是实体类中标注的@Id的字段的类型 (主键类型)
public interface OrderRepository extends JpaRepository<Order,String> {}

OrderDetailRepository

空实现 ,利用jpa本身提供的save方法

package com.artisan.order.repository;import com.artisan.order.domain.OrderDetail;
import org.springframework.data.jpa.repository.JpaRepository;public interface OrderDetailRepository extends JpaRepository<OrderDetail ,String> {}

单元测试

OrderRepositoryTest

package com.artisan.order.repository;import com.artisan.order.ArtisanOrderApplicationTests;
import com.artisan.order.domain.Order;
import com.artisan.order.enums.OrderStatusEnum;
import com.artisan.order.enums.PayStatusEnum;
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 OrderRepositoryTest  extends ArtisanOrderApplicationTests {@Autowiredprivate OrderRepository orderRepository;@Testpublic void testSave(){Order order = new Order();order.setOrderId("1222");order.setBuyerName("artisan");order.setBuyerPhone("123445664");order.setBuyerAddress("Artisan Tech");order.setBuyerOpenid("11112233");order.setOrderAmount(new BigDecimal(3.9));order.setOrderStatus(OrderStatusEnum.NEW.getCode());order.setPayStatus(PayStatusEnum.WAIT.getCode());Order result = orderRepository.save(order);Assert.assertNotNull(result);}
}

数据库记录


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 {@Autowiredprivate OrderDetailRepository orderDetailRepository;@Testpublic 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);}
}

单元测试 通过。


Service层

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


Order 和OrderDetail 合并为一个DTO对象

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


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 {@AutowiredOrderRepository orderRepository;@AutowiredOrderDetailRepository orderDetailRepository;@Overridepublic 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 {@AutowiredOrderService 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 -> orderDTOOrderDTO 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

查看数据库

OK

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


知识点总结

Gson库

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

将Json转换为对象

解析json数组

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

更多示例参考


将Java对象转换为Json


Github

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

Spring Cloud【Finchley】实战-02订单微服务相关推荐

  1. Spring Cloud Alibaba 学无止境:下一代微服务架构的规划与展望

    终于到了课程的最后一讲,本讲我们将对整个微服务架构和 Spring Cloud Alibaba 进行总结和展望.Spring Cloud Alibaba 是优秀的国产微服务架构解决方案,在基于 Spr ...

  2. spring cloud java b2b2c o2o分布式 微服务电子商务平台

    大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B.B2C.C2C.O2O.新零售.直播电商等子平台. 需要JAVA Spring Cloud大型企业分布式 ...

  3. Spring Cloud【Finchley】- 21 Spring Cloud Stream 构建消息驱动微服务

    文章目录 概述 添加依赖 配置文件配置RabbitMQ的地址信息 接口定义 接收方 @EnableBinding @StreamListener 测试 消费组 发送复杂对象 消息回执 代码 概述 官网 ...

  4. Dubbo将积极适配Spring Cloud生态,Spring Cloud体系或将成为微服务的不二选择!

    2016年,我在博客中发表过一篇<微服务架构的基础框架选择:Spring Cloud还是Dubbo?>(http://blog.didispace.com/microservice-fra ...

  5. spring解耦_云端时代的解耦:使用Spring Cloud Azure构建云端原生微服务

    spring解耦 重要要点 云本机应用程序应充分利用云的优势,而不仅仅是迁移到云中 通过在云计算环境上运行,微服务与云原生环境并驾齐驱 集中配置,服务发现,异步消息驱动和分布式跟踪是微服务基础架构 S ...

  6. Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用

    文章目录 Spring Cloud[Finchley]专栏 概述 HTTP方式之RestTemplate 方式一 (直接使用restTemplate访问URL,url写死) 方式二 (使用LoadBa ...

  7. 基于 Spring Cloud + Vue.js完整的微服务架构实战

    介绍 本项目是一个基于 Spring Boot.Spring Cloud.Spring Oauth2 和 Spring Cloud Netflix 等框架构建的微服务项目 前端框架为Vue.js. E ...

  8. SpringCloud Alibaba 实战之《服务门户:Spring Cloud Gateway 如何把好微服务的大门》

    上一讲我们学习了 Dubbo 是如何与 Nacos 协同作业.通过对比 RESTful 与 RPC,我们介绍了两种通信方式的区别,再通过实例讲解如何将 Dubbo 与 Nacos 进行整合.但你是否发 ...

  9. 疯狂Spring Cloud连载(29)微服务跟踪概述

    本文节选自<疯狂Spring Cloud微服务架构实战> 京东购买地址::https://item.jd.com/12256011.html 当当网购买地址::http://product ...

最新文章

  1. 基于点线特征避免单目视觉SLAM的退化
  2. python学习详解_深入解析Python小白学习【操作列表】
  3. 7-19晚牛客网刷题未知点、错题 集合
  4. java对象怎么创建_java对象是怎么创建出来的
  5. mysql限制登录次数_mysql会话控制限制登录次数(connection_control插件)
  6. 万博java_构建高效的企业级Java应用系列(一)架构篇——1
  7. 32位jdk_详解在Linux系统中安装JDK
  8. python的map函数和reduce_python函数_map()、filter()和reduce()
  9. 深度学习笔记--激活函数:sigmoid,maxout
  10. win7设置固定IP重启后无法上网,ipconfig显示为自动配置IPV4 169.254的地址
  11. Adobe Photoshop CC已停止工作重装显卡驱动解决方法
  12. 触动精灵 PLIST 文件读取 详解
  13. 电脑软件测试英雄联盟,揭秘英雄联盟的自动化测试
  14. python懒人小工具:python打包exe 小工具
  15. 完全认识计算机网络之TCP/IP
  16. IE常见问题解决方案大全
  17. MacOS打印自定义尺寸配置教程
  18. 四轴平面机器人手眼标定方法,eye-in-hand,亲测可用(草稿,后期整理)
  19. 努力学习,天天向上!
  20. mac pdf去水印_PDF水印工具Mac版

热门文章

  1. php jquery选择器,常用jQuery选择器总结_jquery
  2. 计算机动画制作 实验要求,有关计算机动画设计课程教学的对比试验
  3. vins中imu融合_双目版 VINS 项目发布,小觅双目摄像头作为双目惯导相机被推荐...
  4. tf.nn.conv2d 与tf.layers.conv2d的区别
  5. 47. Leetcode 107 - 二叉树的层次遍历 ii (二叉树-二叉树遍历)
  6. pytorch笔记:Dataloader
  7. ClickHouse 在字节跳动广告场景的应用
  8. Part2_2 Bs4常见操作
  9. 【android4.3】记一次完整的android源码截屏事件的捕获(不同于网上的老版本)
  10. 剖析Elasticsearch集群系列第一篇 Elasticsearch的存储模型和读写操作