赋值问题
This commit is contained in:
@@ -1,11 +1,8 @@
|
|||||||
package com.czg.task;
|
package com.czg.task;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import com.czg.account.entity.ShopInfo;
|
import com.czg.account.entity.ShopInfo;
|
||||||
import com.czg.account.entity.ShopUser;
|
|
||||||
import com.czg.account.service.ShopInfoService;
|
import com.czg.account.service.ShopInfoService;
|
||||||
import com.czg.account.service.ShopUserService;
|
|
||||||
import com.czg.constant.TableValueConstant;
|
import com.czg.constant.TableValueConstant;
|
||||||
import com.czg.constants.SystemConstants;
|
import com.czg.constants.SystemConstants;
|
||||||
import com.czg.exception.CzgException;
|
import com.czg.exception.CzgException;
|
||||||
@@ -15,13 +12,11 @@ import com.czg.market.service.MkDistributionUserService;
|
|||||||
import com.czg.market.service.OrderInfoService;
|
import com.czg.market.service.OrderInfoService;
|
||||||
import com.czg.order.entity.OrderInfo;
|
import com.czg.order.entity.OrderInfo;
|
||||||
import com.czg.service.market.enums.OrderStatusEnums;
|
import com.czg.service.market.enums.OrderStatusEnums;
|
||||||
import com.czg.utils.FunUtils;
|
|
||||||
import com.mybatisflex.core.query.QueryWrapper;
|
import com.mybatisflex.core.query.QueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.dubbo.config.annotation.DubboReference;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import com.czg.order.entity.MqLog;
|
|||||||
import com.czg.order.service.MqLogService;
|
import com.czg.order.service.MqLogService;
|
||||||
import com.czg.order.service.OrderInfoCustomService;
|
import com.czg.order.service.OrderInfoCustomService;
|
||||||
import com.czg.order.service.OrderInfoRpcService;
|
import com.czg.order.service.OrderInfoRpcService;
|
||||||
import com.czg.service.order.utils.FunUtil;
|
import com.czg.service.RedisService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
@@ -31,7 +31,7 @@ public class OrderMqListener {
|
|||||||
@Resource
|
@Resource
|
||||||
private OrderInfoCustomService orderInfoCustomService;
|
private OrderInfoCustomService orderInfoCustomService;
|
||||||
@Resource
|
@Resource
|
||||||
private FunUtil funUtil;
|
private RedisService redisService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订单上菜
|
* 订单上菜
|
||||||
@@ -44,7 +44,7 @@ public class OrderMqListener {
|
|||||||
info = info.replace("UP_ORDER_DETAIL:", "");
|
info = info.replace("UP_ORDER_DETAIL:", "");
|
||||||
log.info("接收到修改菜品状态mq, info: {}", info);
|
log.info("接收到修改菜品状态mq, info: {}", info);
|
||||||
String finalInfo = info;
|
String finalInfo = info;
|
||||||
funUtil.debounce("UP_ORDER_DETAIL:" + info, 5, () -> {
|
redisService.debounce("UP_ORDER_DETAIL:" + info, 5, () -> {
|
||||||
orderInfoCustomService.updateOrderDetailStatus(Long.valueOf(finalInfo));
|
orderInfoCustomService.updateOrderDetailStatus(Long.valueOf(finalInfo));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import com.czg.config.RabbitConstants;
|
|||||||
import com.czg.config.RedisCst;
|
import com.czg.config.RedisCst;
|
||||||
import com.czg.order.entity.MqLog;
|
import com.czg.order.entity.MqLog;
|
||||||
import com.czg.order.service.MqLogService;
|
import com.czg.order.service.MqLogService;
|
||||||
|
import com.czg.service.RedisService;
|
||||||
import com.czg.service.order.print.PrinterHandler;
|
import com.czg.service.order.print.PrinterHandler;
|
||||||
import com.czg.service.order.utils.FunUtil;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
@@ -27,7 +27,7 @@ public class PrintMqListener {
|
|||||||
@Resource
|
@Resource
|
||||||
private MqLogService mqLogService;
|
private MqLogService mqLogService;
|
||||||
@Resource
|
@Resource
|
||||||
private FunUtil funUtil;
|
private RedisService redisService;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Resource
|
@Resource
|
||||||
@@ -59,7 +59,7 @@ public class PrintMqListener {
|
|||||||
throw new RuntimeException("订单打印失败,未传递orderId");
|
throw new RuntimeException("订单打印失败,未传递orderId");
|
||||||
}
|
}
|
||||||
Boolean printOrder = jsonObject.getBoolean("printOrder");
|
Boolean printOrder = jsonObject.getBoolean("printOrder");
|
||||||
funUtil.runFunAndCheckKey(() -> {
|
redisService.runFunAndCheckKey(() -> {
|
||||||
printerHandler.handler(orderId, printOrder != null && !printOrder ? PrinterHandler.PrintTypeEnum.ONE : PrinterHandler.PrintTypeEnum.ONE_AND_ORDER);
|
printerHandler.handler(orderId, printOrder != null && !printOrder ? PrinterHandler.PrintTypeEnum.ONE : PrinterHandler.PrintTypeEnum.ONE_AND_ORDER);
|
||||||
return null;
|
return null;
|
||||||
}, RedisCst.getLockKey("orderPrint", orderId));
|
}, RedisCst.getLockKey("orderPrint", orderId));
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import jakarta.annotation.Resource;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author GYJoker
|
* @author GYJoker
|
||||||
@@ -650,4 +651,106 @@ public class RedisService {
|
|||||||
}
|
}
|
||||||
return JSON.parseArray(jsonStr, type);
|
return JSON.parseArray(jsonStr, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int retryCount = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行任务并保证锁唯一
|
||||||
|
*
|
||||||
|
* @param supplier 业务逻辑
|
||||||
|
* @param lockKey Redis锁的Key
|
||||||
|
* @return 业务逻辑返回值
|
||||||
|
*/
|
||||||
|
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
|
||||||
|
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
|
||||||
|
try {
|
||||||
|
// 尝试获取锁,超时时间 5 秒,防止死锁
|
||||||
|
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
||||||
|
int count = 0;
|
||||||
|
// 初始等待 10ms
|
||||||
|
int retryDelay = 10;
|
||||||
|
|
||||||
|
while (!lock) {
|
||||||
|
// 最多重试 10 次,大约 10 秒
|
||||||
|
if (count++ > 50) {
|
||||||
|
throw new RuntimeException("系统繁忙, 稍后再试");
|
||||||
|
}
|
||||||
|
Thread.sleep(retryDelay);
|
||||||
|
// 指数退避,最大等待 200ms
|
||||||
|
retryDelay = Math.min(retryDelay * 2, 200);
|
||||||
|
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行任务
|
||||||
|
return supplier.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new RuntimeException("线程被中断", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("执行出错:{}", e.getMessage(), e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
// 释放锁(使用 Lua 脚本确保原子性)
|
||||||
|
unlock(lockKey, lockValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 Lua 脚本确保释放锁的原子性
|
||||||
|
*
|
||||||
|
* @param lockKey 锁的 Key
|
||||||
|
* @param lockValue 当前线程的锁值
|
||||||
|
*/
|
||||||
|
private void unlock(String lockKey, String lockValue) {
|
||||||
|
String luaScript =
|
||||||
|
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
||||||
|
"return redis.call('del', KEYS[1]) " +
|
||||||
|
"else return 0 end";
|
||||||
|
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
|
||||||
|
Collections.singletonList(lockKey), lockValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, R> R runFunAndRetry(
|
||||||
|
Supplier<R> function,
|
||||||
|
Function<R, Boolean> check, Consumer<R> errFun) {
|
||||||
|
R result = function.get();
|
||||||
|
boolean flag = check.apply(result);
|
||||||
|
|
||||||
|
while (flag && retryCount-- > 0) {
|
||||||
|
result = function.get();
|
||||||
|
flag = check.apply(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
errFun.accept(result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防抖函数:在指定秒数内相同 Key 的任务只会执行一次
|
||||||
|
*
|
||||||
|
* @param key 防抖使用的 Redis Key
|
||||||
|
* @param seconds 防抖时间(秒)
|
||||||
|
* @param task 要执行的业务逻辑
|
||||||
|
* @return true 执行了任务;false 在防抖期内被拦截
|
||||||
|
*/
|
||||||
|
public boolean debounce(String key, long seconds, Runnable task) {
|
||||||
|
try {
|
||||||
|
Boolean success = redisTemplate.opsForValue().setIfAbsent(
|
||||||
|
key, "1", seconds, TimeUnit.SECONDS
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(success)) {
|
||||||
|
task.run();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("防抖函数执行失败 key={} err={}", key, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ public class OrderInfo implements Serializable {
|
|||||||
if (!isPrintSuccess) {
|
if (!isPrintSuccess) {
|
||||||
newPrintStatusArray.add(printJson);
|
newPrintStatusArray.add(printJson);
|
||||||
}
|
}
|
||||||
if (newPrintStatusArray.isEmpty()) {
|
if (!newPrintStatusArray.isEmpty()) {
|
||||||
printStatus = newPrintStatusArray.toJSONString();
|
printStatus = newPrintStatusArray.toJSONString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import com.czg.config.RedisCst;
|
|||||||
import com.czg.constants.ParamCodeCst;
|
import com.czg.constants.ParamCodeCst;
|
||||||
import com.czg.exception.CzgException;
|
import com.czg.exception.CzgException;
|
||||||
import com.czg.resp.CzgResult;
|
import com.czg.resp.CzgResult;
|
||||||
|
import com.czg.service.RedisService;
|
||||||
import com.czg.service.account.mapper.CallQueueMapper;
|
import com.czg.service.account.mapper.CallQueueMapper;
|
||||||
import com.czg.service.account.mapper.CallTableMapper;
|
import com.czg.service.account.mapper.CallTableMapper;
|
||||||
import com.czg.service.account.util.FunUtil;
|
|
||||||
import com.czg.service.account.util.WechatMiniMsgUtil;
|
import com.czg.service.account.util.WechatMiniMsgUtil;
|
||||||
import com.czg.system.dto.SysParamsDTO;
|
import com.czg.system.dto.SysParamsDTO;
|
||||||
import com.czg.system.service.SysParamsService;
|
import com.czg.system.service.SysParamsService;
|
||||||
@@ -51,10 +51,10 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopUserService shopUserService;
|
private ShopUserService shopUserService;
|
||||||
@Resource
|
@Resource
|
||||||
private FunUtil funUtil;
|
|
||||||
@Resource
|
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
@Resource
|
@Resource
|
||||||
|
private RedisService redisService;
|
||||||
|
@Resource
|
||||||
private ShopInfoService shopInfoService;
|
private ShopInfoService shopInfoService;
|
||||||
@Resource
|
@Resource
|
||||||
private CallConfigService callConfigService;
|
private CallConfigService callConfigService;
|
||||||
@@ -228,13 +228,13 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getCallNumber(Long shopId, CallTable callTable) {
|
private String getCallNumber(Long shopId, CallTable callTable) {
|
||||||
return funUtil.runFunAndCheckKey(() -> {
|
return redisService.runFunAndCheckKey(() -> {
|
||||||
String callNumKey = RedisCst.getTableCallNumKey(shopId, callTable.getId());
|
String callNumKey = RedisCst.getTableCallNumKey(shopId, callTable.getId());
|
||||||
String value = stringRedisTemplate.opsForValue().get(callNumKey);
|
String value = stringRedisTemplate.opsForValue().get(callNumKey);
|
||||||
AtomicReference<String> newVal = new AtomicReference<>("");
|
AtomicReference<String> newVal = new AtomicReference<>("");
|
||||||
// 初始化
|
// 初始化
|
||||||
if (StrUtil.isBlank(value)) {
|
if (StrUtil.isBlank(value)) {
|
||||||
Boolean setFlag = FunUtil.runFunAndRetry(() -> stringRedisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
|
Boolean setFlag = RedisService.runFunAndRetry(() -> stringRedisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
|
||||||
_ -> newVal.set(stringRedisTemplate.opsForValue().get(callNumKey)));
|
_ -> newVal.set(stringRedisTemplate.opsForValue().get(callNumKey)));
|
||||||
|
|
||||||
if (setFlag) {
|
if (setFlag) {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.czg.market.vo.InviteUserVO;
|
|||||||
import com.czg.market.vo.MemberConfigVO;
|
import com.czg.market.vo.MemberConfigVO;
|
||||||
import com.czg.order.entity.OrderInfo;
|
import com.czg.order.entity.OrderInfo;
|
||||||
import com.czg.service.account.mapper.ShopUserMapper;
|
import com.czg.service.account.mapper.ShopUserMapper;
|
||||||
import com.czg.service.account.util.FunUtil;
|
|
||||||
import com.czg.utils.FunUtils;
|
import com.czg.utils.FunUtils;
|
||||||
import com.czg.utils.PageUtil;
|
import com.czg.utils.PageUtil;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
package com.czg.service.account.util;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Administrator
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class FunUtil {
|
|
||||||
@Resource
|
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
|
||||||
public static int retryCount = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行任务并保证锁唯一
|
|
||||||
* @param supplier 业务逻辑
|
|
||||||
* @param lockKey Redis锁的Key
|
|
||||||
* @return 业务逻辑返回值
|
|
||||||
*/
|
|
||||||
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
|
|
||||||
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
|
|
||||||
try {
|
|
||||||
// 尝试获取锁,超时时间 5 秒,防止死锁
|
|
||||||
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
|
||||||
int count = 0;
|
|
||||||
// 初始等待 10ms
|
|
||||||
int retryDelay = 10;
|
|
||||||
|
|
||||||
while (!lock) {
|
|
||||||
// 最多重试 10 次,大约 10 秒
|
|
||||||
if (count++ > 50) {
|
|
||||||
throw new RuntimeException("系统繁忙, 稍后再试");
|
|
||||||
}
|
|
||||||
Thread.sleep(retryDelay);
|
|
||||||
// 指数退避,最大等待 200ms
|
|
||||||
retryDelay = Math.min(retryDelay * 2, 200);
|
|
||||||
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行任务
|
|
||||||
return supplier.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new RuntimeException("线程被中断", e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("执行出错:{}", e.getMessage(), e);
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
// 释放锁(使用 Lua 脚本确保原子性)
|
|
||||||
unlock(lockKey, lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 Lua 脚本确保释放锁的原子性
|
|
||||||
* @param lockKey 锁的 Key
|
|
||||||
* @param lockValue 当前线程的锁值
|
|
||||||
*/
|
|
||||||
private void unlock(String lockKey, String lockValue) {
|
|
||||||
String luaScript =
|
|
||||||
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
|
||||||
"return redis.call('del', KEYS[1]) " +
|
|
||||||
"else return 0 end";
|
|
||||||
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
|
|
||||||
Collections.singletonList(lockKey), lockValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, R> R runFunAndRetry(
|
|
||||||
Supplier<R> function,
|
|
||||||
Function<R, Boolean> check, Consumer<R> errFun) {
|
|
||||||
log.info("工具类开始执行函数");
|
|
||||||
R result = function.get();
|
|
||||||
boolean flag = check.apply(result);
|
|
||||||
|
|
||||||
log.info("执行结果: {}", result);
|
|
||||||
|
|
||||||
while (flag && retryCount-- > 0) {
|
|
||||||
log.info("执行函数失败, 剩余尝试次数{}", retryCount);
|
|
||||||
result = function.get();
|
|
||||||
log.info("执行结果: {}", result);
|
|
||||||
flag = check.apply(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flag) {
|
|
||||||
errFun.accept(result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,9 @@ import cn.hutool.core.bean.BeanUtil;
|
|||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.czg.account.entity.PrintMachine;
|
import com.czg.account.entity.PrintMachine;
|
||||||
|
import com.czg.config.RedisCst;
|
||||||
import com.czg.market.service.OrderInfoService;
|
import com.czg.market.service.OrderInfoService;
|
||||||
import com.czg.order.entity.OrderInfo;
|
import com.czg.order.entity.OrderInfo;
|
||||||
import com.czg.order.entity.PrintMachineLog;
|
import com.czg.order.entity.PrintMachineLog;
|
||||||
@@ -247,18 +247,8 @@ public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMappe
|
|||||||
} finally {
|
} finally {
|
||||||
//仅当是最后一次任务时,才执行更新操作
|
//仅当是最后一次任务时,才执行更新操作
|
||||||
if (isLastTask) {
|
if (isLastTask) {
|
||||||
redisService.del("order:print:" + orderId);
|
|
||||||
super.updateById(entity);
|
super.updateById(entity);
|
||||||
OrderInfo orderInfo = orderInfoService.getOne(query().select(OrderInfo::getPrintStatus).eq(OrderInfo::getId, orderId));
|
updateOrderEntity(orderId, config, isPrintSuccess);
|
||||||
if (orderInfo == null) {
|
|
||||||
orderInfo = new OrderInfo();
|
|
||||||
}
|
|
||||||
JSONObject jsonObject = new JSONObject();
|
|
||||||
jsonObject.put("id", config.getId());
|
|
||||||
jsonObject.put("name", config.getName());
|
|
||||||
jsonObject.put("time", LocalDateTime.now());
|
|
||||||
orderInfo.upPrintStatus(jsonObject, isPrintSuccess);
|
|
||||||
orderInfoService.update(orderInfo, query().eq(OrderInfo::getId, orderId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -278,6 +268,26 @@ public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMappe
|
|||||||
registerShutdownHookOnce();
|
registerShutdownHookOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新状态
|
||||||
|
*/
|
||||||
|
private void updateOrderEntity(Long orderId, PrintMachine config, boolean isPrintSuccess) {
|
||||||
|
redisService.runFunAndCheckKey(() -> {
|
||||||
|
OrderInfo orderInfo = orderInfoService.getOne(query().select(OrderInfo::getPrintStatus).eq(OrderInfo::getId, orderId));
|
||||||
|
if (orderInfo == null) {
|
||||||
|
orderInfo = new OrderInfo();
|
||||||
|
}
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("id", config.getId());
|
||||||
|
jsonObject.put("name", config.getName());
|
||||||
|
jsonObject.put("time", LocalDateTime.now());
|
||||||
|
orderInfo.upPrintStatus(jsonObject, isPrintSuccess);
|
||||||
|
orderInfoService.update(orderInfo, query().eq(OrderInfo::getId, orderId));
|
||||||
|
return orderInfo;
|
||||||
|
}, RedisCst.getLockKey("UP_ORDER_PRINT", orderId));
|
||||||
|
redisService.del("order:print:" + orderId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一更新打印日志实体
|
* 统一更新打印日志实体
|
||||||
*
|
*
|
||||||
@@ -300,66 +310,6 @@ public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMappe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// public void checkPrintStatus(PrintMachine config, PrintMachineLog entity) {
|
|
||||||
// // 云想印
|
|
||||||
// if ("云想印".equals(config.getContentType())) {
|
|
||||||
// String jsonStr = yxyPrinter.checkPrintStatus(config.getAddress(), entity.getTaskId());
|
|
||||||
// log.info("云想印打印状态查询结果:{}", jsonStr);
|
|
||||||
// JSONObject resp = JSONObject.parseObject(jsonStr);
|
|
||||||
// int code = resp.getIntValue("code");
|
|
||||||
// if (code == 0) {
|
|
||||||
// JSONObject data = resp.getJSONObject("data");
|
|
||||||
// boolean status = data.containsKey("status");
|
|
||||||
// if (!status) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// boolean success = data.getBooleanValue("status", false);
|
|
||||||
// if (entity.getFailFlag() == 0 && success) {
|
|
||||||
// entity.setFailFlag(0);
|
|
||||||
// entity.setRespMsg("打印成功");
|
|
||||||
// entity.setPrintTime(entity.getCreateTime());
|
|
||||||
// } else if (entity.getFailFlag() == 1 && success) {
|
|
||||||
// entity.setFailFlag(0);
|
|
||||||
// entity.setPrintTime(DateUtil.date().toLocalDateTime());
|
|
||||||
// entity.setRespMsg("打印成功");
|
|
||||||
// // 如果设备在线 and 休眠5秒后查询结果是未打印,即视为设备已离线,云端3分钟后才会同步到离线信息
|
|
||||||
// } else if (entity.getFailFlag() == 0 && !success) {
|
|
||||||
// entity.setFailFlag(1);
|
|
||||||
// entity.setPrintTime(null);
|
|
||||||
// entity.setRespMsg("0_离线(设备上线后自动补打)");
|
|
||||||
// } else {
|
|
||||||
// entity.setFailFlag(1);
|
|
||||||
// entity.setPrintTime(null);
|
|
||||||
// entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", entity.getRespMsg()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // 飞鹅云打印机
|
|
||||||
// } else if ("飞鹅".equals(config.getContentType())) {
|
|
||||||
// Boolean success = feiPrinter.checkFPrintStatus(entity.getTaskId());
|
|
||||||
// if (success == null) {
|
|
||||||
// entity.setFailFlag(1);
|
|
||||||
// entity.setRespMsg("打印失败,未知错误");
|
|
||||||
// } else if (success) {
|
|
||||||
// entity.setFailFlag(0);
|
|
||||||
// entity.setPrintTime(DateUtil.date().toLocalDateTime());
|
|
||||||
// entity.setRespMsg("打印成功");
|
|
||||||
// } else {
|
|
||||||
// String msg = feiPrinter.checkOnline(entity.getAddress());
|
|
||||||
// if (msg.indexOf("在线,工作状态正常") > 0) {
|
|
||||||
// entity.setFailFlag(0);
|
|
||||||
// entity.setPrintTime(DateUtil.date().toLocalDateTime());
|
|
||||||
// entity.setRespMsg("打印成功");
|
|
||||||
// } else {
|
|
||||||
// entity.setFailFlag(1);
|
|
||||||
// entity.setPrintTime(null);
|
|
||||||
// entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// super.updateById(entity);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 静态标识,确保关闭钩子仅注册一次
|
// 静态标识,确保关闭钩子仅注册一次
|
||||||
private static volatile boolean shutdownHookRegistered = false;
|
private static volatile boolean shutdownHookRegistered = false;
|
||||||
// 锁对象,保证线程安全
|
// 锁对象,保证线程安全
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
package com.czg.service.order.utils;
|
|
||||||
|
|
||||||
import cn.hutool.core.lang.func.Func0;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Administrator
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class FunUtil {
|
|
||||||
@Resource
|
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
|
||||||
public static int retryCount = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行任务并保证锁唯一
|
|
||||||
* @param supplier 业务逻辑
|
|
||||||
* @param lockKey Redis锁的Key
|
|
||||||
* @return 业务逻辑返回值
|
|
||||||
*/
|
|
||||||
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
|
|
||||||
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
|
|
||||||
try {
|
|
||||||
// 尝试获取锁,超时时间 5 秒,防止死锁
|
|
||||||
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
|
||||||
int count = 0;
|
|
||||||
// 初始等待 10ms
|
|
||||||
int retryDelay = 10;
|
|
||||||
|
|
||||||
while (!lock) {
|
|
||||||
// 最多重试 10 次,大约 10 秒
|
|
||||||
if (count++ > 50) {
|
|
||||||
throw new RuntimeException("系统繁忙, 稍后再试");
|
|
||||||
}
|
|
||||||
Thread.sleep(retryDelay);
|
|
||||||
// 指数退避,最大等待 200ms
|
|
||||||
retryDelay = Math.min(retryDelay * 2, 200);
|
|
||||||
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行任务
|
|
||||||
return supplier.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new RuntimeException("线程被中断", e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("执行出错:{}", e.getMessage(), e);
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
// 释放锁(使用 Lua 脚本确保原子性)
|
|
||||||
unlock(lockKey, lockValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 Lua 脚本确保释放锁的原子性
|
|
||||||
* @param lockKey 锁的 Key
|
|
||||||
* @param lockValue 当前线程的锁值
|
|
||||||
*/
|
|
||||||
private void unlock(String lockKey, String lockValue) {
|
|
||||||
String luaScript =
|
|
||||||
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
|
|
||||||
"return redis.call('del', KEYS[1]) " +
|
|
||||||
"else return 0 end";
|
|
||||||
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
|
|
||||||
Collections.singletonList(lockKey), lockValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, R> R runFunAndRetry(
|
|
||||||
Supplier<R> function,
|
|
||||||
Function<R, Boolean> check, Consumer<R> errFun) {
|
|
||||||
log.info("工具类开始执行函数");
|
|
||||||
R result = function.get();
|
|
||||||
boolean flag = check.apply(result);
|
|
||||||
|
|
||||||
log.info("执行结果: {}", result);
|
|
||||||
|
|
||||||
while (flag && retryCount-- > 0) {
|
|
||||||
log.info("执行函数失败, 剩余尝试次数{}", retryCount);
|
|
||||||
result = function.get();
|
|
||||||
log.info("执行结果: {}", result);
|
|
||||||
flag = check.apply(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flag) {
|
|
||||||
errFun.accept(result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 防抖函数:在指定秒数内相同 Key 的任务只会执行一次
|
|
||||||
* @param key 防抖使用的 Redis Key
|
|
||||||
* @param seconds 防抖时间(秒)
|
|
||||||
* @param task 要执行的业务逻辑
|
|
||||||
* @return true 执行了任务;false 在防抖期内被拦截
|
|
||||||
*/
|
|
||||||
public boolean debounce(String key, long seconds, Runnable task) {
|
|
||||||
try {
|
|
||||||
Boolean success = redisTemplate.opsForValue().setIfAbsent(
|
|
||||||
key, "1", seconds, TimeUnit.SECONDS
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Boolean.TRUE.equals(success)) {
|
|
||||||
task.run();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("防抖函数执行失败 key={} err={}", key, e.getMessage(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user