1. 订单服务,修改为交易服务

2. 迁移订单创建 rpc 接口
This commit is contained in:
YunaiV
2020-11-26 18:40:07 +08:00
parent 8f8f4d72b5
commit db4ee1ed7e
122 changed files with 1499 additions and 4067 deletions

View File

@@ -1,136 +0,0 @@
<?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>
<artifactId>order</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service-impl</artifactId>
<dependencies>
<!-- Mall 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-sdk</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>pay-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>order-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>promotion-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- RPC 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- Registry 和 Config 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Transaction 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<!-- MQ 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
<!-- Job 相关 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- 测试相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 提供给 mapstruct 使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,41 +0,0 @@
package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 订单关闭
*
* - 注意:订单只有在用户为付款前取消。
*
* - 取消订单,这里是取消整个订单,不能对订单 item 单独做取消
*
* @author Sin
* @time 2019-03-30 16:20
*/
@Data
@Accessors(chain = true)
public class OrderCancelDO extends BaseDO {
/**
* id
*/
private Integer id;
/**
* 订单id
*/
private Integer orderId;
/**
* 订单编号
*/
private String orderNo;
/**
* 关闭订单原因(字典)
*/
private Integer reason;
/**
* 原因(如果选择其他,原因保存在这)
*/
private String otherReason;
}

View File

@@ -1,105 +0,0 @@
package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 订单
*
* @author Sin
* @time 2019-03-16 13:49
*/
@Data
@Accessors(chain = true)
@TableName(value = "orders")
public class OrderDO extends DeletableDO {
/**
* id
*/
private Integer id;
/**
* 用户编号
*/
private Integer userId;
/**
* 订单编号
*/
private String orderNo;
/**
* 购买(商品)总金额,单位:分
*/
private Integer buyPrice;
/**
* 优惠总金额,单位:分。
*/
private Integer discountPrice;
/**
* 物流金额 (分)
*/
private Integer logisticsPrice;
/**
* 最终金额,单位:分
*
* buyPrice + logisticsPrice - discountPrice = presentPrice
*/
private Integer presentPrice;
/**
* 实际已支付金额,单位:分
*
* 初始时,金额为 0 。等到支付成功后,会进行更新。
*/
private Integer payAmount;
///
/// 时间信息
/**
* 付款时间(待发货)
*/
private Date paymentTime;
/**
* 发货时间(待收货)
*/
private Date deliveryTime;
/**
* 收货时间(已签收)
*/
private Date receiverTime;
/**
* 成交时间(用户确认收货 -> status = 已完成)
*/
private Date closingTime;
///
/// 其他
/**
* 是否退货
*
* - 1、没有
* - 2、换货
* - 3、退货
* - 4、换货 + 退货
*/
private Integer hasReturnExchange;
/**
* 状态(如果有多个商品分开发货需要全部商品发完才会改变状态)
*
* - 1、待付款
* - 2、待发货
* - 3、待收获
* - 4、已完成
* - 5、已关闭
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,144 +0,0 @@
package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 订单 item
*
* @author Sin
* @time 2019-03-16 14:03
*/
@Data
@Accessors(chain = true)
public class OrderItemDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 订单编号
*/
private Integer orderId;
/**
* 订单号
*/
private String orderNo;
/**
* 商品编号
*/
private Integer skuId;
/**
* 物流id
*/
private Integer orderLogisticsId;
/**
* 商品名称
*/
private String skuName;
/**
* 商品图片
*/
private String skuImage;
/**
* 数量
*/
private Integer quantity;
/**
* 原始单价,单位:分。
*/
private Integer originPrice;
/**
* 购买单价,单位:分
*/
private Integer buyPrice;
/**
* 最终价格,单位:分。
*/
private Integer presentPrice;
/**
* 购买总金额,单位:分
*
* 用途类似 {@link #presentTotal}
*/
private Integer buyTotal;
/**
* 优惠总金额,单位:分。
*/
private Integer discountTotal;
/**
* 最终总金额,单位:分。
*
* 注意presentPrice * quantity 不一定等于 presentTotal 。
* 因为,存在无法整除的情况。
* 举个例子presentPrice = 8.33 quantity = 3 的情况presentTotal 有可能是 24.99 ,也可能是 25 。
* 所以,需要存储一个该字段。
*/
private Integer presentTotal;
// 如上字段,举个例子:
// 假设购买三个,即 quantity = 3 。
// originPrice = 15
// 使用限时折扣单品优惠8 折buyPrice = 12
// 开始算总的价格
// buyTotal = buyPrice * quantity = 12 * 3 = 36
// discountTotal ,假设有满减送(分组优惠)满 20 减 10 ,并且使用优惠劵满 1.01 减 1 ,则 discountTotal = 10 + 1 = 11
// presentTotal = buyTotal - discountTotal = 24 - 11 = 13
// 最终 presentPrice = presentTotal / quantity = 13 / 3 = 4.33
///
/// 时间信息
/**
* 付款时间
*/
private Date paymentTime;
/**
* 发货时间
*/
private Date deliveryTime;
/**
* 收货时间
*/
private Date receiverTime;
/**
* 成交时间
*/
private Date closingTime;
///
/// 其他
/**
* 是否退货
*
* - 1、没有
* - 2、换货
* - 3、退货
* - 4、换货 + 退货
*/
private Integer hasReturnExchange;
/**
* 发货方式
*
* - 1 未选择
* - 2 在线下单
* - 3 自己联系快递
* - 4 无物流
*/
private Integer deliveryType;
/**
* 状态
*
* - 1、待付款
* - 2、待发货
* - 3、已发货
* - 4、已完成
* - 5、已关闭
*/
private Integer status;
}

