预定订单保存时效:10分钟,10分钟后订单取消,不保存记录,菜品库存释放

This commit is contained in:
gaowdong 2025-05-19 13:07:07 +08:00
parent ca34461e7f
commit eb26f17dd4
9 changed files with 155 additions and 11 deletions

View File

@ -16,6 +16,7 @@ import com.bonus.canteen.core.menu.mapper.MenuRecipeDetailMapper;
import com.bonus.common.core.exception.ServiceException; import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.houqin.constant.GlobalConstants; import com.bonus.common.houqin.constant.GlobalConstants;
import com.bonus.common.houqin.utils.JacksonUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -107,6 +108,7 @@ public class MenuRecipeDishesServiceImpl extends ServiceImpl<MenuRecipeDishesMap
@Override @Override
public int reduceMenuRecipeDishesSupplyNum(MenuRecipeDishes menuRecipeDishes, Integer quantity) { public int reduceMenuRecipeDishesSupplyNum(MenuRecipeDishes menuRecipeDishes, Integer quantity) {
log.info("扣减库存入参:{},数量:{}", JacksonUtil.writeValueAsString(menuRecipeDishes), quantity);
if(menuRecipeDishes == null || menuRecipeDishes.getDetailId() == null || menuRecipeDishes.getDishesId() == null) { if(menuRecipeDishes == null || menuRecipeDishes.getDetailId() == null || menuRecipeDishes.getDishesId() == null) {
throw new ServiceException("参数错误"); throw new ServiceException("参数错误");
} }
@ -139,6 +141,7 @@ public class MenuRecipeDishesServiceImpl extends ServiceImpl<MenuRecipeDishesMap
@Override @Override
public int addMenuRecipeDishesSupplyNum(MenuRecipeDishes menuRecipeDishes, Integer quantity) { public int addMenuRecipeDishesSupplyNum(MenuRecipeDishes menuRecipeDishes, Integer quantity) {
log.info("新增库存入参:{},数量:{}", JacksonUtil.writeValueAsString(menuRecipeDishes), quantity);
if(menuRecipeDishes == null || menuRecipeDishes.getDetailId() == null || menuRecipeDishes.getDishesId() == null) { if(menuRecipeDishes == null || menuRecipeDishes.getDetailId() == null || menuRecipeDishes.getDishesId() == null) {
throw new ServiceException("参数错误"); throw new ServiceException("参数错误");
} }

View File

@ -73,6 +73,7 @@ public class OrderBusiness {
} }
public void reduceMenuDishSupplyNum(List<OrderDetail> orderDetailList) { public void reduceMenuDishSupplyNum(List<OrderDetail> orderDetailList) {
log.info("订单扣减库存入参:{}", JacksonUtil.writeValueAsString(orderDetailList));
for(OrderDetail orderDetail : orderDetailList) { for(OrderDetail orderDetail : orderDetailList) {
if(OrderDetailTypeEnum.KEYAMOUNT.getKey().equals(orderDetail.getDetailType())) { if(OrderDetailTypeEnum.KEYAMOUNT.getKey().equals(orderDetail.getDetailType())) {
continue; continue;
@ -86,13 +87,14 @@ public class OrderBusiness {
} }
public void addMenuDishSupplyNum(List<OrderDetail> orderDetailList) { public void addMenuDishSupplyNum(List<OrderDetail> orderDetailList) {
log.info("订单新增库存入参:{}", JacksonUtil.writeValueAsString(orderDetailList));
for(OrderDetail orderDetail : orderDetailList) { for(OrderDetail orderDetail : orderDetailList) {
if(OrderDetailTypeEnum.KEYAMOUNT.getKey().equals(orderDetail.getDetailType())) { if(OrderDetailTypeEnum.KEYAMOUNT.getKey().equals(orderDetail.getDetailType())) {
continue; continue;
} }
MenuRecipeDishes menuRecipeDishes = new MenuRecipeDishes(); MenuRecipeDishes menuRecipeDishes = new MenuRecipeDishes();
ObjectUtils.setAllFieldsToNull(menuRecipeDishes); ObjectUtils.setAllFieldsToNull(menuRecipeDishes);
menuRecipeDishes.setDetailId(orderDetail.getDetailId()); menuRecipeDishes.setDetailId(orderDetail.getMenuDetailId());
menuRecipeDishes.setDishesId(orderDetail.getGoodsId()); menuRecipeDishes.setDishesId(orderDetail.getGoodsId());
menuRecipeDishesService.addMenuRecipeDishesSupplyNum(menuRecipeDishes, orderDetail.getQuantity()); menuRecipeDishesService.addMenuRecipeDishesSupplyNum(menuRecipeDishes, orderDetail.getQuantity());
} }

View File

@ -1,5 +1,7 @@
package com.bonus.canteen.core.order.constants; package com.bonus.canteen.core.order.constants;
import org.apache.commons.lang3.StringUtils;
public enum OrderStateEnum { public enum OrderStateEnum {
PLACE(1, "已下单"), PLACE(1, "已下单"),
FINISH(2, "已完成"), FINISH(2, "已完成"),
@ -21,4 +23,13 @@ public enum OrderStateEnum {
public String getDesc() { public String getDesc() {
return this.desc; return this.desc;
} }
public static String getDescByKey(Integer key) {
for (OrderStateEnum orderState : OrderStateEnum.values()) {
if (orderState.getKey().equals(key)) {
return orderState.getDesc();
}
}
return StringUtils.EMPTY;
}
} }

View File

@ -0,0 +1,41 @@
package com.bonus.canteen.core.order.mq;
import com.bonus.canteen.core.common.utils.MqPayload;
import com.bonus.canteen.core.order.mq.bo.OrderCancelBO;
import com.bonus.canteen.core.order.service.IOrderInfoService;
import com.bonus.common.houqin.mq.MQListener;
import com.bonus.common.houqin.mq.MQMessageListener;
import com.bonus.common.houqin.utils.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import java.util.Objects;
@MQMessageListener(
group = "order-order-v3-async-timeout",
topic = "order",
tag = "order-v3-async-timeout"
)
public class OrderMqListenerTimeout implements MQListener<MqPayload<String>> {
private static final Logger log = LoggerFactory.getLogger(OrderMqListenerTimeout.class);
@Autowired
@Lazy
private IOrderInfoService orderInfoService;
public void onMessage(MqPayload<String> payload) {
log.info("[订单超时MQ]收到消息 {}", payload);
OrderCancelBO payloadData = JacksonUtil.readValue(payload.getData(), OrderCancelBO.class);
if(Objects.nonNull(payloadData)) {
try {
this.orderInfoService.timeOutCancel(payloadData.getOrderId(), "system");
}catch (Exception ex) {
log.error("[订单超时MQ]处理异常", ex);
}
}else {
log.error("[订单超时MQ]收到消息为空");
}
}
}

View File

@ -0,0 +1,10 @@
package com.bonus.canteen.core.order.mq.bo;
import lombok.Data;
@Data
public class OrderCancelBO {
private Long orderId;
private Long merchantId;
private String traceId;
}

View File

@ -0,0 +1,29 @@
package com.bonus.canteen.core.order.mq.utils;
import cn.hutool.core.lang.UUID;
import com.bonus.canteen.core.common.utils.MqUtil;
import com.bonus.canteen.core.order.mq.bo.OrderCancelBO;
import com.bonus.common.houqin.constant.GlobalConstants;
import com.bonus.common.houqin.mq.constant.LeMqConstant;
import com.bonus.common.houqin.utils.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
public class OrderMQUtils {
private static final Logger log = LoggerFactory.getLogger(OrderMQUtils.class);
public static LocalDateTime orderTimeoutByOrderId(Long orderId, int delaySecond) {
log.info("[订单MQ]发送未支付订单异步支付超时通知, 订单ID: {}", orderId);
try {
OrderCancelBO orderCancelPO = new OrderCancelBO();
orderCancelPO.setOrderId(orderId);
orderCancelPO.setMerchantId(GlobalConstants.TENANT_ID);
orderCancelPO.setTraceId(UUID.fastUUID().toString());
MqUtil.sendDelay(JacksonUtil.writeValueAsString(orderCancelPO), LeMqConstant.Topic.ORDER_V3_ASYNC_TIMEOUT, delaySecond * 1000);
}catch (Exception ex) {
log.error("[订单MQ]发送未支付订单超时通知失败, 订单ID: {}", orderId, ex);
}
return LocalDateTime.now().plusSeconds(delaySecond);
}
}

View File

@ -71,6 +71,6 @@ public interface IOrderInfoService
public void refund(Long orderId, String operationUser); public void refund(Long orderId, String operationUser);
public void pay(Long orderId); public void pay(Long orderId);
List<OrderRefundHistoryVO> orderRefundHistory(OrderRefundHistoryParam param); List<OrderRefundHistoryVO> orderRefundHistory(OrderRefundHistoryParam param);
public void writeOffOrderByOrderIds(OrderWriteOffParam param); public void writeOffOrderByOrderIds(OrderWriteOffParam param);
public void timeOutCancel(Long orderId, String operationUser);
} }

View File

@ -24,6 +24,7 @@ import com.bonus.canteen.core.order.constants.OrderStateEnum;
import com.bonus.canteen.core.order.domain.*; import com.bonus.canteen.core.order.domain.*;
import com.bonus.canteen.core.order.domain.param.*; import com.bonus.canteen.core.order.domain.param.*;
import com.bonus.canteen.core.order.mapper.OrderInfoMapper; import com.bonus.canteen.core.order.mapper.OrderInfoMapper;
import com.bonus.canteen.core.order.mq.utils.OrderMQUtils;
import com.bonus.canteen.core.order.service.IOrderDetailService; import com.bonus.canteen.core.order.service.IOrderDetailService;
import com.bonus.canteen.core.order.service.IOrderInfoService; import com.bonus.canteen.core.order.service.IOrderInfoService;
import com.bonus.canteen.core.pay.constants.PayChannelEnum; import com.bonus.canteen.core.pay.constants.PayChannelEnum;
@ -62,6 +63,7 @@ import java.util.stream.Collectors;
public class OrderInfoServiceImpl implements IOrderInfoService public class OrderInfoServiceImpl implements IOrderInfoService
{ {
private static final Logger log = LoggerFactory.getLogger(OrderInfoServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(OrderInfoServiceImpl.class);
private static final String ORDER_REFUND_REDIS_KEY = "sc:order_refund_orderId_%s";
@Autowired @Autowired
private OrderInfoMapper orderInfoMapper; private OrderInfoMapper orderInfoMapper;
@Autowired @Autowired
@ -127,7 +129,6 @@ public class OrderInfoServiceImpl implements IOrderInfoService
List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetail); List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetail);
order.setOrderDetailList(orderDetailList); order.setOrderDetailList(orderDetailList);
order.setPhoneNumber(SM4EncryptUtils.sm4Decrypt(order.getPhoneNumber())); order.setPhoneNumber(SM4EncryptUtils.sm4Decrypt(order.getPhoneNumber()));
order.setMealtimeName(AllocMealtimeTypeEnum.getDescByKey(order.getMealtimeType()));
}); });
} }
return orderInfoList; return orderInfoList;
@ -152,6 +153,9 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderBusiness.orderPay(orderInfoList); orderBusiness.orderPay(orderInfoList);
}catch (Exception ex) { }catch (Exception ex) {
orderBusiness.updateOrderPayFailed(orderInfoList); orderBusiness.updateOrderPayFailed(orderInfoList);
for(OrderInfo orderInfo : orderInfoList) {
OrderMQUtils.orderTimeoutByOrderId(orderInfo.getOrderId(), 60 * 10);
}
throw new ServiceException(ex.getMessage(), 500001); throw new ServiceException(ex.getMessage(), 500001);
}finally { }finally {
AccRedisUtils.unlockUpdateAccWalletBalance(orderAddParam.getUserId()); AccRedisUtils.unlockUpdateAccWalletBalance(orderAddParam.getUserId());
@ -364,7 +368,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
if(Objects.isNull(orderInfo)) { if(Objects.isNull(orderInfo)) {
throw new ServiceException("订单不存在"); throw new ServiceException("订单不存在");
} }
String refundKey = String.format("sc:order_refund_orderId_%s", orderId); String refundKey = String.format(ORDER_REFUND_REDIS_KEY, orderId);
if (!RedisUtil.setNx(refundKey, 1, 2)) { if (!RedisUtil.setNx(refundKey, 1, 2)) {
log.info("退单处理中:{}", refundKey); log.info("退单处理中:{}", refundKey);
throw new ServiceException("退单处理中"); throw new ServiceException("退单处理中");
@ -378,6 +382,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
// if(orderInfo.getOrderState().equals(OrderStateEnum.FINISH.getKey())) { // if(orderInfo.getOrderState().equals(OrderStateEnum.FINISH.getKey())) {
// throw new ServiceException("订单已核销"); // throw new ServiceException("订单已核销");
// } // }
List<OrderDetail> orderDetailList = null;
if(PayStateEnum.PAY_SUCC.getKey().equals(orderInfo.getPayState())) { if(PayStateEnum.PAY_SUCC.getKey().equals(orderInfo.getPayState())) {
List<TradeAndWallerInfo> accTradeList = accTradeService.queryTradeAndWallerInfoByOrderNo List<TradeAndWallerInfo> accTradeList = accTradeService.queryTradeAndWallerInfoByOrderNo
(String.valueOf(orderId), AccTradeTypeEnum.CONSUME); (String.valueOf(orderId), AccTradeTypeEnum.CONSUME);
@ -411,7 +416,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderInfoMapper.updateOrderInfo(refundOrderInfo); orderInfoMapper.updateOrderInfo(refundOrderInfo);
OrderDetail orderDetailQuery = new OrderDetail(); OrderDetail orderDetailQuery = new OrderDetail();
orderDetailQuery.setOrderId(orderId); orderDetailQuery.setOrderId(orderId);
List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetailQuery); orderDetailList = orderDetailService.selectOrderDetailList(orderDetailQuery);
for(OrderDetail orderDetail : orderDetailList) { for(OrderDetail orderDetail : orderDetailList) {
orderDetail.setRefundAmount(orderDetail.getRealAmount()); orderDetail.setRefundAmount(orderDetail.getRealAmount());
orderDetail.setRefundNum(orderDetail.getQuantity()); orderDetail.setRefundNum(orderDetail.getQuantity());
@ -419,7 +424,6 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderDetail.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser); orderDetail.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
orderDetailService.updateOrderDetail(orderDetail); orderDetailService.updateOrderDetail(orderDetail);
} }
orderBusiness.addMenuDishSupplyNum(orderDetailList);
try { try {
DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(orderInfo.getUserId()),"update"); DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(orderInfo.getUserId()),"update");
String jsonString = JacksonUtil.writeValueAsString(bean); String jsonString = JacksonUtil.writeValueAsString(bean);
@ -436,6 +440,44 @@ public class OrderInfoServiceImpl implements IOrderInfoService
refundOrderInfo.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser); refundOrderInfo.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
refundOrderInfo.setUpdateTime(DateUtils.getNowDate()); refundOrderInfo.setUpdateTime(DateUtils.getNowDate());
orderInfoMapper.updateOrderInfo(refundOrderInfo); orderInfoMapper.updateOrderInfo(refundOrderInfo);
OrderDetail orderDetailQuery = new OrderDetail();
orderDetailQuery.setOrderId(orderId);
orderDetailList = orderDetailService.selectOrderDetailList(orderDetailQuery);
}
if (orderDetailList != null) {
orderBusiness.addMenuDishSupplyNum(orderDetailList);
}
}
@Override
@Transactional(rollbackFor = {Exception.class})
public void timeOutCancel(Long orderId, String operationUser) {
log.info("订单超时取消:{}", orderId);
if(Objects.isNull(orderId) || orderId <= 0) {
throw new ServiceException("订单ID不能为空");
}
OrderInfo orderInfo = selectOrderInfoByOrderId(orderId);
if(Objects.isNull(orderInfo)) {
throw new ServiceException("订单不存在");
}
String refundKey = String.format(ORDER_REFUND_REDIS_KEY, orderId);
if (!RedisUtil.setNx(refundKey, 1, 2)) {
log.info("退单处理中:{}", refundKey);
throw new ServiceException("退单处理中");
}
if(orderInfo.getOrderState().equals(OrderStateEnum.WAIT_PLACE.getKey())) {
OrderInfo refundOrderInfo = new OrderInfo();
refundOrderInfo.setOrderId(orderId);
refundOrderInfo.setOrderRefundState(OrderRefundStateEnum.FINISH.getKey());
refundOrderInfo.setOrderState(OrderStateEnum.CANCEL.getKey());
refundOrderInfo.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
refundOrderInfo.setUpdateTime(DateUtils.getNowDate());
orderInfoMapper.updateOrderInfo(refundOrderInfo);
OrderDetail orderDetailQuery = new OrderDetail();
orderDetailQuery.setOrderId(orderId);
List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetailQuery);
orderBusiness.addMenuDishSupplyNum(orderDetailList);
}else {
log.info("[MQ超时订单]订单状态:{}", OrderStateEnum.getDescByKey(orderInfo.getOrderState()));
} }
} }
} }

