6.3 超时取消订单
This commit is contained in:
parent
1ce438395f
commit
21fcb40991
@ -12,11 +12,18 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
|
@FeignClient(value = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
|
||||||
public interface ItemClient {
|
public interface ItemClient {
|
||||||
|
|
||||||
@GetMapping("/items")
|
@GetMapping("/items")
|
||||||
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
|
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
|
||||||
|
|
||||||
|
//批量扣减库存
|
||||||
@PutMapping("/items/stock/deduct")
|
@PutMapping("/items/stock/deduct")
|
||||||
void deductStock(@RequestBody List<OrderDetailDTO> items);
|
void deductStock(@RequestBody List<OrderDetailDTO> items);
|
||||||
|
|
||||||
|
//批量恢复库存
|
||||||
|
@PutMapping("/items/stock/restore")
|
||||||
|
void restoreStock(@RequestBody List<OrderDetailDTO> items);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
18
hm-api/src/main/java/com/hmall/api/client/PayClient.java
Normal file
18
hm-api/src/main/java/com/hmall/api/client/PayClient.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package com.hmall.api.client;
|
||||||
|
|
||||||
|
import com.hmall.api.client.fallback.PayClientFallback;
|
||||||
|
import com.hmall.api.dto.PayOrderDTO;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
|
||||||
|
@FeignClient(value = "pay-service", fallbackFactory = PayClientFallback.class)
|
||||||
|
public interface PayClient {
|
||||||
|
/**
|
||||||
|
* 根据交易订单id查询支付单
|
||||||
|
* @param id 业务订单id
|
||||||
|
* @return 支付单信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/pay-orders/biz/{id}")
|
||||||
|
PayOrderDTO queryPayOrderByBizOrderNo(@PathVariable("id") Long id);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.hmall.api.client.fallback;
|
package com.hmall.api.client.fallback;
|
||||||
|
|
||||||
|
import cn.hutool.db.sql.Order;
|
||||||
import com.hmall.api.client.ItemClient;
|
import com.hmall.api.client.ItemClient;
|
||||||
import com.hmall.api.dto.ItemDTO;
|
import com.hmall.api.dto.ItemDTO;
|
||||||
import com.hmall.api.dto.OrderDetailDTO;
|
import com.hmall.api.dto.OrderDetailDTO;
|
||||||
@ -26,7 +27,12 @@ public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
|
|||||||
@Override
|
@Override
|
||||||
public void deductStock(List<OrderDetailDTO> items) {
|
public void deductStock(List<OrderDetailDTO> items) {
|
||||||
// 库存扣减业务需要触发事务回滚,查询失败,抛出异常
|
// 库存扣减业务需要触发事务回滚,查询失败,抛出异常
|
||||||
log.error("[ITEM-FALLBACK] deductStock 降级,原因:", cause);
|
log.error("扣减库存失败,原因:", cause);
|
||||||
|
throw new BizIllegalException(cause);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void restoreStock(List<OrderDetailDTO> items){
|
||||||
|
log.error("恢复商品库存失败,原因:",cause);
|
||||||
throw new BizIllegalException(cause);
|
throw new BizIllegalException(cause);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.hmall.api.client.fallback;
|
||||||
|
|
||||||
|
import com.hmall.api.client.PayClient;
|
||||||
|
import com.hmall.api.dto.PayOrderDTO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class PayClientFallback implements FallbackFactory<PayClient> {
|
||||||
|
@Override
|
||||||
|
public PayClient create(Throwable cause) {
|
||||||
|
return new PayClient() {
|
||||||
|
@Override
|
||||||
|
public PayOrderDTO queryPayOrderByBizOrderNo(Long id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
49
hm-api/src/main/java/com/hmall/api/dto/PayOrderDTO.java
Normal file
49
hm-api/src/main/java/com/hmall/api/dto/PayOrderDTO.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package com.hmall.api.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 支付订单
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(description = "支付单数据传输实体")
|
||||||
|
public class PayOrderDTO {
|
||||||
|
@ApiModelProperty("id")
|
||||||
|
private Long id;
|
||||||
|
@ApiModelProperty("业务订单号")
|
||||||
|
private Long bizOrderNo;
|
||||||
|
@ApiModelProperty("支付单号")
|
||||||
|
private Long payOrderNo;
|
||||||
|
@ApiModelProperty("支付用户id")
|
||||||
|
private Long bizUserId;
|
||||||
|
@ApiModelProperty("支付渠道编码")
|
||||||
|
private String payChannelCode;
|
||||||
|
@ApiModelProperty("支付金额,单位分")
|
||||||
|
private Integer amount;
|
||||||
|
@ApiModelProperty("付类型,1:h5,2:小程序,3:公众号,4:扫码,5:余额支付")
|
||||||
|
private Integer payType;
|
||||||
|
@ApiModelProperty("付状态,0:待提交,1:待支付,2:支付超时或取消,3:支付成功")
|
||||||
|
private Integer status;
|
||||||
|
@ApiModelProperty("拓展字段,用于传递不同渠道单独处理的字段")
|
||||||
|
private String expandJson;
|
||||||
|
@ApiModelProperty("第三方返回业务码")
|
||||||
|
private String resultCode;
|
||||||
|
@ApiModelProperty("第三方返回提示信息")
|
||||||
|
private String resultMsg;
|
||||||
|
@ApiModelProperty("支付成功时间")
|
||||||
|
private LocalDateTime paySuccessTime;
|
||||||
|
@ApiModelProperty("支付超时时间")
|
||||||
|
private LocalDateTime payOverTime;
|
||||||
|
@ApiModelProperty("支付二维码链接")
|
||||||
|
private String qrCodeUrl;
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
@ApiModelProperty("更新时间")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
@ -82,4 +82,10 @@ public class ItemController {
|
|||||||
public void deductStock(@RequestBody List<OrderDetailDTO> items){
|
public void deductStock(@RequestBody List<OrderDetailDTO> items){
|
||||||
itemService.deductStock(items);
|
itemService.deductStock(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("批量恢复库存")
|
||||||
|
@PutMapping("/stock/restore")
|
||||||
|
public void restoreStock(@RequestBody List<OrderDetailDTO> items) {
|
||||||
|
itemService.restoreStock(items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,11 @@ import org.apache.ibatis.annotations.Update;
|
|||||||
* @since 2023-05-05
|
* @since 2023-05-05
|
||||||
*/
|
*/
|
||||||
public interface ItemMapper extends BaseMapper<Item> {
|
public interface ItemMapper extends BaseMapper<Item> {
|
||||||
|
//减少库存
|
||||||
@Update("UPDATE item SET stock = stock - #{num} WHERE id = #{itemId}")
|
@Update("UPDATE item SET stock = stock - #{num} WHERE id = #{itemId}")
|
||||||
void updateStock(OrderDetailDTO orderDetail);
|
void updateStock(OrderDetailDTO orderDetail);
|
||||||
|
|
||||||
|
//恢复库存
|
||||||
|
@Update("UPDATE item SET stock = stock + #{num} WHERE id = #{itemId}")
|
||||||
|
void restoreStock(OrderDetailDTO orderDetail);
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,6 @@ public interface IItemService extends IService<Item> {
|
|||||||
void deductStock(List<OrderDetailDTO> items);
|
void deductStock(List<OrderDetailDTO> items);
|
||||||
|
|
||||||
List<ItemDTO> queryItemByIds(Collection<Long> ids);
|
List<ItemDTO> queryItemByIds(Collection<Long> ids);
|
||||||
|
|
||||||
|
void restoreStock(List<OrderDetailDTO> items);
|
||||||
}
|
}
|
||||||
|
@ -67,4 +67,22 @@ public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements II
|
|||||||
// 5. 正常转换
|
// 5. 正常转换
|
||||||
return BeanUtils.copyList(items, ItemDTO.class);
|
return BeanUtils.copyList(items, ItemDTO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void restoreStock(List<OrderDetailDTO> items) {
|
||||||
|
String sqlStatement = "com.hmall.item.mapper.ItemMapper.restoreStock";
|
||||||
|
boolean r = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
r = executeBatch(items, (sqlSession, entity) ->
|
||||||
|
sqlSession.update(sqlStatement, entity));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BizIllegalException("恢复库存异常,可能出现业务故障!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
throw new BizIllegalException("恢复库存失败!");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package com.hmall.pay.controller;
|
package com.hmall.pay.controller;
|
||||||
|
|
||||||
|
import com.hmall.api.dto.PayOrderDTO;
|
||||||
import com.hmall.common.exception.BizIllegalException;
|
import com.hmall.common.exception.BizIllegalException;
|
||||||
|
|
||||||
import com.hmall.common.utils.BeanUtils;
|
import com.hmall.common.utils.BeanUtils;
|
||||||
import com.hmall.pay.domain.dto.PayApplyDTO;
|
import com.hmall.pay.domain.dto.PayApplyDTO;
|
||||||
import com.hmall.pay.domain.dto.PayOrderFormDTO;
|
import com.hmall.pay.domain.dto.PayOrderFormDTO;
|
||||||
|
import com.hmall.pay.domain.po.PayOrder;
|
||||||
import com.hmall.pay.domain.vo.PayOrderVO;
|
import com.hmall.pay.domain.vo.PayOrderVO;
|
||||||
import com.hmall.pay.enums.PayType;
|
import com.hmall.pay.enums.PayType;
|
||||||
import com.hmall.pay.service.IPayOrderService;
|
import com.hmall.pay.service.IPayOrderService;
|
||||||
@ -46,4 +48,11 @@ public class PayController {
|
|||||||
payOrderFormDTO.setId(id);
|
payOrderFormDTO.setId(id);
|
||||||
payOrderService.tryPayOrderByBalance(payOrderFormDTO);
|
payOrderService.tryPayOrderByBalance(payOrderFormDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("根据id查询支付单")
|
||||||
|
@GetMapping("/biz/{id}")
|
||||||
|
public PayOrderDTO queryPayOrderByBizOrderNo(@PathVariable("id") Long id){
|
||||||
|
PayOrder payOrder = payOrderService.lambdaQuery().eq(PayOrder::getBizOrderNo, id).one();
|
||||||
|
return BeanUtils.copyBean(payOrder, PayOrderDTO.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.hmall.trade.constants;
|
||||||
|
|
||||||
|
public interface MQConstants {
|
||||||
|
String DELAY_EXCHANGE_NAME = "trade.delay.direct";
|
||||||
|
String DELAY_ORDER_QUEUE_NAME = "trade.delay.order.queue";
|
||||||
|
String DELAY_ORDER_KEY = "delay.order.query";
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.hmall.trade.listener;
|
||||||
|
|
||||||
|
import com.hmall.api.client.PayClient;
|
||||||
|
import com.hmall.api.dto.PayOrderDTO;
|
||||||
|
import com.hmall.trade.constants.MQConstants;
|
||||||
|
import com.hmall.trade.domain.po.Order;
|
||||||
|
import com.hmall.trade.service.IOrderService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.Exchange;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OrderDelayMessageListener {
|
||||||
|
|
||||||
|
private final IOrderService orderService;
|
||||||
|
private final PayClient payClient;
|
||||||
|
|
||||||
|
@RabbitListener(bindings = @QueueBinding(
|
||||||
|
value = @Queue(name = MQConstants.DELAY_ORDER_QUEUE_NAME),
|
||||||
|
exchange = @Exchange(name = MQConstants.DELAY_EXCHANGE_NAME, delayed = "true"),
|
||||||
|
key = MQConstants.DELAY_ORDER_KEY
|
||||||
|
))
|
||||||
|
public void listenOrderDelayMessage(Long orderId){
|
||||||
|
// 1.查询订单
|
||||||
|
Order order = orderService.getById(orderId);
|
||||||
|
// 2.检测订单状态,判断是否已支付
|
||||||
|
if(order == null || order.getStatus() != 1){
|
||||||
|
// 订单不存在或者已经支付
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 3.未支付,需要查询支付流水状态
|
||||||
|
PayOrderDTO payOrder = payClient.queryPayOrderByBizOrderNo(orderId);
|
||||||
|
// 4.判断是否支付
|
||||||
|
if(payOrder != null && payOrder.getStatus() == 3){
|
||||||
|
// 4.1.已支付,标记订单状态为已支付
|
||||||
|
orderService.markOrderPaySuccess(orderId);
|
||||||
|
}else{
|
||||||
|
orderService.cancelOrder(orderId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,6 @@ public class PayStatusListener {
|
|||||||
key = "pay.success"
|
key = "pay.success"
|
||||||
))
|
))
|
||||||
public void listenPaySuccess(Long orderId){
|
public void listenPaySuccess(Long orderId){
|
||||||
log.info("MQtest:orderId:"+orderId);
|
|
||||||
orderService.markOrderPaySuccess(orderId);
|
orderService.markOrderPaySuccess(orderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,4 +18,5 @@ public interface IOrderService extends IService<Order> {
|
|||||||
Long createOrder(OrderFormDTO orderFormDTO);
|
Long createOrder(OrderFormDTO orderFormDTO);
|
||||||
|
|
||||||
void markOrderPaySuccess(Long orderId);
|
void markOrderPaySuccess(Long orderId);
|
||||||
|
void cancelOrder(long orderId);
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,11 @@ import com.hmall.api.client.ItemClient;
|
|||||||
import com.hmall.api.dto.ItemDTO;
|
import com.hmall.api.dto.ItemDTO;
|
||||||
import com.hmall.api.dto.OrderDetailDTO;
|
import com.hmall.api.dto.OrderDetailDTO;
|
||||||
import com.hmall.common.exception.BadRequestException;
|
import com.hmall.common.exception.BadRequestException;
|
||||||
|
import com.hmall.common.utils.BeanUtils;
|
||||||
import com.hmall.common.utils.UserContext;
|
import com.hmall.common.utils.UserContext;
|
||||||
|
|
||||||
|
|
||||||
|
import com.hmall.trade.constants.MQConstants;
|
||||||
import com.hmall.trade.domain.dto.OrderFormDTO;
|
import com.hmall.trade.domain.dto.OrderFormDTO;
|
||||||
import com.hmall.trade.domain.po.Order;
|
import com.hmall.trade.domain.po.Order;
|
||||||
import com.hmall.trade.domain.po.OrderDetail;
|
import com.hmall.trade.domain.po.OrderDetail;
|
||||||
@ -17,6 +19,7 @@ import com.hmall.trade.service.IOrderDetailService;
|
|||||||
import com.hmall.trade.service.IOrderService;
|
import com.hmall.trade.service.IOrderService;
|
||||||
import io.seata.spring.annotation.GlobalTransactional;
|
import io.seata.spring.annotation.GlobalTransactional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
private final ItemClient itemClient;
|
private final ItemClient itemClient;
|
||||||
private final IOrderDetailService detailService;
|
private final IOrderDetailService detailService;
|
||||||
private final CartClient cartClient;
|
private final CartClient cartClient;
|
||||||
|
private final RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@GlobalTransactional
|
@GlobalTransactional
|
||||||
@ -85,11 +89,29 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("库存不足!");
|
throw new RuntimeException("库存不足!");
|
||||||
}
|
}
|
||||||
|
// 5.发送延迟消息,检测订单支付状态
|
||||||
|
rabbitTemplate.convertAndSend(
|
||||||
|
MQConstants.DELAY_EXCHANGE_NAME,
|
||||||
|
MQConstants.DELAY_ORDER_KEY,
|
||||||
|
order.getId(),
|
||||||
|
message -> {
|
||||||
|
message.getMessageProperties().setDelay(10000);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
);
|
||||||
return order.getId();
|
return order.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void markOrderPaySuccess(Long orderId) {
|
public void markOrderPaySuccess(Long orderId) {
|
||||||
|
// 1.查询订单
|
||||||
|
Order old = getById(orderId);
|
||||||
|
// 2.判断订单状态
|
||||||
|
if (old == null || old.getStatus() != 1) {
|
||||||
|
// 订单不存在或者订单状态不是1,放弃处理
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 3.尝试更新订单
|
||||||
Order order = new Order();
|
Order order = new Order();
|
||||||
order.setId(orderId);
|
order.setId(orderId);
|
||||||
order.setStatus(2);
|
order.setStatus(2);
|
||||||
@ -113,4 +135,26 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
}
|
}
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void cancelOrder(long orderId) {
|
||||||
|
// 1. 标记订单为已关闭,同时更新关闭时间和更新时间
|
||||||
|
lambdaUpdate()
|
||||||
|
.set(Order::getStatus, 5)
|
||||||
|
.set(Order::getUpdateTime, LocalDateTime.now())
|
||||||
|
.set(Order::getCloseTime, LocalDateTime.now())
|
||||||
|
.eq(Order::getId, orderId)
|
||||||
|
.ne(Order::getStatus, 5)
|
||||||
|
.update();
|
||||||
|
|
||||||
|
// 恢复库存
|
||||||
|
// 获取 OrderDetail 的 List
|
||||||
|
List<OrderDetail> details = detailService.lambdaQuery()
|
||||||
|
.eq(OrderDetail::getOrderId, orderId)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 构建 OrderDetailDTO 的 List
|
||||||
|
List<OrderDetailDTO> dtos = BeanUtils.copyList(details, OrderDetailDTO.class);
|
||||||
|
itemClient.restoreStock(dtos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user