key 过期

订单 超时 取消 定时任务
历史订单
订单取消 队列
redis 序列化
This commit is contained in:
2025-02-26 16:14:31 +08:00
parent 0674936901
commit b056d53e91
13 changed files with 191 additions and 44 deletions

View File

@@ -0,0 +1,35 @@
package com.czg.config;
import cn.hutool.core.date.DateUtil;
import com.czg.order.entity.OrderInfo;
import com.czg.order.service.OrderInfoService;
import com.czg.service.order.enums.OrderStatusEnums;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 订单定时任务
*
* @author ww
* @description
*/
@Slf4j
@Component
public class OTimeTask {
@Resource
private OrderInfoService orderInfoService;
/**
* order 过期
*/
@Scheduled(cron = "0 0 1 * * ? ")
public void run() {
orderInfoService.updateChain()
.set(OrderInfo::getStatus, OrderStatusEnums.CANCELLED.getCode())
.eq(OrderInfo::getStatus, OrderStatusEnums.UNPAID.getCode())
.lt(OrderInfo::getTradeDay, DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd"))
.update();
}
}

View File

@@ -0,0 +1,51 @@
package com.czg.config;
import com.czg.order.service.OrderInfoService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
* redis key过期监听
*
* @author ww
*/
@Slf4j
@Configuration
public class RedisKeyExpirationListener implements MessageListener {
@Value("${spring.data.redis.database}")
private String database;
@Resource
private OrderInfoService tbOrderInfoService;
//redis key失效监听
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 监听特定键的过期事件
container.addMessageListener(this, new PatternTopic("__keyevent@" + database + "__:expired"));
return container;
}
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = new String(message.getBody());
// 检查过期的键是否以特定前缀开头
if (expiredKey.startsWith(RedisCst.classKeyExpired.EXPIRED_ORDER)) {
log.info("监听到订单过期,订单Id: {}", expiredKey);
String orderId = expiredKey.substring(RedisCst.classKeyExpired.EXPIRED_ORDER.length());
tbOrderInfoService.expired(Long.parseLong(orderId));
}
}
}

View File

