六、SpringCloud之订单服务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_29479041/article/details/83781451

一、商品服务

1、Sql 在数据库运行Sql语句

-- 订单
create table `order_master` (
    `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`)
);

-- 订单商品
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`)
);

-- 用户
CREATE TABLE `user_info` (
  `id` varchar(32) NOT NULL,
  `username` varchar(32) DEFAULT '',
  `password` varchar(32) DEFAULT '',
  `openid` varchar(64) DEFAULT '' COMMENT '微信openid',
  `role` tinyint(1) NOT NULL COMMENT '1买家2卖家',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`)
);

2、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>
	<packaging>jar</packaging>

	<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>
	</properties>

	<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>
	</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>

3、application.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
#配置Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
#配置端扣号
server:
  port: 8866
#自定义负载均衡规则
PRODUCT:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

4、Application 注解开启相关功能

@EnableDiscoveryClient //开启服务发现功能
@SpringBootApplication
@EnableFeignClients//开启Feign功能
public class OrderApplication {
	public static void main(String[] args) {
		SpringApplication.run(OrderApplication.class, args);
	}
}

5、Entity 实体类

//订单主表
@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;//更新时间
}
//订单详情
@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;//商品小图
}
//商品
@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;//修改时间
}

6、Dto 数据传输对象

/**
 * 数据传输对象
 * 将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;//订单详情
}

7、Form 参数对象类

//参数对象
@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;//购物车
}

8、Dao 操作数据库

//订单主表
public interface OrderMasterRepository extends JpaRepository<OrderMaster, String> {
}
//订单详情
public interface OrderDetailRepository extends JpaRepository<OrderDetail, String> {
}

9、Service

public interface OrderService {
    //创建订单  引用数据传输对象
    OrderDTO create(OrderDTO orderDTO);
}

10、ServiceImpl 业务逻辑层

@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<ProductInfo> 调用商品服务
        List<ProductInfo> productInfoList = productClient.listForOrder(productIdList);

        //计算总价
        BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO);
        for (OrderDetail orderDetail:orderDTO.getOrderDetailList()) {
            for (ProductInfo productInfo:productInfoList) {
                if(productInfo.getProductId().equals(orderDetail.getProductId())){
                    //单价*数量
                    orderAmount = productInfo.getProductPrice()
                            .multiply(new BigDecimal(orderDetail.getProductQuantity()))
                            .add(orderAmount);
                    BeanUtils.copyProperties(productInfo,orderDetail);
                    orderDetail.setOrderId(orderId);
                    orderDetail.setDetailId(KeyUtil.genUniqueKey());
                    //订单详情入库
                    orderDetailRepository.save(orderDetail);
                }
            }
        }

        //扣库存(调用商品服务)
        List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream()
                .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
                .collect(Collectors.toList());
        productClient.decreaseStock(cartDTOList);

        //订单入库
        OrderMaster orderMaster = new OrderMaster();
        orderDTO.setOrderId(orderId);//订单id
        BeanUtils.copyProperties(orderDTO, orderMaster);//把orderDTO里的属性值拷贝到orderMaster里面
        orderMaster.setOrderAmount(orderAmount);
        orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());//订单状态
        orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());//支付状态
        orderMasterRepository.save(orderMaster);
        return orderDTO;
    }
}

11、Client 调用客户端方法接口

//调用客户端方法接口
@FeignClient(name="product") //name是指客户端的名字
public interface ProductClient {

    @GetMapping("/msg")//调用客户端的哪个方法
    String productMsg(); // 方法名可以不一样

    //根据productIdList查询List<ProductInfo>
    @PostMapping("/product/listForOrder")
    public List<ProductInfo> listForOrder(List<String> productIdList);

    //扣减库存
    @PostMapping("/product/decreaseStock")
    void decreaseStock(@RequestBody List<CartDTO> cartDTOList);
}

12、Utils  工具类

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);
    }
}
public class ResultVOUtil {
    //操作成功后返回页面Josn数据格式
    public static ResultVO success(Object object) {
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(0);
        resultVO.setMsg("成功");
        resultVO.setData(object);
        return resultVO;
    }
}

13、Converter 数据转换类

/**
 * 数据转换
 * 将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 {
            //将items转换为List<OrderDetail> orderDetailList
            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;
    }
}

14.Enums 枚举状态

//订单状态
@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;
    }
}
//支付状态
@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;
    }
}
//结果状态
@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;
    }
}

15、Exception 自定义异常

//自定义异常
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();
    }
}

16、VO (返回页面的Josn数据)

//http请求返回的最外层对象
@Data
public class ResultVO<T> {
    private Integer code;
    private String msg;
    private T data;
}

17.Controller

@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);
    }
}
@RestController
@Slf4j
public class ClientController {

    @Autowired
    private ProductClient productClient;//调用客户端方法接口

    //调用商品服务
    @GetMapping("/getProductMsg")
    public String msg(){
        String response = productClient.productMsg();
        log.info("response={}",response);
        return response;
    }

    //根据商品Id 查询商品  调用商品服务
    @GetMapping("/getProductList")
    public String getProductList(){
        List<ProductInfo> productInfoList = productClient.listForOrder(Arrays.asList("157875196366160022", "157875227953464068"));
        log.info("response={}",productInfoList);
        return "ok";
    }

    //扣库存 调用商品服务
    @GetMapping("/productDecreaseStock")
    public String productDecreaseStock(){
        productClient.decreaseStock(Arrays.asList(new CartDTO("157875196366160022", 3)));
        return "ok";
    }
}

18.测试

测试地址:http://localhost:8866/order/create

猜你喜欢

转载自blog.csdn.net/qq_29479041/article/details/83781451