版权声明:本文为博主原创文章,未经博主允许不得转载。 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