@@ -42,8 +42,10 @@ public class AdminOrderController {
}
@GetMapping("/historyOrder")
public CzgResult<HistoryOrderVo> historyOrder(Long orderId) {
return CzgResult.success(orderInfoService.historyOrder(orderId));
public CzgResult<HistoryOrderVo> historyOrder(
@RequestParam(required = false) Long orderId,
@RequestParam(required = false) String tableCode) {
return CzgResult.success(orderInfoService.historyOrder(orderId, tableCode));
}
@PostMapping("/createOrder")
@@ -58,7 +60,6 @@ public class AdminOrderController {
/**
* 订单全额退款 只传订单id
* 部分退款 传参refundDetailMap {"详情id":"数量","详情id":"数量"}
*
*/
@PostMapping("/refundOrder")
public CzgResult<Object> refundOrder(@Validated @RequestBody OrderInfoRefundDTO refundDTO) {

View File

@@ -39,8 +39,10 @@ public class UserOrderController {
}
@GetMapping("/historyOrder")
public CzgResult<HistoryOrderVo> historyOrder(Long orderId) {
return CzgResult.success(orderInfoService.historyOrder(orderId));
public CzgResult<HistoryOrderVo> historyOrder(
@RequestParam(required = false) Long orderId,
@RequestParam(required = false) String tableCode) {
return CzgResult.success(orderInfoService.historyOrder(orderId, tableCode));
}
/**

View File

@@ -26,9 +26,14 @@ public class RabbitConfig {
// 设置消息过期时间为 180000 毫秒(即 180 秒)
args.put("x-message-ttl", 180000);
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRINT_QUEUE, true, false, false, args);
// return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_PRINT_QUEUE, true, false, false, null);
}
@Bean
public Queue orderCancelQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_CANCEL_QUEUE, true);
}
@Bean
public Queue orderStockQueue() {
return new Queue(activeProfile + "-" + RabbitConstants.Queue.ORDER_STOCK_QUEUE, true);

View File

@@ -11,6 +11,7 @@ public interface RabbitConstants {
class Queue {
public static final String ORDER_STOCK_QUEUE = "order.stock.queue";
public static final String ORDER_CANCEL_QUEUE = "order.cancel.queue";
public static final String ORDER_PRINT_QUEUE = "order.print.queue";
}
}

View File

@@ -17,24 +17,35 @@ public class RabbitPublisher {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 订单取消消息
* 目前 用来回滚库存
* @param orderId 订单id
*/
public void sendOrderCancelMsg(String orderId) {
sendMsg(RabbitConstants.Queue.ORDER_STOCK_QUEUE, orderId);
}
/**
* 库存损耗消息
* @param orderId 订单id
*/
public void sendOrderStockMsg(String orderId) {
sendMsg(RabbitConstants.Exchange.CASH_EXCHANGE, RabbitConstants.Queue.ORDER_STOCK_QUEUE, orderId);
sendMsg(RabbitConstants.Queue.ORDER_STOCK_QUEUE, orderId);
}
/**
* 订单打印消息
* @param orderId
* @param orderId 订单id
*/
public void sendOrderPrintMsg(String orderId) {
sendMsg(RabbitConstants.Exchange.CASH_EXCHANGE, RabbitConstants.Queue.ORDER_PRINT_QUEUE, orderId);
sendMsg(RabbitConstants.Queue.ORDER_PRINT_QUEUE, orderId);
}
private void sendMsg(String exchange, String queue, String msg) {
rabbitTemplate.convertAndSend(activeProfile + "-" + exchange, activeProfile + "-" + queue, msg);
private void sendMsg(String queue, String msg) {
rabbitTemplate.convertAndSend(activeProfile + "-" + RabbitConstants.Exchange.CASH_EXCHANGE, activeProfile + "-" + queue, msg);
}
}

View File

@@ -40,35 +40,9 @@ public class RedisConfig {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
@Bean
public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
return redisTemplate.opsForValue();
}
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}

View File

@@ -11,6 +11,13 @@ public interface RedisCst {
String LOGIN_CODE = "login:code:";
String SYS_LOG_KEY = "sys:log:";
/**
* key过期监听
*/
class classKeyExpired {
//订单key过期
public static final String EXPIRED_ORDER = "expired.order:";
}
String SMS_CODE = "sms:code:";

View File

@@ -1,5 +1,7 @@
package com.czg.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@@ -17,6 +19,8 @@ import java.util.concurrent.TimeUnit;
@Component
public class RedisService {
Logger log = LoggerFactory.getLogger(RedisService.class);
/**
* 默认过期时长为24小时单位
*/
@@ -59,6 +63,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -83,6 +88,7 @@ public class RedisService {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -127,6 +133,7 @@ public class RedisService {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -148,6 +155,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -215,6 +223,7 @@ public class RedisService {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -235,6 +244,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -252,6 +262,7 @@ public class RedisService {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -273,6 +284,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -346,6 +358,7 @@ public class RedisService {
try {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -471,6 +484,7 @@ public class RedisService {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -491,6 +505,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -507,6 +522,7 @@ public class RedisService {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -527,6 +543,7 @@ public class RedisService {
}
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}
@@ -544,6 +561,7 @@ public class RedisService {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
log.error("redis error:{}", e + "");
return false;
}
}

View File

@@ -25,7 +25,7 @@ import java.time.LocalDateTime;
public interface OrderInfoService extends IService<OrderInfo> {
Page<OrderInfoVo> getOrderByPage(OrderInfoQueryDTO param);
HistoryOrderVo historyOrder(Long orderId);
HistoryOrderVo historyOrder(Long orderId,String tableCode);
OrderInfo createOrder(OrderInfoAddDTO param);
OrderInfo checkOrderPay(CheckOrderPay param);
@@ -35,4 +35,6 @@ public interface OrderInfoService extends IService<OrderInfo> {
void refundCallBackOrder(@NotBlank String orderNo, @NotNull JSONObject resultJson);
void upOrderInfo(OrderInfo orderInfo, BigDecimal payAmount, LocalDateTime payTime, Long payOrderId, PayEnums payType);
void expired(Long orderId);
}

View File

@@ -35,7 +35,6 @@ import com.czg.service.RedisService;
import com.czg.service.order.dto.BigDecimalDTO;
import com.czg.service.order.enums.OrderStatusEnums;
import com.czg.service.order.mapper.OrderInfoMapper;
import com.czg.system.entity.SysParams;
import com.czg.utils.AssertUtil;
import com.czg.utils.CzgStrUtils;
import com.czg.utils.PageUtil;
@@ -131,13 +130,24 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
@Override
public HistoryOrderVo historyOrder(Long orderId) {
public HistoryOrderVo historyOrder(Long orderId, String tableCode) {
if (orderId == null && StrUtil.isBlank(tableCode)) {
throw new ValidateException("订单id或台桌码不可为空");
}
HistoryOrderVo historyOrderVo = new HistoryOrderVo();
OrderInfo orderInfo = getById(orderId);
OrderInfo orderInfo;
if (orderId == null) {
orderInfo = queryChain()
.select()
.eq(OrderInfo::getTableCode, tableCode)
.one();
} else {
orderInfo = getById(orderId);
}
AssertUtil.isNull(orderInfo, "订单不存在");
historyOrderVo.setInfo(orderInfo);
List<OrderDetail> orderDetails = orderDetailService.queryChain().select()
.eq(OrderDetail::getOrderId, orderId).list();
.eq(OrderDetail::getOrderId, orderInfo.getId()).list();
Map<Integer, List<OrderDetail>> resultMap = new HashMap<>();
// 遍历订单详情列表
for (OrderDetail orderDetail : orderDetails) {
@@ -208,6 +218,8 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
if ("after-pay".equals(orderInfo.getPayMode())) {
//发送打票信息
rabbitPublisher.sendOrderPrintMsg(orderInfo.getId().toString());
} else {
redisService.set(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId(), "", 60 * 15);
}
rabbitPublisher.sendOrderStockMsg(orderInfo.getId().toString());
return orderInfo;
@@ -216,6 +228,13 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
@Override
public OrderInfo checkOrderPay(CheckOrderPay param) {
OrderInfo orderInfo = getById(param.getOrderId());
if (!"after-pay".equals(orderInfo.getPayMode())) {
if (redisService.hasKey(RedisCst.classKeyExpired.EXPIRED_ORDER + param.getOrderId())) {
redisService.set(RedisCst.classKeyExpired.EXPIRED_ORDER + param.getOrderId(), "", 60 * 15);
} else {
throw new ValidateException("订单已过期,请重新下单");
}
}
log.info("订单信息:{},优惠信息:{}", JSONObject.toJSONString(orderInfo), JSONObject.toJSONString(param));
if (!orderInfo.getStatus().equals(OrderStatusEnums.UNPAID.getCode())) {
throw new ValidateException("生成支付订单失败,订单不可支付");
@@ -433,6 +452,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
upOrderInfo(orderInfo, new BigDecimal(czgCallBackDto.getAmount() / 100L),
DateUtil.parseLocalDateTime(czgCallBackDto.getPayTime()), payment.getId(), null);
redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
} else if ("memberIn".equals(payment.getPayType())) {
ShopUser shopUser = shopUserService.getById(payment.getSourceId());
if (shopUser == null) {
@@ -525,6 +545,23 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
}
}
/**
* 订单到期
*/
@Override
@Transactional
public void expired(Long orderId) {
OrderInfo orderInfo = getById(orderId);
if (orderInfo.getStatus().equals(OrderStatusEnums.UNPAID.getCode())) {
updateChain().set(OrderInfo::getStatus, OrderStatusEnums.CANCELLED.getCode())
.where(OrderInfo::getId).eq(orderId)
.update();
rabbitPublisher.sendOrderCancelMsg(orderId.toString());
} else {
log.info("订单取消失败,订单Id:{},订状态:{}", orderInfo.getId(), orderInfo.getStatus());
}
}
/**
* 初始化订单信息
*/

View File

@@ -94,6 +94,7 @@ public class PayServiceImpl implements PayService {
OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
orderInfoService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
LocalDateTime.now(), null, PayEnums.CREDIT_PAY);
redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
//TODO 挂账后续逻辑
return CzgResult.success();
}
@@ -104,6 +105,7 @@ public class PayServiceImpl implements PayService {
OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
orderInfoService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
LocalDateTime.now(), null, PayEnums.CASH_PAY);
redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
return CzgResult.success();
}
@@ -152,6 +154,7 @@ public class PayServiceImpl implements PayService {
Long flowId = shopUserService.updateMoney(shopUser.getShopId(), shopUserMoneyEditDTO);
orderInfoService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
LocalDateTime.now(), flowId, PayEnums.VIP_PAY);
redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
return CzgResult.success();
}