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
new file mode 100644
index 00000000..87a3553e
--- /dev/null
+++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/lock/SyncLockManager.java
@@ -0,0 +1,235 @@
+package com.qqchen.deploy.backend.deploy.lock;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 智能同步任务本地锁管理器
+ *
自动识别调用者(类名+方法名)和参数,生成唯一锁key,无需手动指定锁类型
+ *
+ * 使用示例:
+ *
+ * // 加锁(自动识别调用方法和参数)
+ * if (!syncLockManager.tryLock(externalSystemId)) {
+ * return; // 已有同步任务在执行
+ * }
+ * try {
+ * // 执行同步逻辑
+ * } finally {
+ * syncLockManager.unlock(externalSystemId);
+ * }
+ *
+ *
+ * 锁key格式:ClassName.methodName:param1:param2:...
+ */
+@Slf4j
+@Component
+public class SyncLockManager {
+
+ // 统一的锁存储:key = ClassName.methodName:param1:param2:...
+ private final ConcurrentHashMap locks = new ConcurrentHashMap<>();
+
+ /**
+ * 尝试获取锁(自动识别调用者)
+ *
+ * @param params 锁参数(如 externalSystemId, viewId等)
+ * @return true-获取成功,false-已被锁定
+ */
+ public boolean tryLock(Object... params) {
+ CallerInfo callerInfo = getCallerInfo();
+ String lockKey = buildLockKey(callerInfo, params);
+ boolean acquired = locks.putIfAbsent(lockKey, true) == null;
+
+ if (!acquired) {
+ log.warn("同步任务正在执行中,跳过本次同步: method={}, params={}",
+ callerInfo.getFullMethodName(), formatParams(params));
+ } else {
+ log.debug("获取同步锁成功: method={}, params={}",
+ callerInfo.getFullMethodName(), formatParams(params));
+ }
+
+ return acquired;
+ }
+
+ /**
+ * 释放锁(自动识别调用者)
+ *
+ * @param params 锁参数(必须与tryLock时完全一致)
+ */
+ public void unlock(Object... params) {
+ CallerInfo callerInfo = getCallerInfo();
+ String lockKey = buildLockKey(callerInfo, params);
+ locks.remove(lockKey);
+ log.debug("同步任务完成,释放锁: method={}, params={}",
+ callerInfo.getFullMethodName(), formatParams(params));
+ }
+
+ /**
+ * 检查锁是否被持有(自动识别调用者)
+ *
+ * @param params 锁参数
+ * @return true-已被锁定,false-未锁定
+ */
+ public boolean isLocked(Object... params) {
+ CallerInfo callerInfo = getCallerInfo();
+ String lockKey = buildLockKey(callerInfo, params);
+ return locks.containsKey(lockKey);
+ }
+
+ /**
+ * 获取当前锁数量(用于监控)
+ *
+ * @return 当前持有的锁数量
+ */
+ public int getLockCount() {
+ return locks.size();
+ }
+
+ /**
+ * 获取调用者信息(通过栈追踪,智能跳过Spring代理层)
+ *
+ * @return 调用者信息
+ */
+ private CallerInfo getCallerInfo() {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+
+ // 从栈顶开始遍历,跳过本类和Spring的代理/拦截器类
+ // 栈结构示例:
+ // 0: Thread.getStackTrace()
+ // 1: SyncLockManager.getCallerInfo()
+ // 2: SyncLockManager.tryLock/unlock/isLocked()
+ // 3+: 可能是Spring代理类或真实调用者
+
+ for (int i = 3; i < stackTrace.length; i++) {
+ StackTraceElement element = stackTrace[i];
+ String className = element.getClassName();
+ String methodName = element.getMethodName();
+
+ // 跳过 Spring 和 JDK 的内部类/代理类
+ if (shouldSkipFrame(className, methodName)) {
+ continue;
+ }
+
+ // 找到真正的业务调用者
+ String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
+ return new CallerInfo(simpleClassName, methodName, className + "." + methodName);
+ }
+
+ // 兜底:使用unknown
+ return new CallerInfo("Unknown", "unknown", "Unknown.unknown");
+ }
+
+ /**
+ * 判断是否应该跳过该栈帧
+ *
+ * @param className 类名
+ * @param methodName 方法名
+ * @return true-跳过,false-不跳过
+ */
+ private boolean shouldSkipFrame(String className, String methodName) {
+ // 跳过 CGLIB 动态代理类(所有包含$$的类都是CGLIB代理)
+ if (className.contains("$$")) {
+ return true;
+ }
+
+ // 跳过 JDK 动态代理
+ if (className.startsWith("jdk.proxy") || className.startsWith("com.sun.proxy")) {
+ return true;
+ }
+
+ // 跳过 Spring AOP 拦截器
+ if (className.contains("AsyncExecutionInterceptor") ||
+ className.contains("TransactionInterceptor") ||
+ className.contains("CglibAopProxy") ||
+ className.contains("ReflectiveMethodInvocation") ||
+ className.contains("DynamicAdvisedInterceptor")) {
+ return true;
+ }
+
+ // 跳过线程池和任务执行器
+ if (className.contains("ThreadPoolExecutor") ||
+ className.contains("FutureTask") ||
+ className.contains("CompletableFuture")) {
+ return true;
+ }
+
+ // 跳过 Java 反射
+ if (className.startsWith("java.lang.reflect") ||
+ className.startsWith("jdk.internal.reflect")) {
+ return true;
+ }
+
+ // 跳过 lambda 表达式的合成方法
+ if (methodName.contains("lambda$")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 构建锁的唯一key
+ *
+ * @param callerInfo 调用者信息
+ * @param params 参数列表
+ * @return 锁key
+ */
+ private String buildLockKey(CallerInfo callerInfo, Object... params) {
+ StringBuilder key = new StringBuilder(callerInfo.getFullMethodName());
+ for (Object param : params) {
+ key.append(":").append(param != null ? param : "null");
+ }
+ return key.toString();
+ }
+
+ /**
+ * 格式化参数用于日志输出
+ *
+ * @param params 参数列表
+ * @return 格式化字符串
+ */
+ private String formatParams(Object... params) {
+ if (params.length == 0) {
+ return "[]";
+ }
+
+ StringBuilder sb = new StringBuilder("[");
+ for (int i = 0; i < params.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(params[i]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * 调用者信息
+ */
+ private static class CallerInfo {
+ private final String simpleClassName;
+ private final String methodName;
+ private final String fullMethodName;
+
+ public CallerInfo(String simpleClassName, String methodName, String fullMethodName) {
+ this.simpleClassName = simpleClassName;
+ this.methodName = methodName;
+ this.fullMethodName = fullMethodName;
+ }
+
+ public String getSimpleClassName() {
+ return simpleClassName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public String getFullMethodName() {
+ return fullMethodName;
+ }
+ }
+}