一、商品服务
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">
<!-- 父项目 -->
<parent>
<groupId>com.imooc</groupId>
<artifactId>product</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product-server</artifactId>
<name>server</name>
<description>Demo project for Spring Boot</description>
<!-- 引入依赖 -->
<dependencies>
<!-- 引入web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入eureka-client -->
<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-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 引入mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 引入lombok 可以省去get set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 引入amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 引入bus-amqp 消息队列-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!-- 引入config-client 配置中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!-- 引入Spring Cloud Stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入product-common-->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>product-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- 引入jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<!--打包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
bootstrap.yml
spring:
application:
name: product #配置服务名称
#统一配置中心
cloud:
config:
discovery:
enabled: true
service-id: CONFIG #configServer 服务的名字
profile: test
bus:
trace:
enabled: true
stream:
bindings:
input: #消息名字
group: order #消息分组 项目不管启动多少实例,都只有一个实例接收消息
content-type: application/json #序列化
product-test.yml(统一配置中心)
spring:
application:
#配置服务名称
name: product
#配置数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/SpringCloud_Sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
#消息队列
rabbitmq:
host: 192.168.3.233
username: guest
password: guest
port: 5672
# 配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
#配置端扣号
server:
port: 8804
JsonUtil
package com.imooc.product.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
//转换为json字符串
public static String toJson(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
ProductServiceImpl
package com.imooc.product.service.impl;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import com.imooc.product.dataobject.ProductInfo;
import com.imooc.product.enums.ProductStatusEnum;
import com.imooc.product.enums.ResultEnum;
import com.imooc.product.exception.ProductException;
import com.imooc.product.repository.ProductInfoRepository;
import com.imooc.product.service.ProductService;
import com.imooc.product.utils.JsonUtil;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
//商品
@Service
public class ProductServiceImpl implements ProductService {
//引入商品Dao
@Autowired
private ProductInfoRepository productInfoRepository;
//引入消息
@Autowired
private AmqpTemplate amqpTemplate;
//查询所有在架商品列表
@Override
public List<ProductInfo> findUpAll() {
return productInfoRepository.findByProductStatus(ProductStatusEnum.UP.getCode());
}
//根据productIdList查询商品列表
@Override
public List<ProductInfoOutput> findList(List<String> productIdList) {
return productInfoRepository.findByProductIdIn(productIdList).stream()
.map(e -> {
ProductInfoOutput output = new ProductInfoOutput();
BeanUtils.copyProperties(e, output);
return output;
})
.collect(Collectors.toList());
}
/**
* 扣库存并发送消息
* */
@Override
public void decreaseStock(List<DecreaseStockInput> decreaseStockInputList) {
//扣库存逻辑
List<ProductInfo> productInfoList = decreaseStockProcess(decreaseStockInputList);
//把ProductInfo转换为ProductInfoOutput
List<ProductInfoOutput> productInfoOutputList = productInfoList.stream().map(e -> {
ProductInfoOutput output = new ProductInfoOutput();
BeanUtils.copyProperties(e, output);//将e的属性值复制给output
return output;
}).collect(Collectors.toList());
//发送mq消息
amqpTemplate.convertAndSend("productInfo", JsonUtil.toJson(productInfoOutputList));
}
/**
* 扣库存逻辑
* */
@Transactional
public List<ProductInfo> decreaseStockProcess(List<DecreaseStockInput> decreaseStockInputList) {
List<ProductInfo> productInfoList = new ArrayList<>();
for (DecreaseStockInput decreaseStockInput: decreaseStockInputList) {
Optional<ProductInfo> productInfoOptional = productInfoRepository.findById(decreaseStockInput.getProductId());
//判断商品是否存在
if (!productInfoOptional.isPresent()){
throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);
}
ProductInfo productInfo = productInfoOptional.get();
//库存是否足够
Integer result = productInfo.getProductStock() - decreaseStockInput.getProductQuantity();
if (result < 0) {
throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);
}
productInfo.setProductStock(result);
productInfoRepository.save(productInfo);
productInfoList.add(productInfo);
}
return productInfoList;
}
}
二、订单服务
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>
<artifactId>order</artifactId>
<groupId>com.imooc</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-server</artifactId>
<!-- 引入依赖 -->
<dependencies>
<!-- 引入web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 引入mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 引入lombok 可以省去get set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 引入gson 转换数据-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<!-- 引入feign 应用通信-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 引入order-common-->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>order-common</artifactId>
</dependency>
<!-- 引入product-client -->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>product-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- 引入cloud-config-client 统一配置中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!-- 引入bus-amqp 消息队列-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<!-- 引入Spring Cloud Stream -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 引入redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<!-- 版本依赖管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</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>
<!-- 仓库地址 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
bootstrap.yml
spring:
application:
name: order #配置服务名称
#统一配置中心
cloud:
config:
discovery:
enabled: true
service-id: CONFIG #configServer 服务的名字
profile: dev
bus:
trace:
enabled: true
stream:
bindings:
input: #消息名字
group: order #消息分组 项目不管启动多少实例,都只有一个实例接收消息
content-type: application/json #序列化
redis:
host: 192.168.3.233
port: 6379
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
order-dev.yml(统一配置中心)
spring:
application:
#配置服务名称
name: order
#配置数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/SpringCloud_Sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
#消息队列
rabbitmq:
host: 192.168.3.233
username: guest
password: guest
port: 5672
# 配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
#配置端扣号
server:
port: 8802
#自定义负载均衡规则
#PRODUCT:
# ribbon:
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
env:
dev4
girl:
name: lili
age: 24
JsonUtil
public class JsonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* json转换为对象
*/
public static Object fromJson(String string,Class classType){
try {
return objectMapper.readValue(string,classType);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* json转换为对象
*/
public static Object fromJson(String string,TypeReference typeReference){
try {
return objectMapper.readValue(string,typeReference);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
ProductInfoReceiver
/**
* @Title
* @Author zwj
* @Date 2018/11/28
* @Description
*/
@Component
@Slf4j
public class ProductInfoReceiver {
private static final String PRODUCT_STOCK_TEMPLATE="product_stock_%s";
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 接收orderDTO对象消息
* */
@RabbitListener(queuesToDeclare = @Queue("productInfo"))
public void process(String message) {
List<ProductInfoOutput> productInfoOutputList = (List<ProductInfoOutput>) JsonUtil.fromJson(message,
new TypeReference<List<ProductInfoOutput>>() {});
log.info("从队列【{}】接收到消息:{}", "productInfo", productInfoOutputList);
//储存到redis中
for (ProductInfoOutput productInfoOutput : productInfoOutputList) {
stringRedisTemplate.opsForValue()
.set(String.format(PRODUCT_STOCK_TEMPLATE,productInfoOutput.getProductId())
,String.valueOf(productInfoOutput.getProductStock()));
}
}
}
3、测试