一、商品服务
1、product父项目
- 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>
<groupId>com.imooc</groupId>
<artifactId>product</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 引入三个模块 -->
<modules>
<module>common</module>
<module>server</module>
<module>client</module>
</modules>
<name>product</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.M1</spring-cloud.version>
<product-common.version>0.0.1-SNAPSHOT</product-common.version>
</properties>
<!-- 版本依赖管理 -->
<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>
<dependency>
<groupId>com.imooc</groupId>
<artifactId>product-common</artifactId>
<version>${product-common.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--打包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</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>
2、client模块
- 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-client</artifactId>
<name>client</name>
<description>Demo project for Spring Boot</description>
<!-- 引入依赖 -->
<dependencies>
<!-- 引入web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入 spring-cloud-netflix-core -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-core</artifactId>
</dependency>
<!-- 引入feign 应用通信-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<!-- 引入product-common-->
<dependency>
<groupId>com.imooc</groupId>
<artifactId>product-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<!-- 打包 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
<mainClass>com.imooc.product.ProductApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-
productClient
package com.imooc.product.client;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
//调用客户端方法接口
@FeignClient(name="product") //name是指客户端的名字
public interface ProductClient {
//根据productIdList查询List<ProductInfoOutput>listForOrder
@PostMapping("/product/listForOrder")
List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList);
//扣减库存
@PostMapping("/product/decreaseStock")
void decreaseStock(@RequestBody List<DecreaseStockInput> decreaseStockInputList);
}
3、common模块
- 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-common</artifactId>
<name>common</name>
<description>Demo project for Spring Boot</description>
<!-- 引入依赖 -->
<dependencies>
<!-- 引入lombok 可以省去get set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<!-- 打包 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 指定启动类 -->
<mainClass>com.imooc.product.ProductApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- Java
package com.imooc.product.common;
import lombok.Data;
//减库存入参
@Data
public class DecreaseStockInput {
private String productId;//商品id
private Integer productQuantity;//商品数量
public DecreaseStockInput() {
}
public DecreaseStockInput(String productId, Integer productQuantity) {
this.productId = productId;
this.productQuantity = productQuantity;
}
}
package com.imooc.product.common;
import lombok.Data;
import java.math.BigDecimal;
//商品
@Data //使用lombok省去get set方法
public class ProductInfoOutput {
private String productId;//id
private String productName;//名字
private BigDecimal productPrice;//单价
private Integer productStock;//库存
private String productDescription;//描述
private String productIcon;//小图
private Integer productStatus;//状态, 0正常1下架
private Integer categoryType;//类目编号
}
4、server模块
- 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>
<!-- 引入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
profile: dev
#配置数据源
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
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
#配置端口号
server:
port: 8888
- ProductApplication
package com.imooc.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient //开启发现服务功能
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
- dataobject
package com.imooc.product.dataobject;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;
//类目
@Data
@Entity
public class ProductCategory {
@Id //主键
@GeneratedValue //id自增
private Integer categoryId;//类目id
private String categoryName;//类目名字
private Integer categoryType;//类目编号
private Date createTime;//创建时间
private Date updateTime;//修改时间
}
package com.imooc.product.dataobject;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.util.Date;
//商品
//@Table(name="product_info") //如果表名跟类名不一样,要写表名,一样就不要
@Data //使用lombok省去get set方法
@Entity //数据库与表对应
public class ProductInfo {
@Id //主键
private String productId;//id
private String productName;//名字
private BigDecimal productPrice;//单价
private Integer productStock;//库存
private String productDescription;//描述
private String productIcon;//小图
private Integer productStatus;//状态, 0正常1下架
private Integer categoryType;//类目编号
private Date createTime;//创建时间
private Date updateTime;//修改时间
}
- enums
package com.imooc.product.enums;
import lombok.Getter;
//商品上下线状态
@Getter //get方法
public enum ProductStatusEnum {
UP(0,"在架"),
DOWN(1,"下架"),
;
private Integer code;
private String message;
ProductStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
package com.imooc.product.enums;
import lombok.Getter;
@Getter
public enum ResultEnum {
PRODUCT_NOT_EXIST(1,"商品不存在"),
PRODUCT_STOCK_ERROR(1,"库存有误"),
;
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
- exception
package com.imooc.product.exception;
import com.imooc.product.enums.ResultEnum;
//自定义异常
public class ProductException extends RuntimeException{
private Integer code;//异常码
public ProductException(Integer code, String message) {
super(message);
this.code = code;
}
public ProductException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
}
}
- VO
package com.imooc.product.VO;
import lombok.Data;
//http请求返回的最外层对象
@Data
public class ResultVO<T> {
private Integer code;//错误码
private String msg;//提示信息
private T data;//具体内容
}
package com.imooc.product.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
//http请求返回的中间层对象
@Data
public class ProductVO {
@JsonProperty("name") //返回前端显示的字段
private String categoryName;//内目名字
@JsonProperty("type")
private Integer categoryType;//类目编号
@JsonProperty("foods")
private List<ProductInfoVO> productInfoVOList;//食物
}
package com.imooc.product.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
//http请求返回的最里层对象
@Data
public class ProductInfoVO {
@JsonProperty("id")
private String productId;//商品id
@JsonProperty("name")
private String productName;//商品名称
@JsonProperty("price")
private String productPrice;//商品价格
@JsonProperty("description")
private String productDescription;//商品描述
@JsonProperty("icon")
private String productIcon;//商品突破
}
- utils
package com.imooc.product.utils;
import com.imooc.product.VO.ResultVO;
//返回页面josn数据格式
public class ResultVOUtil {
public static ResultVO success(Object object) {
ResultVO resultVO = new ResultVO();
resultVO.setData(object);
resultVO.setCode(0);
resultVO.setMsg("成功");
return resultVO;
}
}
- repository
package com.imooc.product.repository;
import com.imooc.product.dataobject.ProductCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
//类目
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, Integer> {
List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}
package com.imooc.product.repository;
import com.imooc.product.common.ProductInfoOutput;
import com.imooc.product.dataobject.ProductInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
//商品
public interface ProductInfoRepository extends JpaRepository<ProductInfo, String>{
List<ProductInfo> findByProductStatus(Integer productStatus);
List<ProductInfo> findByProductIdIn(List<String> productIdList);
}
- service
package com.imooc.product.service;
import com.imooc.product.dataobject.ProductCategory;
import java.util.List;
//类目
public interface CategoryService {
//根据类目编号查询List<ProductCategory>
List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
}
package com.imooc.product.service;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import com.imooc.product.dataobject.ProductInfo;
import java.util.List;
//商品
public interface ProductService {
//查询所有在架商品列表
List<ProductInfo> findUpAll();
//根据productIdList查询商品列表
List<ProductInfoOutput> findList(List<String> productIdList);
//扣库存
void decreaseStock(List<DecreaseStockInput> decreaseStockInputList);
}
- impl
package com.imooc.product.service.impl;
import com.imooc.product.dataobject.ProductCategory;
import com.imooc.product.repository.ProductCategoryRepository;
import com.imooc.product.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
//类目
@Service
public class CategoryServiceImpl implements CategoryService {
//引入商品Dao
@Autowired
private ProductCategoryRepository productCategoryRepository;
//根据类目编号查询List<ProductCategory>
@Override
public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
return productCategoryRepository.findByCategoryTypeIn(categoryTypeList);
}
}
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);
//发送mq消息
List<ProductInfoOutput> productInfoOutputList = productInfoList.stream().map(e -> {
ProductInfoOutput output = new ProductInfoOutput();
BeanUtils.copyProperties(e, output);
return output;
}).collect(Collectors.toList());
//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;
}
}
- controller
package com.imooc.product.controller;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import com.imooc.product.VO.ProductInfoVO;
import com.imooc.product.VO.ProductVO;
import com.imooc.product.VO.ResultVO;
import com.imooc.product.dataobject.ProductCategory;
import com.imooc.product.dataobject.ProductInfo;
import com.imooc.product.service.CategoryService;
import com.imooc.product.service.ProductService;
import com.imooc.product.utils.ResultVOUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
//商品
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;//商品Service
@Autowired
private CategoryService categoryService;//类目Service
@GetMapping("/list")
public ResultVO<ProductVO> list(){
//1.查询所有在架的商品
List<ProductInfo> productInfoList = productService.findUpAll();
//2.获取类目type列表
List<Integer> categoryTypeList = productInfoList.stream()
.map(ProductInfo::getCategoryType)
.collect(Collectors.toList());
//3.从数据库查询类目
List<ProductCategory> categoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
//4.构造数据
List<ProductVO> productVOList = new ArrayList<>();
for (ProductCategory productCategory: categoryList) {
ProductVO productVO = new ProductVO();
productVO.setCategoryName(productCategory.getCategoryName());
productVO.setCategoryType(productCategory.getCategoryType());
List<ProductInfoVO> productInfoVOList = new ArrayList<>();
for (ProductInfo productInfo: productInfoList) {
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
return ResultVOUtil.success(productVOList);
}
/**
* 获取商品列表(给订单服务用的)
* */
@PostMapping("/listForOrder")
public List<ProductInfoOutput> listForOrder(@RequestBody List<String> productIdList) {
return productService.findList(productIdList);
}
/**
* 扣减库存(给订单服务用的)
* */
@PostMapping("/decreaseStock")
public void decreaseStock(@RequestBody List<DecreaseStockInput> decreaseStockInputList) {
productService.decreaseStock(decreaseStockInputList);
}
}
5、发布商品项目
将其打成 jar包上传到本地的maven仓库中,方便订单项目调用(注意发布的地址和订单项目调用的地址要一致)
mvn -Dmaven.test.skip=true -U clean install
二、订单服务
1、order父项目
- 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>
<groupId>com.imooc</groupId>
<artifactId>order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 引入三个模块 -->
<modules>
<module>client</module>
<module>common</module>
<module>server</module>
</modules>
<name>order</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.M1</spring-cloud.version>
<product-client.version>0.0.1-SNAPSHOT</product-client.version>
<order-common.version>0.0.1-SNAPSHOT</order-common.version>
</properties>
<!-- 版本依赖管理 -->
<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>
<dependency>
<groupId>com.imooc</groupId>
<artifactId>product-client</artifactId>
<version>${product-client.version}</version>
</dependency>
<dependency>
<groupId>com.imooc</groupId>
<artifactId>order-common</artifactId>
<version>${order-common.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 仓库地址 -->
<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>
2、client模块
- 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-client</artifactId>
</project>
3、common模块
- 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-common</artifactId>
<!-- 引入依赖 -->
<dependencies>
<!-- 引入lombok 可以省去get set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
4、server模块
- 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>
</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
# profile: test
#配置数据源
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
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
#配置端扣号
server:
port: 8866
#自定义负载均衡规则
#PRODUCT:
# ribbon:
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
- OrderApplication
package com.imooc.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication //开启服务发现功能
@EnableDiscoveryClient
//扫描商品的包路径
@EnableFeignClients(basePackages = "com.imooc.product.client")//开启Feign功能
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
- dataobject
package com.imooc.order.dataobject;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;
//订单详情
@Data
@Entity
public class OrderDetail {
@Id //主键
private String detailId;
private String orderId;//订单id
private String productId;//商品id
private String productName;//商品名称
private BigDecimal productPrice;//商品单价
private Integer productQuantity;//商品数量
private String productIcon;//商品小图
}
package com.imooc.order.dataobject;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.util.Date;
//订单主表
@Data
@Entity
public class OrderMaster {
@Id //主键
private String orderId;//订单id
private String buyerName;//买家名字
private String buyerPhone;//买家手机号
private String buyerAddress;//买家地址
private String buyerOpenid;//买家微信Openid
private BigDecimal orderAmount;//订单总金额
private Integer orderStatus;//订单状态, 默认为0新下单
private Integer payStatus;//支付状态, 默认为0未支付
private Date createTime;//创建时间
private Date updateTime;//更新时间
}
package com.imooc.order.dataobject;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.util.Date;
//商品
@Data //使用lombok省去get set方法
@Entity //数据库与表对应
public class ProductInfo {
@Id //主键
private String productId;//id
private String productName;//名字
private BigDecimal productPrice;//单价
private Integer productStock;//库存
private String productDescription;//描述
private String productIcon;//小图
private Integer productStatus;//状态, 0正常1下架
private Integer categoryType;//类目编号
private Date createTime;//创建时间
private Date updateTime;//修改时间
}
- dto
package com.imooc.order.dto;
import lombok.Data;
//购物车
@Data
public class CartDTO {
private String productId;//商品id
private Integer productQuantity;//商品数量
public CartDTO() {
}
public CartDTO(String productId, Integer productQuantity) {
this.productId = productId;
this.productQuantity = productQuantity;
}
}
package com.imooc.order.dto;
import com.imooc.order.dataobject.OrderDetail;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 数据传输对象
* 将OrderMaster和OrderDetail关联起来,一对多
* */
@Data
public class OrderDTO {
private String orderId;//订单id
private String buyerName;//买家名字
private String buyerPhone;//买家手机号
private String buyerAddress;//买家地址
private String buyerOpenid;//买家微信Openid
private BigDecimal orderAmount;//订单总金额
private Integer orderStatus;//订单状态, 默认为0新下单
private Integer payStatus;//支付状态, 默认为0未支付
private List<OrderDetail> orderDetailList;//订单详情
}
- enums
package com.imooc.order.enums;
import lombok.Getter;
//订单状态
@Getter
public enum OrderStatusEnum {
NEW(0, "新订单"),
FINISHED(1, "完结"),
CANCEL(2, "取消"),
;
private Integer code;
private String message;
OrderStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
package com.imooc.order.enums;
import lombok.Getter;
//支付状态
@Getter
public enum PayStatusEnum {
WAIT(0, "等待支付"),
SUCCESS(1, "支付成功"),
;
private Integer code;
private String message;
PayStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
package com.imooc.order.enums;
import lombok.Getter;
//结果状态
@Getter
public enum ResultEnum {
PARAM_ERROR(1, "参数错误"),
CART_EMPTY(2, "购物车为空")
;
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
- exception
package com.imooc.order.exception;
import com.imooc.order.enums.ResultEnum;
//自定义异常
public class OrderException extends RuntimeException {
private Integer code;//异常码
public OrderException(Integer code, String message) {
super(message);
this.code = code;
}
public OrderException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
}
}
- form
package com.imooc.order.form;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
//参数对象
@Data
public class OrderForm {
@NotEmpty(message = "姓名必填")
private String name;//买家姓名
@NotEmpty(message = "手机号必填")
private String phone;//买家手机号
@NotEmpty(message = "地址必填")
private String address;//买家地址
@NotEmpty(message = "openid必填")
private String openid;//买家微信openid
@NotEmpty(message = "购物车不能为空")
private String items;//购物车
}
- utils
package com.imooc.order.utils;
import java.util.Random;
public class KeyUtil {
/**
* 生成唯一的主键
* 格式: 时间+随机数
* synchronized 避免多线程的时候生成同样的订单号
*/
public static synchronized String genUniqueKey() {
Random random = new Random();
Integer number = random.nextInt(900000) + 100000;
return System.currentTimeMillis() + String.valueOf(number);
}
}
package com.imooc.order.utils;
import com.imooc.order.VO.ResultVO;
public class ResultVOUtil {
//操作成功后返回页面Josn数据格式
public static ResultVO success(Object object) {
ResultVO resultVO = new ResultVO();
resultVO.setCode(0);
resultVO.setMsg("成功");
resultVO.setData(object);
return resultVO;
}
}
- converter
package com.imooc.order.converter;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.imooc.order.dataobject.OrderDetail;
import com.imooc.order.dto.OrderDTO;
import com.imooc.order.enums.ResultEnum;
import com.imooc.order.exception.OrderException;
import com.imooc.order.form.OrderForm;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* 数据转换
* 将OrderForm 转换为 OrderDTO
*/
@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 {
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;
}
}
- VO
package com.imooc.order.VO;
import lombok.Data;
//http请求返回的最外层对象
@Data
public class ResultVO<T> {
private Integer code;
private String msg;
private T data;
}
- repository
package com.imooc.order.repository;
import com.imooc.order.dataobject.OrderDetail;
import org.springframework.data.jpa.repository.JpaRepository;
//订单详情
public interface OrderDetailRepository extends JpaRepository<OrderDetail, String> {
}
package com.imooc.order.repository;
import com.imooc.order.dataobject.OrderMaster;
import org.springframework.data.jpa.repository.JpaRepository;
//订单主表
public interface OrderMasterRepository extends JpaRepository<OrderMaster, String> {
}
- service
package com.imooc.order.service;
import com.imooc.order.dto.OrderDTO;
public interface OrderService {
//创建订单 引用数据传输对象
OrderDTO create(OrderDTO orderDTO);
}
- impl
package com.imooc.order.service.impl;
import com.imooc.order.dataobject.OrderDetail;
import com.imooc.order.dataobject.OrderMaster;
import com.imooc.order.dto.OrderDTO;
import com.imooc.order.enums.OrderStatusEnum;
import com.imooc.order.enums.PayStatusEnum;
import com.imooc.order.repository.OrderDetailRepository;
import com.imooc.order.repository.OrderMasterRepository;
import com.imooc.order.service.OrderService;
import com.imooc.order.utils.KeyUtil;
import com.imooc.product.client.ProductClient;
import com.imooc.product.common.DecreaseStockInput;
import com.imooc.product.common.ProductInfoOutput;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMasterRepository orderMasterRepository;//订单主表Dao
@Autowired
private OrderDetailRepository orderDetailRepository;//订单详情Dao
@Autowired
private ProductClient productClient;//调用客户端方法接口
//创建订单
@Override
public OrderDTO create(OrderDTO orderDTO) {
String orderId = KeyUtil.genUniqueKey();//用工具类自动生成id组件
//查询商品信息(调用商品服务)
List<String> productIdList = orderDTO.getOrderDetailList().stream()
.map(OrderDetail::getProductId)
.collect(Collectors.toList());
//根据productIdList查询List<ProductInfoOutput> 调用商品服务
List<ProductInfoOutput> productInfoList = productClient.listForOrder(productIdList);
//计算总价
BigDecimal orderAmout = new BigDecimal(BigInteger.ZERO);
for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {
for (ProductInfoOutput productInfo: productInfoList) {
if (productInfo.getProductId().equals(orderDetail.getProductId())) {
//单价*数量
orderAmout = productInfo.getProductPrice()
.multiply(new BigDecimal(orderDetail.getProductQuantity()))
.add(orderAmout);
BeanUtils.copyProperties(productInfo, orderDetail);
orderDetail.setOrderId(orderId);
orderDetail.setDetailId(KeyUtil.genUniqueKey());
//订单详情入库
orderDetailRepository.save(orderDetail);
}
}
}
//扣库存(调用商品服务)
List<DecreaseStockInput> decreaseStockInputList = orderDTO.getOrderDetailList().stream()
.map(e -> new DecreaseStockInput(e.getProductId(), e.getProductQuantity()))
.collect(Collectors.toList());
productClient.decreaseStock(decreaseStockInputList);
//订单入库
OrderMaster orderMaster = new OrderMaster();
orderDTO.setOrderId(orderId);//订单id
BeanUtils.copyProperties(orderDTO, orderMaster);//把orderDTO里的属性值拷贝到orderMaster里面
orderMaster.setOrderAmount(orderAmout);
orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());//订单状态
orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());//支付状态
orderMasterRepository.save(orderMaster);
return orderDTO;
}
}
- controller
package com.imooc.order.controller;
import com.imooc.order.VO.ResultVO;
import com.imooc.order.converter.OrderForm2OrderDTOConverter;
import com.imooc.order.dto.OrderDTO;
import com.imooc.order.enums.ResultEnum;
import com.imooc.order.exception.OrderException;
import com.imooc.order.form.OrderForm;
import com.imooc.order.service.OrderService;
import com.imooc.order.utils.ResultVOUtil;
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
private OrderService orderService;//订单Service
/**
* 1. 参数检验
* 2. 查询商品信息(调用商品服务)
* 3. 计算总价
* 4. 扣库存(调用商品服务)
* 5. 订单入库
*/
@PostMapping("/create")
public ResultVO<Map<String, String>> create(@Valid OrderForm orderForm, BindingResult bindingResult) {
//参数检验,如果结果有错,抛出自定义异常
if (bindingResult.hasErrors()){
log.error("【创建订单】参数不正确, 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("【创建订单】购物车信息为空");
throw new OrderException(ResultEnum.CART_EMPTY);
}
//创建订单
OrderDTO result = orderService.create(orderDTO);
Map<String, String> map = new HashMap<>();
map.put("orderId", result.getOrderId());
return ResultVOUtil.success(map);
}
}
5、测试
注意:order调用maven仓库的地址要跟product发布到maven仓库的地址一致,不然调用不到product里的方法