diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/lock/SyncLockManager.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/lock/SyncLockManager.java index 87a3553e..14060780 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/lock/SyncLockManager.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/lock/SyncLockManager.java @@ -7,13 +7,16 @@ import java.util.concurrent.ConcurrentHashMap; /** * 智能同步任务本地锁管理器 - *

自动识别调用者(类名+方法名)和参数,生成唯一锁key,无需手动指定锁类型

+ *

支持两种使用方式:

+ *
    + *
  1. 自动识别调用者(类名+方法名)生成锁key - 适用于单方法独立锁
  2. + *
  3. 显式指定锁类型 - 适用于跨方法/跨服务协调锁
  4. + *
* - *

使用示例: + *

使用示例1(自动识别): *

- * // 加锁(自动识别调用方法和参数)
  * if (!syncLockManager.tryLock(externalSystemId)) {
- *     return; // 已有同步任务在执行
+ *     return;
  * }
  * try {
  *     // 执行同步逻辑
@@ -22,17 +25,91 @@ import java.util.concurrent.ConcurrentHashMap;
  * }
  * 
* - *

锁key格式:ClassName.methodName:param1:param2:...

+ *

使用示例2(显式锁类型 - 推荐用于跨服务协调): + *

+ * if (!syncLockManager.tryLock(SyncLockType.JENKINS_SYNC, externalSystemId)) {
+ *     return;
+ * }
+ * try {
+ *     // 执行同步逻辑
+ * } finally {
+ *     syncLockManager.unlock(SyncLockType.JENKINS_SYNC, externalSystemId);
+ * }
+ * 
*/ @Slf4j @Component public class SyncLockManager { - // 统一的锁存储:key = ClassName.methodName:param1:param2:... + // 统一的锁存储 private final ConcurrentHashMap locks = new ConcurrentHashMap<>(); + // ==================== 显式锁类型方法(推荐用于跨服务协调) ==================== + + /** + * 尝试获取锁(显式指定锁类型) + *

推荐用于需要跨方法/跨服务协调的场景

+ * + * @param lockType 锁类型 + * @param params 锁参数(如 externalSystemId) + * @return true-获取成功,false-已被锁定 + */ + public boolean tryLock(SyncLockType lockType, Object... params) { + String lockKey = buildLockKey(lockType, params); + boolean acquired = locks.putIfAbsent(lockKey, true) == null; + + if (!acquired) { + log.warn("同步任务正在执行中,跳过本次同步: lockType={}, params={}", + lockType.getKey(), formatParams(params)); + } else { + log.debug("获取同步锁成功: lockType={}, params={}", + lockType.getKey(), formatParams(params)); + } + + return acquired; + } + + /** + * 释放锁(显式指定锁类型) + * + * @param lockType 锁类型 + * @param params 锁参数(必须与tryLock时完全一致) + */ + public void unlock(SyncLockType lockType, Object... params) { + String lockKey = buildLockKey(lockType, params); + locks.remove(lockKey); + log.debug("同步任务完成,释放锁: lockType={}, params={}", + lockType.getKey(), formatParams(params)); + } + + /** + * 检查锁是否被持有(显式指定锁类型) + * + * @param lockType 锁类型 + * @param params 锁参数 + * @return true-已被锁定,false-未锁定 + */ + public boolean isLocked(SyncLockType lockType, Object... params) { + String lockKey = buildLockKey(lockType, params); + return locks.containsKey(lockKey); + } + + /** + * 构建锁key(显式锁类型) + */ + private String buildLockKey(SyncLockType lockType, Object... params) { + StringBuilder key = new StringBuilder(lockType.getKey()); + for (Object param : params) { + key.append(":").append(param != null ? param : "null"); + } + return key.toString(); + } + + // ==================== 自动识别调用者方法(向后兼容) ==================== + /** * 尝试获取锁(自动识别调用者) + *

适用于单方法独立锁场景,不需要跨服务协调

* * @param params 锁参数(如 externalSystemId, viewId等) * @return true-获取成功,false-已被锁定 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java index 4ac11a5c..a9c418a5 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java @@ -25,6 +25,7 @@ import com.qqchen.deploy.backend.notification.service.INotificationSendService; import com.qqchen.deploy.backend.notification.dto.SendNotificationRequest; import com.qqchen.deploy.backend.deploy.dto.sync.JenkinsSyncContext; import com.qqchen.deploy.backend.deploy.lock.SyncLockManager; +import com.qqchen.deploy.backend.deploy.lock.SyncLockType; import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; @@ -117,8 +118,8 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl