From 34f50153698c69fbe9545d67ba5900f494957285 Mon Sep 17 00:00:00 2001 From: qaq Date: Sat, 28 Jun 2025 22:42:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8E=E5=BC=95=E8=B5=B7=E7=9A=84=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go-gost/x/api/config_service.go | 29 ++- go-gost/x/internal/util/port/port.go | 193 ++++++++++++++++++ go-gost/x/socket/service.go | 9 +- go-gost/x/socket/websocket_reporter.go | 15 +- .../java/com/admin/common/utils/GostUtil.java | 4 +- .../com/admin/controller/FlowController.java | 63 ++++-- .../service/impl/ForwardServiceImpl.java | 109 ++++++++-- 7 files changed, 368 insertions(+), 54 deletions(-) create mode 100644 go-gost/x/internal/util/port/port.go diff --git a/go-gost/x/api/config_service.go b/go-gost/x/api/config_service.go index ed4c83d..bcf3143 100644 --- a/go-gost/x/api/config_service.go +++ b/go-gost/x/api/config_service.go @@ -2,7 +2,9 @@ package api import ( "fmt" + "net/http" + "strings" "time" @@ -10,6 +12,7 @@ import ( "github.com/go-gost/core/service" "github.com/go-gost/x/config" parser "github.com/go-gost/x/config/parsing/service" + kill "github.com/go-gost/x/internal/util/port" "github.com/go-gost/x/registry" ) @@ -534,8 +537,24 @@ func pauseService(ctx *gin.Context) { return } - // 关闭服务但保持注册状态 + // 获取服务地址用于强制断开连接 + var serviceAddr string + cfg := config.Global() + for _, s := range cfg.Services { + if s.Name == name { + serviceAddr = s.Addr + break + } + } + + // 使用和 updateService 相同的方法彻底断开所有连接 svc.Close() + registry.ServiceRegistry().Unregister(name) + + // 强制断开端口的所有连接 + if serviceAddr != "" { + _ = kill.ForceClosePortConnections(serviceAddr) + } // 更新配置中的暂停状态 config.OnUpdate(func(c *config.Config) error { @@ -893,8 +912,14 @@ func pauseServices(ctx *gin.Context) { return } - // 暂停服务 + // 使用和 updateService 相同的方法彻底断开所有连接 stp.service.Close() + registry.ServiceRegistry().Unregister(stp.name) + + // 强制断开端口的所有连接 + if serviceConfig.Addr != "" { + _ = kill.ForceClosePortConnections(serviceConfig.Addr) + } // 记录已暂停的服务 pausedServices = append(pausedServices, struct { diff --git a/go-gost/x/internal/util/port/port.go b/go-gost/x/internal/util/port/port.go new file mode 100644 index 0000000..f93bfb8 --- /dev/null +++ b/go-gost/x/internal/util/port/port.go @@ -0,0 +1,193 @@ +package port + +import ( + "bufio" + "fmt" + "net" + "os" + "runtime" + "strconv" + "strings" +) + +// ForceClosePortConnections 强制断开指定端口的所有连接 +func ForceClosePortConnections(addr string) error { + if addr == "" { + return nil + } + + // 解析地址和端口 + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + // 如果解析失败,可能是因为没有端口,或者格式不对 + return fmt.Errorf("failed to parse address %s: %v", addr, err) + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return fmt.Errorf("invalid port %s: %v", portStr, err) + } + + switch runtime.GOOS { + case "linux": + return forceClosePortConnectionsLinux(host, port) + default: + // 对于非Linux系统,静默忽略 + return nil + } +} + +// forceClosePortConnectionsLinux 在Linux上通过/proc/net/tcp*读取连接并关闭 +func forceClosePortConnectionsLinux(host string, port int) error { + // 关闭IPv4连接 + if err := closeConnectionsFromProcNet("/proc/net/tcp", port); err != nil { + fmt.Printf("Warning: failed to close IPv4 connections on port %d: %v\n", port, err) + } + + // 关闭IPv6连接 + if err := closeConnectionsFromProcNet("/proc/net/tcp6", port); err != nil { + fmt.Printf("Warning: failed to close IPv6 connections on port %d: %v\n", port, err) + } + + return nil +} + +// closeConnectionsFromProcNet 从/proc/net/tcp*文件读取连接信息并关闭连接 +func closeConnectionsFromProcNet(procFile string, targetPort int) error { + file, err := os.Open(procFile) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + // 跳过第一行(header) + scanner.Scan() + + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) + if len(fields) < 10 { + continue + } + + // local_address字段格式: IP:PORT (hex) + localAddr := fields[1] + parts := strings.Split(localAddr, ":") + if len(parts) != 2 { + continue + } + + // 解析端口(十六进制) + portHex := parts[1] + port, err := strconv.ParseInt(portHex, 16, 32) + if err != nil { + continue + } + + // 检查是否是目标端口 + if int(port) == targetPort { + // 获取远程地址信息 + remoteAddr := fields[2] + state := fields[3] + + // 只关闭已建立的连接,跳过监听状态 + if state == "0A" { // LISTEN状态 + continue + } + + // 尝试通过socket操作关闭连接 + if err := closeConnectionByAddresses(localAddr, remoteAddr); err != nil { + fmt.Printf("Warning: failed to close connection %s->%s: %v\n", localAddr, remoteAddr, err) + } + } + } + + return scanner.Err() +} + +// closeConnectionByAddresses 通过地址信息关闭连接 +func closeConnectionByAddresses(localAddr, remoteAddr string) error { + // 解析本地地址 + localParts := strings.Split(localAddr, ":") + if len(localParts) != 2 { + return fmt.Errorf("invalid local address format") + } + + // 解析远程地址 + remoteParts := strings.Split(remoteAddr, ":") + if len(remoteParts) != 2 { + return fmt.Errorf("invalid remote address format") + } + + // 转换十六进制IP和端口 + localIP, err := hexToIP(localParts[0]) + if err != nil { + return err + } + + localPort, err := strconv.ParseInt(localParts[1], 16, 32) + if err != nil { + return err + } + + remoteIP, err := hexToIP(remoteParts[0]) + if err != nil { + return err + } + + remotePort, err := strconv.ParseInt(remoteParts[1], 16, 32) + if err != nil { + return err + } + + // 创建连接并立即关闭来触发RST + localAddr4 := fmt.Sprintf("%s:%d", localIP, localPort) + remoteAddr4 := fmt.Sprintf("%s:%d", remoteIP, remotePort) + + return sendRSTToConnection(localAddr4, remoteAddr4) +} + +// hexToIP 将十六进制IP地址转换为可读格式 +func hexToIP(hexIP string) (string, error) { + if len(hexIP) == 8 { // IPv4 + ip := make([]byte, 4) + for i := 0; i < 4; i++ { + val, err := strconv.ParseUint(hexIP[i*2:(i+1)*2], 16, 8) + if err != nil { + return "", err + } + ip[3-i] = byte(val) // 小端序 + } + return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]), nil + } + // 对于IPv6,处理更复杂,这里简化处理 + return "", fmt.Errorf("IPv6 not implemented yet") +} + +// sendRSTToConnection 向指定连接发送RST包 +func sendRSTToConnection(localAddr, remoteAddr string) error { + // 尝试创建一个到远程地址的连接,然后立即使用SO_LINGER=0关闭 + // 这会发送RST包而不是正常的FIN包 + conn, err := net.Dial("tcp", remoteAddr) + if err != nil { + // 如果无法连接,说明连接可能已经不存在了 + return nil + } + + // 获取底层的TCP连接 + tcpConn, ok := conn.(*net.TCPConn) + if !ok { + conn.Close() + return fmt.Errorf("not a TCP connection") + } + + // 设置SO_LINGER为0,这样关闭时会发送RST而不是FIN + if err := tcpConn.SetLinger(0); err != nil { + tcpConn.Close() + return err + } + + // 关闭连接,这会发送RST包 + return tcpConn.Close() +} diff --git a/go-gost/x/socket/service.go b/go-gost/x/socket/service.go index d644b06..e893185 100644 --- a/go-gost/x/socket/service.go +++ b/go-gost/x/socket/service.go @@ -9,6 +9,7 @@ import ( "github.com/go-gost/core/service" "github.com/go-gost/x/config" parser "github.com/go-gost/x/config/parsing/service" + kill "github.com/go-gost/x/internal/util/port" "github.com/go-gost/x/registry" ) @@ -273,8 +274,14 @@ func pauseServices(req pauseServicesRequest) error { return errors.New(fmt.Sprintf("service %s configuration not found", stp.name)) } - // 暂停服务 + // 使用和 updateService 相同的方法彻底断开所有连接 stp.service.Close() + registry.ServiceRegistry().Unregister(stp.name) + + // 强制断开端口的所有连接 + if serviceConfig.Addr != "" { + _ = kill.ForceClosePortConnections(serviceConfig.Addr) + } // 记录已暂停的服务 pausedServices = append(pausedServices, struct { diff --git a/go-gost/x/socket/websocket_reporter.go b/go-gost/x/socket/websocket_reporter.go index f66544b..22294e3 100644 --- a/go-gost/x/socket/websocket_reporter.go +++ b/go-gost/x/socket/websocket_reporter.go @@ -341,14 +341,13 @@ func (w *WebSocketReporter) handleReceivedMessage(messageType int, message []byt // routeCommand 路由命令到对应的处理函数 func (w *WebSocketReporter) routeCommand(cmd CommandMessage) { - //jsonBytes, errs := json.MarshalIndent(cmd, "", " ") - //if errs != nil { - // fmt.Println("Error marshaling JSON:", errs) - // return - //} - // - //// 打印 JSON 字符串 - //fmt.Println("🔔 收到命令: ", string(jsonBytes)) + jsonBytes, errs := json.Marshal(cmd) + if errs != nil { + fmt.Println("Error marshaling JSON:", errs) + return + } + + fmt.Println("🔔 收到命令: ", string(jsonBytes)) var err error var response CommandResponse diff --git a/springboot-backend/src/main/java/com/admin/common/utils/GostUtil.java b/springboot-backend/src/main/java/com/admin/common/utils/GostUtil.java index 8618c5c..31a1ade 100644 --- a/springboot-backend/src/main/java/com/admin/common/utils/GostUtil.java +++ b/springboot-backend/src/main/java/com/admin/common/utils/GostUtil.java @@ -83,7 +83,7 @@ public class GostUtil { JSONArray services = new JSONArray(); services.add(name + "_tls"); data.put("services", services); - return WebSocketServer.send_msg(node_id, data, "PauseRemoteService"); + return WebSocketServer.send_msg(node_id, data, "PauseService"); } public static GostDto ResumeRemoteService(Long node_id, String name) { @@ -91,7 +91,7 @@ public class GostUtil { JSONArray services = new JSONArray(); services.add(name + "_tls"); data.put("services", services); - return WebSocketServer.send_msg(node_id, data, "ResumeRemoteService"); + return WebSocketServer.send_msg(node_id, data, "ResumeService"); } public static GostDto AddChains(Long node_id, String name, String remoteAddr, String protocol) { diff --git a/springboot-backend/src/main/java/com/admin/controller/FlowController.java b/springboot-backend/src/main/java/com/admin/controller/FlowController.java index 569d473..f5defcc 100644 --- a/springboot-backend/src/main/java/com/admin/controller/FlowController.java +++ b/springboot-backend/src/main/java/com/admin/controller/FlowController.java @@ -300,6 +300,12 @@ public class FlowController extends BaseController { */ private void pauseServiceDueToTunnelLimit(Integer tunnelId, String forwardId, String userId, String userTunnelId) { + // 先检查转发状态,如果已经是暂停状态就不需要调用暂停服务 + Forward currentForward = forwardService.getById(forwardId); + if (currentForward == null || currentForward.getStatus() == 0) { + return; // 转发不存在或已经暂停,无需处理 + } + Tunnel tunnel = tunnelService.getById(tunnelId); if (tunnel != null) { Node node = nodeService.getNodeById(tunnel.getInNodeId()); @@ -317,12 +323,11 @@ public class FlowController extends BaseController { } } - // 更新转发状态为暂停 - Forward forward = forwardService.getById(forwardId); - if (forward != null) { - forward.setStatus(0); - forwardService.updateById(forward); - } + // 更新转发状态为暂停(只更新状态字段,避免覆盖流量数据) + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", forwardId); + updateWrapper.set("status", 0); + forwardService.update(null, updateWrapper); } /** @@ -330,6 +335,12 @@ public class FlowController extends BaseController { */ private void pauseServiceDueToTunnelExpiration(Integer tunnelId, String forwardId, String userId, String userTunnelId) { + // 先检查转发状态,如果已经是暂停状态就不需要调用暂停服务 + Forward currentForward = forwardService.getById(forwardId); + if (currentForward == null || currentForward.getStatus() == 0) { + return; // 转发不存在或已经暂停,无需处理 + } + Tunnel tunnel = tunnelService.getById(tunnelId); if (tunnel != null) { Node node = nodeService.getNodeById(tunnel.getInNodeId()); @@ -347,12 +358,11 @@ public class FlowController extends BaseController { } } - // 更新转发状态为暂停 - Forward forward = forwardService.getById(forwardId); - if (forward != null) { - forward.setStatus(0); - forwardService.updateById(forward); - } + // 更新转发状态为暂停(只更新状态字段,避免覆盖流量数据) + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", forwardId); + updateWrapper.set("status", 0); + forwardService.update(null, updateWrapper); } /** @@ -416,8 +426,8 @@ public class FlowController extends BaseController { * 检查转发状态 - 优化版本,使用传入的Forward实体 */ private void checkForwardStatus(Forward forward, String userId, String userTunnelId) { - // 检查转发状态是否为正常(1) - if (forward.getStatus() != 1) { + // 检查转发状态是否为正常(1),如果不正常且不是暂停状态,才需要暂停 + if (forward.getStatus() != 1 && forward.getStatus() != 0) { pauseSpecificForward(forward, userId, userTunnelId); } } @@ -426,6 +436,11 @@ public class FlowController extends BaseController { * 暂停指定的转发服务 */ private void pauseSpecificForward(Forward forward, String userId, String userTunnelId) { + // 先检查转发状态,如果已经是暂停状态就不需要调用暂停服务 + if (forward.getStatus() == 0) { + return; // 已经暂停,无需处理 + } + Tunnel tunnel = tunnelService.getById(forward.getTunnelId()); if (tunnel != null) { Node node = nodeService.getNodeById(tunnel.getInNodeId()); @@ -443,9 +458,11 @@ public class FlowController extends BaseController { } } - // 更新转发状态为暂停 - forward.setStatus(0); - forwardService.updateById(forward); + // 更新转发状态为暂停(只更新状态字段,避免覆盖流量数据) + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", forward.getId()); + updateWrapper.set("status", 0); + forwardService.update(null, updateWrapper); } /** @@ -455,6 +472,11 @@ public class FlowController extends BaseController { List userForwards = forwardService.list(new QueryWrapper().eq("user_id", userId)); for (Forward forward : userForwards) { + // 先检查转发状态,如果已经是暂停状态就跳过 + if (forward.getStatus() == 0) { + continue; // 已经暂停,跳过此转发 + } + Tunnel tunnel = tunnelService.getById(forward.getTunnelId()); if (tunnel != null) { Node node = nodeService.getNodeById(tunnel.getInNodeId()); @@ -474,8 +496,11 @@ public class FlowController extends BaseController { } } - forward.setStatus(0); - forwardService.updateById(forward); + // 更新转发状态为暂停(只更新状态字段,避免覆盖流量数据) + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", forward.getId()); + updateWrapper.set("status", 0); + forwardService.update(null, updateWrapper); } } diff --git a/springboot-backend/src/main/java/com/admin/service/impl/ForwardServiceImpl.java b/springboot-backend/src/main/java/com/admin/service/impl/ForwardServiceImpl.java index 34ee5a9..5c46db5 100644 --- a/springboot-backend/src/main/java/com/admin/service/impl/ForwardServiceImpl.java +++ b/springboot-backend/src/main/java/com/admin/service/impl/ForwardServiceImpl.java @@ -148,7 +148,7 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.err("隧道已禁用,无法更新转发"); } - // 4. 检查权限和限制(仅当隧道发生变化时) + // 4. 检查权限和限制 UserPermissionResult permissionResult = null; if (isTunnelChanged(existForward, forwardUpdateDto)) { permissionResult = checkUserPermissions(currentUser, tunnel, forwardUpdateDto.getId()); @@ -156,35 +156,48 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.err(permissionResult.getErrorMessage()); } } + + // 5. 获取UserTunnel(即使隧道未变化也需要获取,用于构建服务名称) + UserTunnel userTunnel = null; + if (currentUser.getRoleId() != ADMIN_ROLE_ID) { + userTunnel = getUserTunnel(currentUser.getUserId(), tunnel.getId().intValue()); + if (userTunnel == null) { + return R.err("你没有该隧道权限"); + } + } else { + // 管理员用户也需要获取UserTunnel(如果存在的话),用于构建正确的服务名称 + // 通过forward记录获取原始的用户ID + userTunnel = getUserTunnel(existForward.getUserId(), tunnel.getId().intValue()); + } - // 5. 更新Forward对象 + // 6. 更新Forward对象 Forward updatedForward = updateForwardEntity(forwardUpdateDto, existForward, tunnel); - // 6. 获取所需的节点信息 + // 7. 获取所需的节点信息 NodeInfo nodeInfo = getRequiredNodes(tunnel); if (nodeInfo.isHasError()) { return R.err(nodeInfo.getErrorMessage()); } - // 7. 调用Gost服务更新转发 + // 8. 调用Gost服务更新转发 R gostResult; if (isTunnelChanged(existForward, forwardUpdateDto)) { // 隧道变化时:先删除原配置,再创建新配置 gostResult = updateGostServicesWithTunnelChange(existForward, updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, - nodeInfo, permissionResult != null ? permissionResult.getUserTunnel() : null); + nodeInfo, userTunnel); } else { // 隧道未变化时:直接更新配置 gostResult = updateGostServices(updatedForward, tunnel, permissionResult != null ? permissionResult.getLimiter() : null, - nodeInfo, permissionResult != null ? permissionResult.getUserTunnel() : null); + nodeInfo, userTunnel); } if (gostResult.getCode() != 0) { return gostResult; } updatedForward.setStatus(1); - // 8. 保存更新 + // 9. 保存更新 boolean result = this.updateById(updatedForward); return result ? R.ok("端口转发更新成功") : R.err("端口转发更新失败"); } @@ -213,6 +226,9 @@ public class ForwardServiceImpl extends ServiceImpl impl if (userTunnel == null) { return R.err("你没有该隧道权限"); } + } else { + // 管理员删除用户记录时,需要获取对应的UserTunnel用于构建正确的服务名称 + userTunnel = getUserTunnel(forward.getUserId(), tunnel.getId().intValue()); } // 5. 获取所需的节点信息 @@ -317,14 +333,20 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.err("你没有该隧道权限"); } } + + // 6. 确保获取UserTunnel用于构建服务名称(包括管理员用户) + if (userTunnel == null) { + // 通过forward记录获取原始的用户ID来查找UserTunnel + userTunnel = getUserTunnel(forward.getUserId(), tunnel.getId().intValue()); + } - // 6. 获取所需的节点信息 + // 7. 获取所需的节点信息 NodeInfo nodeInfo = getRequiredNodes(tunnel); if (nodeInfo.isHasError()) { return R.err(nodeInfo.getErrorMessage()); } - // 7. 调用Gost服务 + // 8. 调用Gost服务 String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), userTunnel); GostDto gostResult; @@ -354,7 +376,7 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.err(operation + "服务失败:" + gostResult.getMsg()); } - // 8. 更新转发状态 + // 9. 更新转发状态 forward.setStatus(targetStatus); forward.setUpdatedTime(System.currentTimeMillis()); boolean result = this.updateById(forward); @@ -691,20 +713,14 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.err("原隧道不存在,无法删除旧配置"); } - // 2. 获取原隧道的节点信息 - NodeInfo oldNodeInfo = getRequiredNodes(oldTunnel); - if (oldNodeInfo.isHasError()) { - log.warn("获取原隧道{}的节点信息失败: {}", oldTunnel.getId(), oldNodeInfo.getErrorMessage()); - } else { - // 3. 删除原有的Gost服务配置 - R deleteResult = deleteGostServices(existForward, oldTunnel, oldNodeInfo, userTunnel); - if (deleteResult.getCode() != 0) { - // 删除失败时记录日志,但不影响后续创建(可能原配置已不存在) - log.warn("删除原隧道{}的Gost配置失败: {}", oldTunnel.getId(), deleteResult.getMsg()); - } + // 2. 删除原有的Gost服务配置 + R deleteResult = deleteOldGostServices(existForward, oldTunnel, userTunnel); + if (deleteResult.getCode() != 0) { + // 删除失败时记录日志,但不影响后续创建(可能原配置已不存在) + log.warn("删除原隧道{}的Gost配置失败: {}", oldTunnel.getId(), deleteResult.getMsg()); } - // 4. 创建新的Gost服务配置 + // 3. 创建新的Gost服务配置 R createResult = createGostServices(updatedForward, newTunnel, limiter, nodeInfo, userTunnel); if (createResult.getCode() != 0) { updateForwardStatusToError(updatedForward); @@ -714,6 +730,55 @@ public class ForwardServiceImpl extends ServiceImpl impl return R.ok(); } + /** + * 删除原有的Gost服务(隧道变化时专用) + */ + private R deleteOldGostServices(Forward forward, Tunnel oldTunnel, UserTunnel userTunnel) { + // 获取原隧道的用户隧道关系 + UserTunnel oldUserTunnel = getUserTunnel(forward.getUserId(), oldTunnel.getId().intValue()); + String serviceName = buildServiceName(forward.getId(), forward.getUserId(), forward.getTunnelId(), oldUserTunnel); + + // 获取原隧道的节点信息 + NodeInfo oldNodeInfo = getRequiredNodes(oldTunnel); + + // 删除主服务(使用原隧道的入口节点) + if (!oldNodeInfo.isHasError() && oldNodeInfo.getInNode() != null) { + GostDto serviceResult = GostUtil.DeleteService(oldNodeInfo.getInNode().getId(), serviceName); + if (!isGostOperationSuccess(serviceResult)) { + log.warn("删除主服务失败: {}", serviceResult.getMsg()); + } + } + + // 如果原隧道是隧道转发类型,需要删除链和远程服务 + if (oldTunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) { + // 删除链服务 + if (!oldNodeInfo.isHasError() && oldNodeInfo.getInNode() != null) { + GostDto chainResult = GostUtil.DeleteChains(oldNodeInfo.getInNode().getId(), serviceName); + if (!isGostOperationSuccess(chainResult)) { + log.warn("删除链服务失败: {}", chainResult.getMsg()); + } + } + + // 删除远程服务(即使节点信息获取失败,也要尝试删除) + Node outNode = null; + if (!oldNodeInfo.isHasError()) { + outNode = oldNodeInfo.getOutNode(); + } else { + // 即使获取节点信息失败,也尝试直接获取出口节点来删除远程服务 + outNode = nodeService.getNodeById(oldTunnel.getOutNodeId()); + } + + if (outNode != null) { + GostDto remoteResult = GostUtil.DeleteRemoteService(outNode.getId(), serviceName); + if (!isGostOperationSuccess(remoteResult)) { + log.warn("删除远程服务失败: {}", remoteResult.getMsg()); + } + } + } + + return R.ok(); + } + /** * 删除Gost服务 */