02 订单微服务

  微服务都是分数据库的。新建个数据库给订单微服务 ,数据库实例名 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等,主要是考虑到有些促销活动这些字段会经常更改这些因素

  请求: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. 订单入库

  搭建订单微服务

  application.yml

  连接到 order微服务的数据库

server:
  port: 8081

spring:
   application:
      name: order
   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:
     show-sql: true
eureka:
  client:
    service-url:
       defaultZone: http://localhost:8761/eureka/

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

package com.smart.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

}

  实体类

       Order

package com.smart.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="t_order")
public class Order {

    @Id
    private String orderId;
    private String buyerName;
    private String buyerPhone;
    private String buyerAddress;

    private String buyerOpenid;
    private BigDecimal orderAmount;
    /**
     * 订单状态, 默认为0新下单.
     */
    private Integer orderStatus;
    /**
     * 支付状态, 默认为0未支付.
     */
    private Integer payStatus;
    private Date createTime;
    private Date updateTime;

}

  OrderDetail

package com.smart.order.domain;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;

@Data
@Entity
@Table(name="order_detail")
public class OrderDetail {

    @Id
    private String detailId;
    private String orderId;
    private String productId;
    private String productName;
    private BigDecimal productPrice;
    private Integer productQuantity;
    private String prodductIcon;
}

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

OrderRepository

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

 

package com.smart.order.Repository;

import com.smart.order.domain.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order,String> {
}

 

  OrderDetailRepository

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

 

package com.smart.order.Repository;

import com.smart.order.domain.OrderDetail;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderDedetailRepository extends JpaRepository<OrderDetail,String> {
}

 

  Service层

  根据业务规则,一个Order中可能有多个OrderDetail, 所以入参OrderDetail 必须是个集合, 因此将这俩合并一下,封装成DTO来使用,作为入参和返回结果。

package com.smart.order.domain;

import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

@Data
public class OrderDTO {
    private String orderId;
    private String buyerName;
    private String buyerPhone;
    private String buyerAddress;
    private String buyerOpenid;
    private BigDecimal orderAmount;
    private Integer orderStatus;
    private Integer payStatus;

    /**
     * 一个订单有多个orderDetail
     */
    List<OrderDetail> orderDetailList;
}

  OrderService接口和实现类

  先实现订单入库部分

package com.smart.order.service.impl;

import com.smart.order.Repository.OrderDedetailRepository;
import com.smart.order.Repository.OrderRepository;
import com.smart.order.constant.OrderStatus;
import com.smart.order.constant.PayStatus;
import com.smart.order.domain.Order;
import com.smart.order.domain.OrderDTO;
import com.smart.order.service.OrderService;
import com.smart.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 {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private OrderDedetailRepository orderDedetailRepository;

    @Override
    public OrderDTO createOrder(OrderDTO orderDTO) {

        //查询商品信息
        //计算订单总价
        //减库存

        //订单入库
        Order order = new Order();
        orderDTO.setOrderId(KeyUtil.genUniqueKey());
        BeanUtils.copyProperties(orderDTO,order);
        order.setOrderAmount(new BigDecimal(100));
        order.setOrderStatus(OrderStatus.NEW.getCode());
        order.setPayStatus(PayStatus.WAIT.getCode());
        orderRepository.save(order);
        return orderDTO;
    }

}

  Controller层

package com.smart.order.domain;

import lombok.Data;

import javax.validation.constraints.NotEmpty;

/**
 * 对应:
 *   {
 *       "name":"xxx",
 *       "phone":"xxx",
 *       "address":"xxx",
 *       "openid":"xxx",
 *       "items":[
 *           "productId":"xxxx",
 *           "productQuantity":"2"
 *       ]
 *   }
 */
@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;

    @NotEmpty(message="购物车不能为空")
    private String items;

}
package com.smart.order.converter;

import com.alibaba.fastjson.JSONObject;
import com.smart.order.domain.OrderDTO;
import com.smart.order.domain.OrderDetail;
import com.smart.order.domain.OrderForm;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class OrderForm2OrderDTOConverter {

    public static OrderDTO convert(OrderForm orderForm){
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setBuyerName(orderForm.getName());
        orderDTO.setBuyerPhone(orderForm.getPhone());
        orderDTO.setBuyerAddress(orderForm.getAddress());
        orderDTO.setBuyerOpenid(orderForm.getOpenid());

        List<OrderDetail> orderDetails = new ArrayList<>();
        orderDetails = JSONObject.parseArray(orderForm.getItems(), OrderDetail.class);

        orderDTO.setOrderDetailList(orderDetails);
        return orderDTO;
    }
}
package com.smart.order.controller;

import com.smart.order.constant.ErrorCode;
import com.smart.order.converter.OrderForm2OrderDTOConverter;
import com.smart.order.domain.OrderDTO;
import com.smart.order.domain.OrderForm;
import com.smart.order.exception.OrderException;
import com.smart.order.service.OrderService;
import com.smart.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 {

    @Autowired
    private OrderService orderService;

    /**
     * 创建订单
     * @param orderForm
     * @param bindingResult
     * @return
     */
    @PostMapping("/create")
    public Result createOrder(@Valid OrderForm orderForm, BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            log.error("createOrder的参数不正确:{}",orderForm);
            throw new OrderException(ErrorCode.PARAM_ERROR.getCode(),bindingResult.getFieldError().getDefaultMessage());
        }

        OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
        if(CollectionUtils.isEmpty(orderDTO.getOrderDetailList())){
            log.error("createOrder的购物车为空");
            throw new OrderException(ErrorCode.CART_EMPTY);
        }
        OrderDTO order = orderService.createOrder(orderDTO);
        Map<String,String> result=new HashMap<>();
        result.put("orderId",order.getOrderId());
        return Result.success(result);
    }
}

测试

 

 

参考:

https://blog.csdn.net/yangshangwei/article/details/88762602

 

posted on 2019-09-22 17:52  溪水静幽  阅读(346)  评论(0)    收藏  举报