提交 2e76294f 编写于 作者: H haoxr

feat:订单优化

上级 bc2b3881
......@@ -27,7 +27,7 @@ public class CartItemVO implements Serializable {
private Integer inventory;
private Integer num;
private Integer count;
private Long price;
......
......@@ -53,16 +53,4 @@ public class CartVO implements Serializable {
public void setTotalPrice(Long totalPrice) {
this.totalPrice = totalPrice;
}
public Long getTotalCoupon() {
long total = 0L;
// if (items != null && items.size() > 0) {
// total = items.stream().filter(CartItemVo::isChecked).mapToLong(CartItemVo::getCoupon).sum();
// }
return total;
}
public void setTotalCoupon(Long totalCoupon) {
this.totalCoupon = totalCoupon;
}
}
package com.youlai.mall.oms.pojo.vo;
import com.youlai.common.base.BaseVO;
import lombok.Getter;
import lombok.Setter;
import com.youlai.mall.ums.pojo.domain.UmsAddress;
import lombok.Data;
import java.util.List;
/**
* 订单确认页需要的数据
*/
@Data
public class OrderConfirmVO extends BaseVO {
/**
* 订单总额
*/
@Setter
private Long totalPrice;
private String orderToken;
private List<OrderItemVO> orderItems;
/**
* 订单商品
*/
@Getter
@Setter
private List<OrderItemVO> items;
private List<UmsAddress> addresses;
public Long getTotalPrice() {
Long total = 0L;
if (items != null && items.size() > 0) {
total = items.stream().mapToLong(OrderItemVO::getSubtotal).sum();
}
return total;
}
}
......@@ -2,6 +2,7 @@ package com.youlai.mall.oms.pojo.vo;
import com.youlai.common.base.BaseVO;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
......@@ -9,58 +10,15 @@ import lombok.Setter;
* 订单商品
*/
@Builder
@Data
public class OrderItemVO extends BaseVO {
/**
* 商品id
*/
@Getter
@Setter
private Long skuId;
/**
* 商品图片
*/
@Getter
@Setter
private String skuImg;
/**
* 商品名称
*/
@Getter
@Setter
private String skuName;
/**
* 商品数量
*/
@Getter
@Setter
private Integer num;
/**
* 商品单价
*/
@Getter
@Setter
private Integer count;
private Long price;
private Long coupon;
@Getter
@Setter
private Long coupon = 0L;
/**
* 小计
*/
@Setter
private Long subtotal;
public Long getSubtotal() {
Long total = 0L;
if (price != null && num != null){
total = price * num;
}
return total;
}
}
......@@ -23,10 +23,10 @@ import org.springframework.web.bind.annotation.*;
*/
@Api(tags = "【移动端】订单支付")
@RestController
@RequestMapping("/api.app/v1/order_pays")
@RequestMapping("/api.app/v1/payments")
@Slf4j
@AllArgsConstructor
public class OrderPayController {
public class PaymentController {
private IOrderPayService orderPayService;
......
package com.youlai.mall.oms.listener;
import com.rabbitmq.client.Channel;
import com.youlai.mall.oms.pojo.domain.OmsOrderItem;
import com.youlai.mall.oms.service.IOrderItemService;
import com.youlai.mall.oms.service.IOrderService;
import com.youlai.mall.pms.api.app.InventoryFeignService;
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
......@@ -10,6 +14,8 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
......@@ -17,28 +23,36 @@ import java.io.IOException;
* @date 2021-03-16
*/
@Component
//@Component
@AllArgsConstructor
@Slf4j
public class RabbitMQListener {
IOrderService orderService;
IOrderItemService orderItemService;
InventoryFeignService inventoryFeignService;
RabbitTemplate rabbitTemplate;
/**
* 订单超时未支付,关闭订单,释放库存
* @param orderSn
* @param message
* @param channel
*/
@RabbitListener(queues = "order.close.queue")
public void closeOrder(String orderSn, Message message, Channel channel) {
public void closeOrder(Long orderId, Message message, Channel channel) {
try {
if (orderService.closeOrder(orderSn)) {
if (orderService.closeOrder(orderId)) {
// 如果关单成功,发送消息释放库存
rabbitTemplate.convertAndSend("product_event_change", "inventory:unlock", orderSn);
// rabbitTemplate.convertAndSend("product_event_change", "inventory:unlock", orderSn);
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
List<InventoryDTO> inventoryList = orderItems.stream().map(orderItem -> InventoryDTO.builder()
.skuId(orderItem.getSkuId())
.count(orderItem.getSkuQuantity())
.build())
.collect(Collectors.toList());
inventoryFeignService.unlockInventory(inventoryList);
} else {
// 如果关单失败,则订单可能已经被处理,直接手动ACK确认消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
......@@ -48,9 +62,10 @@ public class RabbitMQListener {
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ioException) {
log.error("释放库存失败,orderSn=[{}]", orderSn);
log.error("释放库存失败,orderId=[{}]", orderId);
}
}
}
}
......@@ -41,7 +41,7 @@ public interface IOrderService extends IService<OmsOrder> {
*
* @param orderSn 订单号
*/
boolean closeOrder(String orderSn);
boolean closeOrder(Long orderId);
/**
* 取消订单接口
......
......@@ -22,7 +22,6 @@ public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OmsOrderItem
@Override
public List<OmsOrderItem> getByOrderId(Long orderId) {
log.info("根据订单ID,查询订单商品列表,orderId={}", orderId);
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<OmsOrderItem>().eq(OmsOrderItem::getOrderId, orderId);
return this.list(queryWrapper);
}
......@@ -35,7 +34,6 @@ public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OmsOrderItem
List<OmsOrderItem> orderItems = this.list(queryWrapper);
if (CollectionUtil.isEmpty(orderItems)) {
log.info("根据订单ID列表查询商品为空,orderIds={}", orderIds);
return new HashMap<>(8);
}
Map<Long, List<OmsOrderItem>> orderItemsMap = orderItems.stream()
......
......@@ -7,18 +7,25 @@ import com.youlai.mall.oms.dao.OrderPayDao;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.pojo.domain.OmsOrder;
import com.youlai.mall.oms.pojo.domain.OmsOrderItem;
import com.youlai.mall.oms.pojo.domain.OmsOrderPay;
import com.youlai.mall.oms.pojo.vo.PayInfoVO;
import com.youlai.mall.oms.service.IOrderItemService;
import com.youlai.mall.oms.service.IOrderLogService;
import com.youlai.mall.oms.service.IOrderPayService;
import com.youlai.mall.oms.service.IOrderService;
import com.youlai.mall.pms.api.app.InventoryFeignService;
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
import com.youlai.mall.ums.api.app.MemberFeignService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
......@@ -27,50 +34,57 @@ import java.util.Date;
public class OrderPayServiceImpl extends ServiceImpl<OrderPayDao, OmsOrderPay> implements IOrderPayService {
private IOrderService orderService;
private IOrderLogService orderLogService;
private MemberFeignService memberFeignService;
private IOrderItemService orderItemService;
private InventoryFeignService inventoryFeignService;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void payWithBalance(Long orderId) {
// 1. 查询订单详情,判断订单状态是否是待支付状态
// 查询订单状态
OmsOrder order = orderService.getByOrderId(orderId);
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
throw new BizException("订单" + OrderStatusEnum.getValue(order.getStatus()).getText());
throw new BizException("订单状态异常,请检查");
}
// 2. 远程调用查询会员余额
// 查询会员余额
Long userId = RequestUtils.getUserId();
Long balance = memberFeignService.getBalance(userId).getData();
if (Long.compare(balance, order.getPayAmount()) == -1) {
throw new BizException("会员余额不足,请先充值");
throw new BizException("会员余额不足");
}
// 3. 更新用户余额
// 更新用户余额
memberFeignService.updateBalance(userId, order.getPayAmount());
// 4. 更新订单状态
// 更新订单状态
order.setStatus(OrderStatusEnum.PAID.getCode());
order.setPayTime(new Date());
order.setPayType(PayTypeEnum.BALANCE.getCode());
order.setPayTime(new Date());
orderService.updateById(order);
// 扣减库存
List<OmsOrderItem> orderItems = orderItemService.getByOrderId(orderId);
List<InventoryDTO> inventoryList = orderItems.stream().map(orderItem -> InventoryDTO.builder()
.skuId(orderItem.getSkuId())
.count(orderItem.getSkuQuantity())
.build())
.collect(Collectors.toList());
// 5. 添加订单支付记录
inventoryFeignService.minusInventory(inventoryList);
// 添加订单支付记录
OmsOrderPay orderPay = OmsOrderPay.builder()
.orderId(orderId)
.payAmount(order.getPayAmount())
.payTime(new Date())
.payType(PayTypeEnum.BALANCE.getCode())
.build();
this.save(orderPay);
// 6. 添加操作日志
orderLogService.addOrderLogs(order.getId(), OrderStatusEnum.PAID.getCode(), userId.toString(), "支付订单");
}
@Override
......
......@@ -8,27 +8,24 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.BeanMapperUtils;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.constant.OmsConstants;
import com.youlai.mall.oms.dao.OrderDao;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.OrderTypeEnum;
import com.youlai.mall.oms.pojo.bo.app.OrderBO;
import com.youlai.mall.oms.pojo.domain.OmsOrderDelivery;
import com.youlai.mall.oms.pojo.domain.OmsOrder;
import com.youlai.mall.oms.pojo.domain.OmsOrderDelivery;
import com.youlai.mall.oms.pojo.domain.OmsOrderItem;
import com.youlai.mall.oms.pojo.dto.OrderSubmitInfoDTO;
import com.youlai.mall.oms.pojo.vo.*;
import com.youlai.mall.oms.service.*;
import com.youlai.mall.pms.api.app.InventoryFeignService;
import com.youlai.mall.pms.pojo.dto.SkuDTO;
import com.youlai.mall.pms.pojo.dto.InventoryDTO;
import com.youlai.mall.pms.pojo.dto.SkuDTO;
import com.youlai.mall.ums.api.app.MemberFeignService;
import com.youlai.mall.ums.pojo.dto.UmsAddressDTO;
import io.seata.spring.annotation.GlobalTransactional;
......@@ -42,7 +39,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
......@@ -71,10 +71,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
@Override
public OrderConfirmVO confirm(Long skuId, Integer num) {
List<OrderItemVO> orderItems = getOrderItem(skuId, num);
if (CollectionUtil.isEmpty(orderItems)) { // 订单中无商品,直接返回
return new OrderConfirmVO();
public OrderConfirmVO confirm(Long skuId, Integer count) {
OrderConfirmVO orderConfirmVO=new OrderConfirmVO();
List<OrderItemVO> orderItems = getOrderItems(skuId, count);
if (CollectionUtil.isEmpty(orderItems)) {
return orderConfirmVO;
}
// 远程获取商品信息填充订单商品属性
......@@ -89,9 +92,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
});
}
OrderConfirmVO confirm = new OrderConfirmVO();
confirm.setItems(orderItems);
return confirm;
OrderConfirmVO confirmVO = new OrderConfirmVO();
confirmVO.setOrderItems(orderItems);
return confirmVO;
}
@Override
......@@ -112,7 +115,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
orderFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
threadLocal.set(submitInfoDTO);
OrderSubmitInfoDTO submitInfo = threadLocal.get();
log.info("订单提交信息:{}", submitInfo);
OmsOrder order = new OmsOrder();
......@@ -132,23 +134,15 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
orderItemFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
threadLocal.set(submitInfoDTO);
log.info("创建订单商品,submitInfoDTO:{}", submitInfoDTO);
OrderSubmitInfoDTO submitInfo = threadLocal.get();
log.info("创建订单商品,submitInfo:{}", submitInfo);
List<OmsOrderItem> orderItems;
log.info("判断结果,{}", submitInfoDTO.getSkuId() != null);
if (submitInfoDTO.getSkuId() != null) { // 直接下单
log.info("直接下单");
orderItems = new ArrayList<>();
orderItems.add(OmsOrderItem.builder().skuId(submitInfo.getSkuId()).skuQuantity(submitInfo.getSkuNum()).build());
} else { // 购物车下单
log.info("准备获取购物车");
CartVO cart = cartService.getCart();
log.info("购物车:{}", cart.toString());
orderItems = cart.getItems().stream().map(cartItem -> OmsOrderItem.builder().skuId(cartItem.getSkuId())
.skuQuantity(cartItem.getNum())
.skuQuantity(cartItem.getCount())
.build()
).collect(Collectors.toList());
}
......@@ -177,12 +171,10 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
orderDeliveryFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
threadLocal.set(submitInfoDTO);
String addressId = threadLocal.get().getAddressId();
UmsAddressDTO userAddress = memberFeignService.getAddressById(addressId).getData();
log.debug("获取用户地址:{}", userAddress.toString());
if (userAddress == null) {
throw new BizException("提交订单失败,无法获取用户地址信息");
throw new BizException("会员地址不存在");
}
OmsOrderDelivery orderDelivery = OmsOrderDelivery.builder()
.receiverName(userAddress.getName())
......@@ -239,12 +231,12 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
List<OmsOrderItem> orderItems = orderBO.getOrderItems();
List<InventoryDTO> items = orderItems.stream().map(orderItem -> InventoryDTO.builder()
.skuId(orderItem.getSkuId())
.num(orderItem.getSkuQuantity())
.count(orderItem.getSkuQuantity())
.build())
.collect(Collectors.toList());
Result result = inventoryFeignService.lockInventory(items);
if (result == null || !StrUtil.equals(result.getCode(), ResultCode.SUCCESS.getCode())) {
if (!StrUtil.equals(result.getCode(), ResultCode.SUCCESS.getCode())) {
throw new BizException("下单失败,锁定库存错误");
}
}
......@@ -270,14 +262,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
}
// 将订单放入延时队列,超时未支付系统自动关单
rabbitTemplate.convertAndSend("order_event_exchange", "order:create", orderBO.getOrder().getOrderSn());
// 保存日志
orderLogService.addOrderLogs(orderId,
orderBO.getOrder().getStatus(),
RequestUtils.getUserId().toString(),
"创建订单"
);
rabbitTemplate.convertAndSend("order_event_exchange", "order:create", orderId);
OrderSubmitResultVO result = new OrderSubmitResultVO();
result.setId(orderId);
......@@ -287,8 +272,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
@Override
public boolean closeOrder(String orderSn) {
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>().eq(OmsOrder::getOrderSn, orderSn));
public boolean closeOrder(Long orderId) {
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>().eq(OmsOrder::getId, orderId));
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
return false;
}
......@@ -372,14 +357,12 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
/**
* 获取订单商品 1. 直接购买 2. 购物车结算
*
* @return
*/
private List<OrderItemVO> getOrderItem(Long skuId, Integer num) {
private List<OrderItemVO> getOrderItems(Long skuId, Integer count) {
if (skuId != null) { // 直接购买
OrderItemVO itemVO = OrderItemVO.builder()
.skuId(skuId)
.num(num)
.count(count)
.build();
return Arrays.asList(itemVO);
}
......@@ -387,7 +370,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderDao, OmsOrder> implements
CartVO cart = cartService.getCart();
List<OrderItemVO> items = cart.getItems().stream()
.filter(CartItemVO::isChecked)
.map(item -> OrderItemVO.builder().skuId(item.getSkuId()).num(item.getNum()).build())
.map(item -> OrderItemVO.builder().skuId(item.getSkuId()).count(item.getCount()).build())
.collect(Collectors.toList());
return items;
}
......
......@@ -36,4 +36,8 @@ public interface InventoryFeignService {
Result<Boolean> unlockInventory(@RequestBody List<InventoryDTO> list);
@PutMapping("/api.app/v1/skus/batch/minus_inventory")
Result minusInventory(@RequestBody List<InventoryDTO> list);
}
......@@ -21,6 +21,6 @@ public class InventoryDTO {
private Long skuId;
@ApiModelProperty("数量")
private Integer num;
private Integer count;
}
......@@ -58,6 +58,17 @@ public class SkuController {
return Result.judge(result);
}
@ApiOperation(value = "扣减库存", httpMethod = "PUT")
@ApiImplicitParam(name = "list", value = "释放库存", required = true, paramType = "body", dataType = "InventoryNumDTO")
@PutMapping("/batch/minus_inventory")
public Result<Boolean> minusInventory(@RequestBody List<InventoryDTO> list) {
boolean result = iPmsSkuService.minusInventory(list);
return Result.judge(result);
}
@ApiOperation(value = "库存列表", httpMethod = "GET")
@ApiImplicitParam(name = "skuIds", value = "库存ID集合", required = true, paramType = "body", dataType = "String")
......
......@@ -38,4 +38,6 @@ public interface IPmsSkuService extends IService<PmsSku> {
* @return
*/
List<SkuDTO> listBySkuIds(List<Long> ids);
boolean minusInventory(List<InventoryDTO> list);
}
......@@ -35,11 +35,11 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
inventories.forEach(item -> {
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
.eq(PmsSku::getId, item.getSkuId())
.apply("inventory >= locked_inventory + {0}", item.getNum())
.setSql("locked_inventory = locked_inventory + " + item.getNum())
.apply("inventory >= locked_inventory + {0}", item.getCount())
.setSql("locked_inventory = locked_inventory + " + item.getCount())
);
if (!result) {
throw new BizException("锁定库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getNum());
throw new BizException("锁定库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getCount());
}
});
......@@ -48,21 +48,35 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
@Override
public boolean unlockInventory(List<InventoryDTO> inventories) {
log.info("释放库存:{}", inventories);
inventories.forEach(item -> {
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
.eq(PmsSku::getId, item.getSkuId())
.setSql("locked_inventory = locked_inventory - " + item.getNum())
.setSql("locked_inventory = locked_inventory - " + item.getCount())
);
if (!result) {
throw new BizException("解锁库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getNum());
throw new BizException("解锁库存失败,库存ID:" + item.getSkuId() + ",数量:" + item.getCount());
}
});
return true;
}
@Override
public boolean minusInventory(List<InventoryDTO> inventories) {
inventories.forEach(item -> {
boolean result = this.update(new LambdaUpdateWrapper<PmsSku>()
.eq(PmsSku::getId, item.getSkuId())
.setSql("locked_inventory = locked_inventory - " + item.getCount())
.setSql("inventory = inventory - " + item.getCount())
);
if (!result) {
throw new BizException("扣减库存失败");
}
});
return false;
}
/**
* Cache-Aside pattern 缓存、数据库读写模式
* 1. 读取数据,先读缓存,没有就去读数据库,然后将结果写入缓存
......@@ -100,4 +114,6 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
public List<SkuDTO> listBySkuIds(List<Long> ids) {
return this.baseMapper.listBySkuIds(ids);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册