Преглед на файлове

refactor(waf): 重构 TCP 转发功能并优化相关服务

- 新增 AidedTcpService 辅助服务,用于处理 TCP 转发的通用逻辑
- 重构 TcpforwardingService,使用新的 AidedTcpService 实现大部分功能
- 优化 TCP 转发的添加、编辑和删除流程,提高代码可读性和可维护性
- 并发查询优化,提升 GetTcpforwarding 和 GetTcpForwardingAllIpsByHostId 函数性能- 移除冗余代码,简化 AddTcpForwarding 和 EditTcpForwarding 函数逻辑
-优化 DeleteTcpForwarding 函数,支持批量删除操作
fusu преди 14 часа
родител
ревизия
2c8b050acc
променени са 1 файла, в които са добавени 550 реда и са изтрити 0 реда
  1. 550 0
      internal/service/api/waf/aidedtcp.go

+ 550 - 0
internal/service/api/waf/aidedtcp.go

@@ -0,0 +1,550 @@
+package waf
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"maps"
+	"net"
+	"strconv"
+
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/model"
+	"github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
+	"github.com/go-nunu/nunu-layout-advanced/internal/service"
+	"github.com/go-nunu/nunu-layout-advanced/internal/service/api/flexCdn"
+)
+
+// AidedTcpService TCP转发辅助服务接口
+type AidedTcpService interface {
+	// 验证相关
+	ValidateAddRequest(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse) error
+	ValidateEditRequest(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse, oldData *model.Tcpforwarding) error
+	ValidateDeletePermission(oldData *model.Tcpforwarding, hostId int) error
+
+	// CDN操作相关
+	CreateCdnWebsite(ctx context.Context, formData v1.WebsiteSend) (int64, error)
+	UpdateCdnConfiguration(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require RequireResponse, formData v1.WebsiteSend) error
+	DeleteCdnServer(ctx context.Context, cdnWebId int) error
+
+	// 源站操作相关
+	AddOriginsToWebsite(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) (map[string]int64, error)
+	UpdateOriginServers(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, ipData *model.TcpForwardingRule) error
+
+	// 代理协议配置
+	ConfigureProxyProtocol(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) error
+
+	// 异步任务处理
+	ProcessAsyncTasks(req *v1.TcpForwardingRequest)
+	ProcessIpWhitelistChanges(ctx context.Context, req *v1.TcpForwardingRequest, ipData *model.TcpForwardingRule) error
+	ProcessDeleteIpWhitelist(ctx context.Context, id int) error
+
+	// 数据准备和配置
+	PrepareWafData(ctx context.Context, req *v1.TcpForwardingRequest) (RequireResponse, v1.WebsiteSend, error)
+	BuildTcpListenConfig(gatewayIps []string, portStr string) ([]byte, error)
+
+	// 模型构建
+	BuildTcpForwardingModel(req *v1.TcpForwardingDataRequest, ruleId int, require RequireResponse) *model.Tcpforwarding
+	BuildTcpRuleModel(reqData *v1.TcpForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.TcpForwardingRule
+
+	// 数据库操作
+	SaveToDatabase(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse, tcpId int64, cdnOriginIds map[string]int64) (int, error)
+	UpdateDatabaseRecords(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require RequireResponse, ipData *model.TcpForwardingRule) error
+	CleanupDatabaseRecords(ctx context.Context, id int) error
+
+	// 工具函数
+	ExtractIpsFromBackends(backends []string) []string
+}
+
+type aidedTcpService struct {
+	*service.Service
+	wafformatter  WafFormatterService
+	cdn           flexCdn.CdnService
+	proxy         flexCdn.ProxyService
+	globalRep     waf.GlobalLimitRepository
+	tcpRepository waf.TcpforwardingRepository
+}
+
+func NewAidedTcpService(
+	service *service.Service,
+	wafformatter WafFormatterService,
+	cdn flexCdn.CdnService,
+	proxy flexCdn.ProxyService,
+	globalRep waf.GlobalLimitRepository,
+	tcpRepository waf.TcpforwardingRepository,
+) AidedTcpService {
+	return &aidedTcpService{
+		Service:       service,
+		wafformatter:  wafformatter,
+		cdn:           cdn,
+		proxy:         proxy,
+		globalRep:     globalRep,
+		tcpRepository: tcpRepository,
+	}
+}
+
+// ValidateAddRequest 验证添加 TCP 转发请求的合法性
+// 该函数验证以下内容:
+// 1. 验证 WAF 端口数量限制(防止超出配额)
+// 2. 验证端口号是否已被占用(确保端口唯一性)
+func (s *aidedTcpService) ValidateAddRequest(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse) error {
+	// 验证端口数量限制
+	if err := s.wafformatter.validateWafPortCount(ctx, require.HostId); err != nil {
+		return fmt.Errorf("端口数量验证失败: %w", err)
+	}
+
+	// 验证端口占用情况
+	if err := s.wafformatter.VerifyPort(ctx, "tcp", int64(req.TcpForwardingData.Id), req.TcpForwardingData.Port, int64(require.HostId), ""); err != nil {
+		return fmt.Errorf("端口 %d 已被占用或不合法: %w", req.TcpForwardingData.Port, err)
+	}
+
+	return nil
+}
+
+// ValidateEditRequest 验证编辑 TCP 转发请求的合法性
+// 该函数仅在端口发生变更时才验证端口冲突,提高性能并避免不必要的检查
+func (s *aidedTcpService) ValidateEditRequest(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse, oldData *model.Tcpforwarding) error {
+	// 只有端口发生变更时才需要验证端口冲突
+	if oldData.Port != req.TcpForwardingData.Port {
+		if err := s.wafformatter.VerifyPort(ctx, "tcp", int64(req.TcpForwardingData.Id), req.TcpForwardingData.Port, int64(require.HostId), ""); err != nil {
+			return fmt.Errorf("新端口 %d 已被占用或不合法: %w", req.TcpForwardingData.Port, err)
+		}
+	}
+	return nil
+}
+
+// ValidateDeletePermission 验证删除 TCP 转发配置的权限
+// 该函数确保用户只能删除属于自己主机的配置,防止越权操作
+func (s *aidedTcpService) ValidateDeletePermission(oldData *model.Tcpforwarding, hostId int) error {
+	if oldData == nil {
+		return fmt.Errorf("TCP转发配置数据不存在")
+	}
+	if oldData.HostId != hostId {
+		return fmt.Errorf("用户权限不足,无法删除不属于自己主机的配置(主机ID: %d)", hostId)
+	}
+	return nil
+}
+
+// CreateCdnWebsite 在 CDN 系统中创建 TCP 代理网站
+// 该函数调用 CDN 服务接口创建一个新的 TCP 代理网站,返回网站ID用于后续配置
+func (s *aidedTcpService) CreateCdnWebsite(ctx context.Context, formData v1.WebsiteSend) (int64, error) {
+	tcpId, err := s.cdn.CreateWebsite(ctx, formData)
+	if err != nil {
+		return 0, fmt.Errorf("在CDN系统中创建 TCP 代理网站失败: %w", err)
+	}
+	if tcpId <= 0 {
+		return 0, fmt.Errorf("CDN系统返回了无效的网站ID: %d", tcpId)
+	}
+	return tcpId, nil
+}
+
+// UpdateCdnConfiguration 更新 CDN 系统中的 TCP 代理配置
+// 该函数根据变更内容智能更新:
+// 1. 端口变更 - 更新网站监听配置
+// 2. 名称变更 - 更新网站基本信息
+// 3. 代理协议变更 - 开启/关闭 Proxy Protocol
+func (s *aidedTcpService) UpdateCdnConfiguration(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require RequireResponse, formData v1.WebsiteSend) error {
+	// 更新网站端口
+	if oldData.Port != req.TcpForwardingData.Port {
+		if err := s.cdn.EditServerType(ctx, v1.EditWebsite{
+			Id:       int64(oldData.CdnWebId),
+			TypeJSON: formData.TcpJSON,
+		}, "tcp"); err != nil {
+			return fmt.Errorf("更新网站端口失败: %w", err)
+		}
+	}
+
+	// 更新网站名称
+	if oldData.Comment != req.TcpForwardingData.Comment {
+		nodeId, err := s.globalRep.GetNodeId(ctx, oldData.CdnWebId)
+		if err != nil {
+			return fmt.Errorf("获取节点ID失败: %w", err)
+		}
+		if err := s.cdn.EditServerBasic(ctx, int64(oldData.CdnWebId), require.Tag, nodeId); err != nil {
+			return fmt.Errorf("更新网站名称失败: %w", err)
+		}
+	}
+
+	// 更新代理协议
+	if oldData.Proxy != req.TcpForwardingData.Proxy {
+		if err := s.proxy.EditProxy(ctx, int64(oldData.CdnWebId), v1.ProxyProtocolJSON{
+			IsOn:    req.TcpForwardingData.Proxy,
+			Version: 1,
+		}); err != nil {
+			return fmt.Errorf("更新代理协议失败: %w", err)
+		}
+	}
+
+	return nil
+}
+
+// DeleteCdnServer 从 CDN 系统中删除 TCP 代理网站
+// 该函数删除 CDN 中的网站配置,释放端口资源和相关配置
+func (s *aidedTcpService) DeleteCdnServer(ctx context.Context, cdnWebId int) error {
+	if err := s.cdn.DelServer(ctx, int64(cdnWebId)); err != nil {
+		return fmt.Errorf("删除CDN服务器失败: %w", err)
+	}
+	return nil
+}
+
+// AddOriginsToWebsite 为 TCP 代理网站添加后端源站
+// 该函数执行以下操作:
+// 1. 批量创建后端服务器的源站记录
+// 2. 将源站关联到 TCP 代理网站
+// 3. 返回后端地址与源站ID的映射关系
+func (s *aidedTcpService) AddOriginsToWebsite(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) (map[string]int64, error) {
+	cdnOriginIds := make(map[string]int64)
+
+	// 批量创建源站
+	for _, backend := range req.TcpForwardingData.BackendList {
+		id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
+			ApiType:     "tcp",
+			BackendList: backend,
+			Comment:     req.TcpForwardingData.Comment,
+		})
+		if err != nil {
+			return nil, fmt.Errorf("添加源站失败 %s: %w", backend, err)
+		}
+		cdnOriginIds[backend] = id
+	}
+
+	// 批量关联源站到网站
+	for _, originId := range cdnOriginIds {
+		if err := s.cdn.AddServerOrigin(ctx, tcpId, originId); err != nil {
+			return nil, fmt.Errorf("关联源站到网站失败: %w", err)
+		}
+	}
+
+	return cdnOriginIds, nil
+}
+
+// UpdateOriginServers 更新 TCP 代理的后端源站配置
+// 该函数智能对比新旧后端列表,只处理变更的部分:
+// 1. 添加新增的后端服务器
+// 2. 删除不再需要的后端服务器
+// 3. 更新源站ID映射关系
+func (s *aidedTcpService) UpdateOriginServers(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, ipData *model.TcpForwardingRule) error {
+	addOrigins, delOrigins := s.wafformatter.findIpDifferences(ipData.BackendList, req.TcpForwardingData.BackendList)
+
+	// 添加新源站
+	addedIds := make(map[string]int64)
+	for _, origin := range addOrigins {
+		id, err := s.wafformatter.AddOrigin(ctx, v1.WebJson{
+			ApiType:     "tcp",
+			BackendList: origin,
+			Comment:     req.TcpForwardingData.Comment,
+		})
+		if err != nil {
+			return fmt.Errorf("添加源站失败 %s: %w", origin, err)
+		}
+		addedIds[origin] = id
+	}
+
+	// 关联新源站到网站
+	for _, originId := range addedIds {
+		if err := s.cdn.AddServerOrigin(ctx, int64(oldData.CdnWebId), originId); err != nil {
+			return fmt.Errorf("关联源站到网站失败: %w", err)
+		}
+	}
+
+	// 更新源站ID映射
+	maps.Copy(ipData.CdnOriginIds, addedIds)
+
+	// 删除不需要的源站
+	for backend, originId := range ipData.CdnOriginIds {
+		for _, delOrigin := range delOrigins {
+			if backend == delOrigin {
+				if err := s.cdn.DelServerOrigin(ctx, int64(oldData.CdnWebId), originId); err != nil {
+					return fmt.Errorf("删除源站失败: %w", err)
+				}
+				delete(ipData.CdnOriginIds, backend)
+				break
+			}
+		}
+	}
+
+	return nil
+}
+
+// ConfigureProxyProtocol 配置 TCP 代理的 Proxy Protocol 协议
+// 该函数根据请求参数决定是否开启 Proxy Protocol,用于传递客户端真实IP
+func (s *aidedTcpService) ConfigureProxyProtocol(ctx context.Context, req *v1.TcpForwardingRequest, tcpId int64) error {
+	// 如果不需要开启 Proxy Protocol,直接返回
+	if !req.TcpForwardingData.Proxy {
+		return nil
+	}
+
+	// 配置 Proxy Protocol v1
+	if err := s.proxy.EditProxy(ctx, tcpId, v1.ProxyProtocolJSON{
+		IsOn:    true,
+		Version: 1,
+	}); err != nil {
+		return fmt.Errorf("开启 Proxy Protocol 失败: %w", err)
+	}
+
+	return nil
+}
+
+// ProcessAsyncTasks 处理 TCP 转发相关的异步任务
+// 该函数主要处理新增后端服务器时的IP白名单添加任务,确保后端服务器能正常访问
+func (s *aidedTcpService) ProcessAsyncTasks(req *v1.TcpForwardingRequest) {
+	// 检查是否有后端服务器需要处理
+	if req == nil || len(req.TcpForwardingData.BackendList) == 0 {
+		return
+	}
+
+	// 提取后端 IP 地址
+	ips := s.ExtractIpsFromBackends(req.TcpForwardingData.BackendList)
+	if len(ips) > 0 {
+		// 异步添加到白名单
+		go s.wafformatter.PublishIpWhitelistTask(ips, "add", "", "white")
+	}
+}
+
+// ProcessIpWhitelistChanges 处理 TCP 转发编辑时的IP白名单变更
+// 该函数对比新旧后端列表,智能处理IP白名单:
+// 1. 将新增的后端 IP 加入白名单
+// 2. 将不再使用的后端 IP 从白名单移除
+func (s *aidedTcpService) ProcessIpWhitelistChanges(ctx context.Context, req *v1.TcpForwardingRequest, ipData *model.TcpForwardingRule) error {
+	addedIps, removedIps, err := s.wafformatter.WashEditWafIp(ctx, req.TcpForwardingData.BackendList, ipData.BackendList)
+	if err != nil {
+		return fmt.Errorf("处理IP变更失败: %w", err)
+	}
+
+	// 处理新增IP
+	if len(addedIps) > 0 {
+		go s.wafformatter.PublishIpWhitelistTask(addedIps, "add", "", "white")
+	}
+
+	// 处理移除IP
+	if len(removedIps) > 0 {
+		ipsToDelist, err := s.wafformatter.WashDelIps(ctx, removedIps)
+		if err != nil {
+			return fmt.Errorf("处理移除IP失败: %w", err)
+		}
+		if len(ipsToDelist) > 0 {
+			go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
+		}
+	}
+
+	return nil
+}
+
+// ProcessDeleteIpWhitelist 处理 TCP 转发删除时的IP白名单清理
+// 该函数在删除 TCP 转发配置时,智能清理不再需要的后端 IP 白名单条目
+func (s *aidedTcpService) ProcessDeleteIpWhitelist(ctx context.Context, id int) error {
+	ipData, err := s.tcpRepository.GetTcpForwardingIpsByID(ctx, id)
+	if err != nil {
+		return fmt.Errorf("获取IP数据失败: %w", err)
+	}
+
+	if ipData == nil || len(ipData.BackendList) == 0 {
+		return nil
+	}
+
+	ips, err := s.wafformatter.WashDeleteWafIp(ctx, ipData.BackendList)
+	if err != nil {
+		return fmt.Errorf("处理删除IP失败: %w", err)
+	}
+
+	if len(ips) > 0 {
+		ipsToDelist, err := s.wafformatter.WashDelIps(ctx, ips)
+		if err != nil {
+			return fmt.Errorf("清理IP列表失败: %w", err)
+		}
+		if len(ipsToDelist) > 0 {
+			go s.wafformatter.PublishIpWhitelistTask(ipsToDelist, "del", "0", "white")
+		}
+	}
+
+	return nil
+}
+
+// ExtractIpsFromBackends 从后端服务器地址列表中提取纯IP地址
+// 该函数解析 "IP:端口" 格式的后端地址,提取出纯IP地址用于白名单处理
+func (s *aidedTcpService) ExtractIpsFromBackends(backends []string) []string {
+	var ips []string
+	for _, backend := range backends {
+		// 跳过空字符串
+		if backend == "" {
+			continue
+		}
+		
+		// 解析 "IP:端口" 格式
+		if ip, _, err := net.SplitHostPort(backend); err == nil && ip != "" {
+			ips = append(ips, ip)
+		}
+	}
+	return ips
+}
+
+// PrepareWafData 准备WAF配置数据
+// 该函数的作用是根据请求参数准备创建CDN网站所需的所有配置数据,包括:
+// 1. 获取全局配置信息(用户、主机、网关等)
+// 2. 构建 TCP 代理的监听配置 JSON
+// 3. 组装 CDN 创建网站的表单数据
+func (s *aidedTcpService) PrepareWafData(ctx context.Context, req *v1.TcpForwardingRequest) (RequireResponse, v1.WebsiteSend, error) {
+	// 获取全局配置信息,包括用户信息、网关IP等
+	require, err := s.wafformatter.Require(ctx, v1.GlobalRequire{
+		HostId:  req.HostId,
+		Uid:     req.Uid,
+		Comment: req.TcpForwardingData.Comment,
+	})
+	if err != nil {
+		return RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("获取全局配置信息失败: %w", err)
+	}
+
+	// 验证实例配置是否完整
+	if require.Uid == 0 {
+		return RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("请先配置实例,确保用户信息和网关配置正确")
+	}
+
+	// 构建 TCP 监听配置
+	tcpConfig, err := s.BuildTcpListenConfig(require.GatewayIps, req.TcpForwardingData.Port)
+	if err != nil {
+		return RequireResponse{}, v1.WebsiteSend{}, fmt.Errorf("构建 TCP 监听配置失败: %w", err)
+	}
+
+	// 组装 CDN 创建网站的表单数据
+	formData := v1.WebsiteSend{
+		UserId:         int64(require.CdnUid),
+		Type:           "tcpProxy",
+		Name:           require.Tag,
+		Description:    req.TcpForwardingData.Comment,
+		TcpJSON:        tcpConfig,
+		ServerGroupIds: []int64{int64(require.GroupId)},
+		NodeClusterId:  2, // 默认节点集群ID
+	}
+
+	return require, formData, nil
+}
+
+// BuildTcpListenConfig 构建 TCP 监听配置 JSON
+// 该函数将网关IP列表和端口转换为 CDN 所需的 JSON 配置格式
+func (s *aidedTcpService) BuildTcpListenConfig(gatewayIps []string, portStr string) ([]byte, error) {
+	if len(gatewayIps) == 0 {
+		return nil, fmt.Errorf("网关IP列表不能为空")
+	}
+
+	// 验证端口字符串不为空
+	if portStr == "" {
+		return nil, fmt.Errorf("端口号不能为空")
+	}
+
+	// 转换端口字符串为整数并验证范围
+	port, err := strconv.Atoi(portStr)
+	if err != nil {
+		return nil, fmt.Errorf("端口号格式不合法: %s", portStr)
+	}
+
+	if port <= 0 || port > 65535 {
+		return nil, fmt.Errorf("端口号不合法: %d,必须在 1-65535 范围内", port)
+	}
+
+	// 构建监听配置
+	var listenConfigs []v1.Listen
+	for _, gatewayIp := range gatewayIps {
+		if gatewayIp == "" {
+			continue // 跳过空的IP
+		}
+		listenConfigs = append(listenConfigs, v1.Listen{
+			Protocol: "tcp",
+			Host:     gatewayIp,
+			Port:     portStr,
+		})
+	}
+
+	if len(listenConfigs) == 0 {
+		return nil, fmt.Errorf("没有有效的网关IP配置")
+	}
+
+	// 组装最终的 JSON 配置
+	tcpJSON := v1.TypeJSON{
+		IsOn:   true,
+		Listen: listenConfigs,
+	}
+
+	byteData, err := json.Marshal(tcpJSON)
+	if err != nil {
+		return nil, fmt.Errorf("序列化 TCP 配置失败: %w", err)
+	}
+
+	return byteData, nil
+}
+
+// BuildTcpForwardingModel 构建 TCP 转发主记录模型
+// 该函数将请求数据转换为数据库模型,包含主机、CDN网站、端口等信息
+func (s *aidedTcpService) BuildTcpForwardingModel(req *v1.TcpForwardingDataRequest, ruleId int, require RequireResponse) *model.Tcpforwarding {
+	return &model.Tcpforwarding{
+		HostId:   require.HostId,
+		CdnWebId: ruleId,
+		Port:     req.Port,
+		Comment:  req.Comment,
+		Proxy:    req.Proxy,
+	}
+}
+
+// BuildTcpRuleModel 构建 TCP 转发规则记录模型
+// 该函数构建包含后端服务器列表和 CDN 源站ID映射的规则记录
+func (s *aidedTcpService) BuildTcpRuleModel(reqData *v1.TcpForwardingDataRequest, require RequireResponse, localDbId int, cdnOriginIds map[string]int64) *model.TcpForwardingRule {
+	return &model.TcpForwardingRule{
+		Uid:          require.Uid,
+		HostId:       require.HostId,
+		TcpId:        localDbId,
+		CdnOriginIds: cdnOriginIds,
+		BackendList:  reqData.BackendList,
+	}
+}
+
+// SaveToDatabase 保存 TCP 转发配置到数据库
+// 该函数分别保存主记录(基本信息)和规则记录(后端服务器和源站ID映射)
+func (s *aidedTcpService) SaveToDatabase(ctx context.Context, req *v1.TcpForwardingRequest, require RequireResponse, tcpId int64, cdnOriginIds map[string]int64) (int, error) {
+	// 保存主记录
+	tcpModel := s.BuildTcpForwardingModel(&req.TcpForwardingData, int(tcpId), require)
+	id, err := s.tcpRepository.AddTcpforwarding(ctx, tcpModel)
+	if err != nil {
+		return 0, fmt.Errorf("保存TCP转发记录失败: %w", err)
+	}
+
+	// 保存规则记录
+	tcpRuleModel := s.BuildTcpRuleModel(&req.TcpForwardingData, require, id, cdnOriginIds)
+	if _, err := s.tcpRepository.AddTcpforwardingIps(ctx, *tcpRuleModel); err != nil {
+		return 0, fmt.Errorf("保存TCP转发规则失败: %w", err)
+	}
+
+	return id, nil
+}
+
+// UpdateDatabaseRecords 更新数据库记录
+// 该函数更新 TCP 转发的主记录和规则记录,同步最新的配置变更
+func (s *aidedTcpService) UpdateDatabaseRecords(ctx context.Context, req *v1.TcpForwardingRequest, oldData *model.Tcpforwarding, require RequireResponse, ipData *model.TcpForwardingRule) error {
+	// 更新主记录
+	tcpModel := s.BuildTcpForwardingModel(&req.TcpForwardingData, oldData.CdnWebId, require)
+	tcpModel.Id = req.TcpForwardingData.Id
+	if err := s.tcpRepository.EditTcpforwarding(ctx, tcpModel); err != nil {
+		return fmt.Errorf("更新TCP转发记录失败: %w", err)
+	}
+
+	// 更新规则记录
+	tcpRuleModel := s.BuildTcpRuleModel(&req.TcpForwardingData, require, req.TcpForwardingData.Id, ipData.CdnOriginIds)
+	if err := s.tcpRepository.EditTcpforwardingIps(ctx, *tcpRuleModel); err != nil {
+		return fmt.Errorf("更新TCP转发规则失败: %w", err)
+	}
+
+	return nil
+}
+
+// CleanupDatabaseRecords 清理数据库记录
+// 该函数删除 TCP 转发相关的所有数据库记录,包括主记录和规则记录
+func (s *aidedTcpService) CleanupDatabaseRecords(ctx context.Context, id int) error {
+	if err := s.tcpRepository.DeleteTcpforwarding(ctx, int64(id)); err != nil {
+		return fmt.Errorf("删除TCP转发主记录失败: %w", err)
+	}
+
+	if err := s.tcpRepository.DeleteTcpForwardingIpsById(ctx, id); err != nil {
+		return fmt.Errorf("删除TCP转发规则记录失败: %w", err)
+	}
+
+	return nil
+}
+