feat(REQ-3714): 更新org_user加上锁

This commit is contained in:
zhanghonghao 2025-03-14 10:20:16 +08:00
parent ad22076462
commit 8f1cc335be
3 changed files with 110 additions and 1 deletions

View File

@ -50,6 +50,10 @@
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId> <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.axzo.pokonyan</groupId>
<artifactId>pokonyan</artifactId>
</dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>--> <!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>lock4j-redisson-spring-boot-starter</artifactId>--> <!-- <artifactId>lock4j-redisson-spring-boot-starter</artifactId>-->

View File

@ -15,6 +15,7 @@ import cn.axzo.orgmanax.infra.dao.orguser.repository.OrgUserUpsertRepository;
import cn.axzo.orgmanax.server.mq.producer.OrgUserChangedEventProducer; import cn.axzo.orgmanax.server.mq.producer.OrgUserChangedEventProducer;
import cn.axzo.orgmanax.server.orguser.foundation.OrgUserFoundationService; import cn.axzo.orgmanax.server.orguser.foundation.OrgUserFoundationService;
import cn.axzo.orgmanax.server.orguser.foundation.req.OrgUserWithdrawOrQuitReq; import cn.axzo.orgmanax.server.orguser.foundation.req.OrgUserWithdrawOrQuitReq;
import cn.axzo.orgmanax.server.util.RedisLockUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair; import cn.hutool.core.lang.Pair;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -35,6 +36,13 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class OrgUserFoundationServiceImpl implements OrgUserFoundationService { public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
private static final String LOG_PREFIX = "[ORG_USER_OP] ";
private static final long OP_LOCK_TIMEOUT = 1000 * 5;
private static final String OP_LOCK_PREFIX = "ORG_USER";
private static final String OP_SINGLETON_UPSERT = "SINGLETON_UPSERT";
private static final String OP_BATCH_UPSERT = "BATCH_UPSERT";
private final WorkspaceGateway workspaceGateway; private final WorkspaceGateway workspaceGateway;
private final OrgUserQueryRepository orgUserQueryRepository; private final OrgUserQueryRepository orgUserQueryRepository;
private final OrgUserUpsertRepository orgUserUpsertRepository; private final OrgUserUpsertRepository orgUserUpsertRepository;
@ -42,10 +50,16 @@ public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
@Override @Override
public void batchWithdrawOrQuit(OrgUserWithdrawOrQuitReq req) { public void batchWithdrawOrQuit(OrgUserWithdrawOrQuitReq req) {
OrgUserStatusEnum status;
if (NumberUtil.isNotPositiveNumber(req.getWorkspaceId())) { if (NumberUtil.isNotPositiveNumber(req.getWorkspaceId())) {
return; return;
} }
// 优先获取批量写操作的锁成功获取后再执行单个的写操作
String redisKey = generateBatchOperationKey(req.getOuId(), req.getWorkspaceId());
RedisLockUtil.tryLock(redisKey, OP_LOCK_TIMEOUT, () -> dealUpdate(req));
}
private void dealUpdate(OrgUserWithdrawOrQuitReq req) {
OrgUserStatusEnum status;
if (req.isUnitUpdate()) { if (req.isUnitUpdate()) {
WorkspaceDetailReq workspaceDetailReq = new WorkspaceDetailReq(); WorkspaceDetailReq workspaceDetailReq = new WorkspaceDetailReq();
workspaceDetailReq.setId(req.getWorkspaceId()); workspaceDetailReq.setId(req.getWorkspaceId());
@ -123,4 +137,8 @@ public class OrgUserFoundationServiceImpl implements OrgUserFoundationService {
return dto; return dto;
} }
private static String generateBatchOperationKey(Long ouId, Long workspaceId) {
return RedisLockUtil.formatKey(OP_LOCK_PREFIX, OP_BATCH_UPSERT, ouId, workspaceId);
}
} }

View File

@ -0,0 +1,87 @@
package cn.axzo.orgmanax.server.util;
import cn.axzo.basics.common.util.AssertUtil;
import cn.axzo.maokai.common.enums.ErrorCodeEnum;
import cn.axzo.pokonyan.config.redis.RedisClient;
import cn.hutool.core.lang.UUID;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* @author luofu
* @version 1.0
* @date 2024/6/3
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class RedisLockUtil {
/**
* redis key format
*
* @param prefix 前缀
* @param params 参数
* @return 格式化后的字符串
*/
public static String formatKey(String prefix, Object... params) {
AssertUtil.notEmpty(prefix, "prefix can not be empty");
if (Objects.isNull(params) || 0 == params.length) {
return prefix;
}
String strParams = Arrays.stream(params).map(Object::toString)
.collect(Collectors.joining(REDIS_LOCK_SPLITER));
return APP_NAME + ":" + prefix + REDIS_LOCK_SPLITER + strParams;
}
/**
* do some thing while successfully fetch lock
*
* @param key
* @param tryTimeoutMills 超时时间单位毫秒
* @param executor 执行器
*/
public static void tryLock(String key, long tryTimeoutMills, Runnable executor) {
AssertUtil.notEmpty(key, "key can not be empty");
AssertUtil.notNull(executor, "executor can not be null");
String requestId = UUID.randomUUID().toString();
try {
long keyTimeoutMills = tryTimeoutMills * 2;
Boolean lockResult = RedisClient.LockOps.getLockUntilTimeout(key, requestId, keyTimeoutMills, TimeUnit.MILLISECONDS, tryTimeoutMills);
AssertUtil.isTrue(Boolean.TRUE.equals(lockResult), ErrorCodeEnum.SYSTEM_BUSY.getMessage());
executor.run();
} finally {
RedisClient.LockOps.releaseLock(key, requestId);
}
}
/**
* do some thing while successfully fetch lock
*
* @param key
* @param tryTimeoutMills 超时时间单位毫秒
* @param executor 执行器
*/
public static <T> T tryLock(String key, long tryTimeoutMills, Supplier<T> executor) {
AssertUtil.notEmpty(key, "key can not be empty");
AssertUtil.notNull(executor, "executor can not be null");
String requestId = UUID.randomUUID().toString();
try {
long keyTimeoutMills = tryTimeoutMills * 2;
Boolean lockResult = RedisClient.LockOps.getLockUntilTimeout(key, requestId, keyTimeoutMills, TimeUnit.MILLISECONDS, tryTimeoutMills);
AssertUtil.isTrue(Boolean.TRUE.equals(lockResult), ErrorCodeEnum.SYSTEM_BUSY.getMessage());
return executor.get();
} finally {
RedisClient.LockOps.releaseLock(key, requestId);
}
}
private static final String REDIS_LOCK_SPLITER = ":";
private static final String APP_NAME = "[maokai]";
}