Spring Cloud【Finchley】专栏
如果还没有系统的学过Spring Cloud ,先到我的专栏去逛逛吧
概述
这里我们简单的说下业务相关的需求,重点是体会微服务这种理念是如何落地的。
数据模型-订单微服务
通常来讲,微服务都是分数据库的。这里我们新建个数据库给订单微服务 ,数据库实例名 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" } }
业务逻辑分析
- 校验前台入参
- 查询商品信息(调用商品微服务)
- 计算订单总价
- 扣减库存(调用商品微服务)
- 订单入库
搭建订单微服务
依赖及配置文件
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: 8081 spring: application: name: artisan-order # datasource datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/o2o-order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: root #jpa jpa: 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. */ @Id private 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 { // 必不可少 @Id private 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 { @Autowired private OrderRepository orderRepository; @Test public 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); } }
数据库记录