|
@@ -2,7 +2,10 @@ package org.dromara.order.service.impl;
|
|
|
|
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.util.IdUtil;
|
|
import cn.hutool.core.util.IdUtil;
|
|
|
|
|
+import com.baomidou.lock.LockInfo;
|
|
|
import com.baomidou.lock.LockTemplate;
|
|
import com.baomidou.lock.LockTemplate;
|
|
|
|
|
+import com.baomidou.lock.executor.RedissonLockExecutor;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.dromara.common.core.constant.CacheConstants;
|
|
import org.dromara.common.core.constant.CacheConstants;
|
|
@@ -42,6 +45,11 @@ public class SysOrderServiceImpl implements ISysOrderService {
|
|
|
|
|
|
|
|
private final LockTemplate lockTemplate;
|
|
private final LockTemplate lockTemplate;
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 订单号生成分布式锁Key前缀
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final String ORDER_CODE_LOCK_KEY = "order:code:lock:";
|
|
|
|
|
+
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
@Override
|
|
@Override
|
|
|
public boolean create(SysOrderCreateBo bo) {
|
|
public boolean create(SysOrderCreateBo bo) {
|
|
@@ -168,47 +176,52 @@ public class SysOrderServiceImpl implements ISysOrderService {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 按照一定规则生成订单号
|
|
* 按照一定规则生成订单号
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 规则:yyyyMMdd + 6位当日递增序号。
|
|
|
|
|
+ * 常规路径直接 Redis INCR 原子递增;
|
|
|
|
|
+ * 仅在计数器丢失时(Redis重启/Key过期),加分布式锁后查数据库恢复。
|
|
|
*/
|
|
*/
|
|
|
private String generateOrderCode() {
|
|
private String generateOrderCode() {
|
|
|
|
|
+ String date = DateUtil.format(new Date(), "yyyyMMdd");
|
|
|
|
|
+ String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
|
|
|
|
|
+ String lockKey = ORDER_CODE_LOCK_KEY + date;
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // 方案一:使用分布式锁来串行化
|
|
|
|
|
-// String date = DateUtil.format(new Date(), "yyyyMMddHHmmss");
|
|
|
|
|
-
|
|
|
|
|
-// LockInfo locker = lockTemplate.lock(LockContants.ORDER, 500L, 1000L);
|
|
|
|
|
-//
|
|
|
|
|
-// try {
|
|
|
|
|
-//
|
|
|
|
|
-// Long count = RedisUtils.getCacheObject(CacheConstants.SYS_ORDER_COUNT);
|
|
|
|
|
-// if (count == null) {
|
|
|
|
|
-// count = 1L;
|
|
|
|
|
-// }
|
|
|
|
|
-// RedisUtils.setCacheObject(CacheConstants.SYS_ORDER_COUNT, count + 1L);
|
|
|
|
|
-//
|
|
|
|
|
-// } finally {
|
|
|
|
|
-// // 无论是否抛出异常,都解锁
|
|
|
|
|
-// if (locker != null) {
|
|
|
|
|
-// lockTemplate.releaseLock(locker);
|
|
|
|
|
-// }
|
|
|
|
|
-// }
|
|
|
|
|
-// return date;
|
|
|
|
|
-
|
|
|
|
|
- // 方案二:Redis原子递增 以每天的订单为准
|
|
|
|
|
- // 先不按照品牌或门店隔离
|
|
|
|
|
- String date = DateUtil.format(new Date(), "yyyyMMdd");
|
|
|
|
|
- String redisKey = CacheConstants.SYS_ORDER_COUNT + date;
|
|
|
|
|
- long count = RedisUtils.incrAtomicValue(redisKey);
|
|
|
|
|
- if (count == 1L) {
|
|
|
|
|
- // 应对服务器时钟微调或网络延迟导致的临界点问题
|
|
|
|
|
- RedisUtils.expire(redisKey, 60L * 60L * 25L);
|
|
|
|
|
|
|
+ Long exists = RedisUtils.getCacheObject(redisKey);
|
|
|
|
|
+ if (exists == null) {
|
|
|
|
|
+ LockInfo lockInfo = lockTemplate.lock(lockKey, 5000L, 3000L, RedissonLockExecutor.class);
|
|
|
|
|
+ if (lockInfo == null) {
|
|
|
|
|
+ log.warn("订单号恢复锁获取超时,降级使用雪花ID");
|
|
|
|
|
+ return "ORD" + IdUtil.getSnowflakeNextIdStr();
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ exists = RedisUtils.getCacheObject(redisKey);
|
|
|
|
|
+ if (exists == null) {
|
|
|
|
|
+ SysOrder lastOrder = baseMapper.selectOne(
|
|
|
|
|
+ Wrappers.lambdaQuery(SysOrder.class)
|
|
|
|
|
+ .likeRight(SysOrder::getCode, date)
|
|
|
|
|
+ .orderByDesc(SysOrder::getId)
|
|
|
|
|
+ .last("LIMIT 1")
|
|
|
|
|
+ );
|
|
|
|
|
+ if (lastOrder != null) {
|
|
|
|
|
+ String lastCode = lastOrder.getCode();
|
|
|
|
|
+ exists = Long.parseLong(lastCode.substring(lastCode.length() - 6));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ exists = 0L;
|
|
|
|
|
+ }
|
|
|
|
|
+ RedisUtils.setCacheObject(redisKey, exists);
|
|
|
|
|
+ RedisUtils.expire(redisKey, 60L * 60L * 25L);
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ lockTemplate.releaseLock(lockInfo);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ long count = RedisUtils.incrAtomicValue(redisKey);
|
|
|
return date + String.format("%06d", count);
|
|
return date + String.format("%06d", count);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- // 容灾 如果Redis挂掉,降级采用雪花ID
|
|
|
|
|
- log.error("Redis 订单号生成失败,降级使用本地雪花算法", e);
|
|
|
|
|
|
|
+ log.error("订单号生成失败,降级使用本地雪花算法", e);
|
|
|
return "ORD" + IdUtil.getSnowflakeNextIdStr();
|
|
return "ORD" + IdUtil.getSnowflakeNextIdStr();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|