View File

@ -1,9 +1,6 @@
package com.bonus.canteen.core.order.service.impl; package com.bonus.canteen.core.order.service.impl;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
@ -21,6 +18,8 @@ import com.bonus.canteen.core.order.service.IOrderShoppingCartService;
import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.houqin.utils.id.Id; import com.bonus.common.houqin.utils.id.Id;
import com.bonus.common.security.utils.SecurityUtils; import com.bonus.common.security.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -34,6 +33,8 @@ import org.springframework.stereotype.Service;
@Service @Service
public class OrderShoppingCartServiceImpl implements IOrderShoppingCartService public class OrderShoppingCartServiceImpl implements IOrderShoppingCartService
{ {
private static final Logger log = LoggerFactory.getLogger(OrderShoppingCartServiceImpl.class);
@Autowired @Autowired
private OrderShoppingCartMapper orderShoppingCartMapper; private OrderShoppingCartMapper orderShoppingCartMapper;
@Autowired @Autowired
@ -86,7 +87,12 @@ public class OrderShoppingCartServiceImpl implements IOrderShoppingCartService
.findFirst().orElse(null); .findFirst().orElse(null);
menuDishCheckDTO.setApplyDate(orderDateStr); menuDishCheckDTO.setApplyDate(orderDateStr);
Map<Integer, List<MenuRecipeDishesVO>> menuRecipeDishMap = menuModule.getMenuRecipeDish(menuDishCheckDTO); Map<Integer, List<MenuRecipeDishesVO>> menuRecipeDishMap = new HashMap<>();
try{
menuRecipeDishMap = menuModule.getMenuRecipeDish(menuDishCheckDTO);
}catch (Exception ex) {
log.info("菜单数据获取失败:" + ex.getMessage());
}
for (OrderShoppingCart shoppingCart : carts) { for (OrderShoppingCart shoppingCart : carts) {
OrderShoppingCartVO shoppingCartVO = convertToOrderShoppingCartVO(shoppingCart, menuRecipeDishMap); OrderShoppingCartVO shoppingCartVO = convertToOrderShoppingCartVO(shoppingCart, menuRecipeDishMap);