View File

@@ -1,46 +0,0 @@
package cn.iocoder.mall.order.biz.dataobject;
/**
* 订单优惠明细
*/
// TODO 芋艿 后续在完善
public class OrderPreferentialDO {
/**
* 编号
*/
private Integer id;
/**
* 类型
*
* 1 - 促销活动
* 2 - 优惠劵
*/
private Integer type;
// TODO 芋艿 优惠劵编号 or 促销活动编号
/**
* 订单编号
*/
private Integer orderId;
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品数量
*/
private Integer quantity;
/**
* 传入时的价格
*/
private Integer originTotal;
/**
* 总优惠价格
*/
private Integer discountTotal;
}

View File

@@ -1,48 +0,0 @@
package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 订单收件人信息 order_recipient (配送信息)
*
* @author Sin
* @time 2019-03-31 11:37
*/
@Data
@Accessors(chain = true)
public class OrderRecipientDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 订单id
*/
private Integer orderId;
/**
* 收件区域编号
*/
private String areaNo;
/**
* 收件人名称
*/
private String name;
/**
* 收件手机号
*/
private String mobile;
/**
* 配送类型
*
* - 1 快递
*/
private Integer type;
/**
* 收件详细地址
*/
private String address;
}

View File

@@ -210,151 +210,6 @@ public class OrderServiceImpl implements OrderService {
return CommonResult.success(orderInfoBO);
}
@Override
@GlobalTransactional
// @Transactional // 使用 Seata 分布事务后,无需在使用 @Transactional 注解。
public CommonResult<OrderCreateBO> createOrder(OrderCreateDTO orderCreateDTO) {
Integer userId = orderCreateDTO.getUserId();
List<OrderCreateDTO.OrderItem> orderItemDTOList = orderCreateDTO.getOrderItems();
List<OrderItemDO> orderItemDOList = OrderItemConvert.INSTANCE.convert(orderItemDTOList);
// 获取商品信息
Set<Integer> skuIds = orderItemDOList.stream().map(OrderItemDO::getSkuId).collect(Collectors.toSet());
List<ProductSkuDetailBO> productList = productSpuService.getProductSkuDetailList(skuIds);
if (orderItemDTOList.size() != productList.size()) { // 校验获得的数量,是否匹配
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_GET_GOODS_INFO_INCORRECT.getCode());
}
// 价格计算
CalcOrderPriceBO calcOrderPrice = calcOrderPrice(productList, orderCreateDTO);
// 设置 orderItem
Map<Integer, ProductSkuDetailBO> productSpuBOMap = productList
.stream().collect(Collectors.toMap(ProductSkuDetailBO::getId, o -> o)); // 商品 SKU 信息的集合
Map<Integer, CalcOrderPriceBO.Item> priceItemMap = new HashMap<>(); // 商品 SKU 价格的映射
calcOrderPrice.getItemGroups().forEach(itemGroup ->
itemGroup.getItems().forEach(item -> priceItemMap.put(item.getId(), item)));
// 遍历 orderItemDOList 数组,将商品信息、商品价格,设置到其中
for (OrderItemDO orderItemDO : orderItemDOList) {
ProductSkuDetailBO productSkuDetailBO = productSpuBOMap.get(orderItemDO.getSkuId());
if (productSkuDetailBO.getQuantity() <= 0) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_INSUFFICIENT_INVENTORY.getCode());
}
if (productSkuDetailBO.getPrice() <= 0) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_GOODS_AMOUNT_INCORRECT.getCode());
}
// 设置 SKU 信息
orderItemDO.setSkuImage(Optional.ofNullable(productSkuDetailBO.getSpu().getPicUrls().get(0)).get());
orderItemDO.setSkuName(productSkuDetailBO.getSpu().getName());
// 设置价格信息
CalcOrderPriceBO.Item priceItem = priceItemMap.get(orderItemDO.getSkuId());
Assert.notNull(priceItem, "商品计算价格为空");
orderItemDO.setOriginPrice(priceItem.getOriginPrice())
.setBuyPrice(priceItem.getBuyPrice())
.setPresentPrice(priceItem.getPresentPrice())
.setBuyTotal(priceItem.getBuyTotal())
.setDiscountTotal(priceItem.getDiscountTotal())
.setPresentTotal(priceItem.getPresentTotal());
}
// 标记优惠劵已使用
if (orderCreateDTO.getCouponCardId() != null) {
couponService.useCouponCard(userId, orderCreateDTO.getCouponCardId());
}
// TODO 芋艿,扣除库存
// order
// TODO: 2019-04-11 Sin 订单号需要生成规则
// TODO FROM 芋艿 to 小范:可以考虑抽象成一个方法,下面几个也是。
String orderNo = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
// Integer totalAmount = orderCommon.calculatedAmount(orderItemDOList);
// Integer totalPrice = orderCommon.calculatedPrice(orderItemDOList);
// Integer totalLogisticsPrice = orderCommon.calculatedLogisticsPrice(orderItemDOList);
OrderDO orderDO = new OrderDO()
.setUserId(userId)
.setOrderNo(orderNo)
.setBuyPrice(calcOrderPrice.getFee().getBuyTotal())
.setDiscountPrice(calcOrderPrice.getFee().getDiscountTotal())
.setLogisticsPrice(calcOrderPrice.getFee().getPostageTotal())
.setPresentPrice(calcOrderPrice.getFee().getPresentTotal())
.setPayAmount(0)
.setClosingTime(null)
.setDeliveryTime(null)
.setPaymentTime(null)
.setStatus(OrderStatusEnum.WAITING_PAYMENT.getValue())
.setHasReturnExchange(OrderHasReturnExchangeEnum.NO.getValue())
.setRemark(Optional.ofNullable(orderCreateDTO.getRemark()).orElse(""));
orderDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
// orderDO.setCreateTime(new Date());
// orderDO.setUpdateTime(null);
orderMapper.insert(orderDO);
// 收件人信息
CommonResult<UserAddressBO> userAddressResult = userAddressService.getAddress(userId, orderCreateDTO.getUserAddressId());
if (userAddressResult.isError()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_GET_USER_ADDRESS_FAIL.getCode());
}
UserAddressBO userAddressBO = userAddressResult.getData();
OrderRecipientDO orderRecipientDO = OrderRecipientConvert.INSTANCE.convert(userAddressBO);
orderRecipientDO
.setOrderId(orderDO.getId())
.setType(OrderRecipientTypeEnum.EXPRESS.getValue())
.setCreateTime(new Date())
.setUpdateTime(null);
orderRecipientMapper.insert(orderRecipientDO);
// order item
orderItemDOList.forEach(orderItemDO -> {
orderItemDO
.setOrderId(orderDO.getId())
.setOrderNo(orderDO.getOrderNo())
.setPaymentTime(null)
.setDeliveryTime(null)
.setReceiverTime(null)
.setClosingTime(null)
.setHasReturnExchange(OrderStatusEnum.WAITING_PAYMENT.getValue())
.setDeliveryType(OrderDeliveryTypeEnum.NONE.getValue())
.setStatus(OrderStatusEnum.WAITING_PAYMENT.getValue())
.setDeleted(DeletedStatusEnum.DELETED_NO.getValue())
.setCreateTime(new Date())
.setUpdateTime(null);
});
// 一次性插入
orderItemMapper.insert(orderItemDOList);
// 创建预订单
createPayTransaction(orderDO, orderItemDOList, orderCreateDTO.getIp());
// if (commonResult.isError()) {
// //手动开启事务回滚
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
// return ServiceExceptionUtil.error(OrderErrorCodeEnum.ORDER_GET_PAY_FAIL.getValue());
// }
// TODO: 2019-03-17 Sin 需要发送 创建成果 MQ 消息,业务扩展和统计
return CommonResult.success(
new OrderCreateBO()
.setId(orderDO.getId())
.setOrderNo(orderDO.getOrderNo())
.setPayAmount(orderDO.getPayAmount())
);
}
private CalcOrderPriceBO calcOrderPrice(List<ProductSkuDetailBO> skus, OrderCreateDTO orderCreateDTO) {
// 创建计算的 DTO
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
.setUserId(orderCreateDTO.getUserId())
.setItems(new ArrayList<>(skus.size()))
.setCouponCardId(orderCreateDTO.getCouponCardId());
for (OrderCreateDTO.OrderItem item : orderCreateDTO.getOrderItems()) {
calcOrderPriceDTO.getItems().add(new CalcOrderPriceDTO.Item(item.getSkuId(), item.getQuantity(), true));
}
// 执行计算
return cartService.calcOrderPrice(calcOrderPriceDTO);
}
private PayTransactionBO createPayTransaction(OrderDO order, List<OrderItemDO> orderItems, String ip) {
// TODO sin 支付订单 orderSubject 暂时取第一个子订单商品信息
String orderSubject = orderItems.get(0).getSkuName();