package service import ( "context" "encoding/json" "fmt" 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" "maps" "slices" "sort" "strconv" "strings" "github.com/spf13/cast" ) type FormatterService interface { FormatBackendData(ctx context.Context, req *v1.GameShieldBackendArrayRequest, output map[string]v1.SendGameShieldBackend, keyCounter int) (string, error) FormatPort(ctx context.Context, req interface{}) []int OldFormat(ctx context.Context, req *[]model.GameShieldBackend) (map[string]v1.SendGameShieldBackend, error) TidyFormatBackendData(ctx context.Context, req *v1.GameShieldBackendArrayRequest, keyCounter int) (map[string]v1.SendGameShieldBackend, error) Sort(ctx context.Context, mapData map[string]v1.SendGameShieldBackend) (map[string]v1.SendGameShieldBackend, error) } func NewFormatterService( service *Service, gameShieldPublicIpService GameShieldPublicIpService, gameShieldBackendRepository repository.GameShieldBackendRepository, hostService HostService, ) FormatterService { return &formatterService{ Service: service, gameShieldPublicIpService: gameShieldPublicIpService, gameShieldBackendRepository: gameShieldBackendRepository, hostService: hostService, } } type formatterService struct { *Service gameShieldPublicIpService GameShieldPublicIpService gameShieldBackendRepository repository.GameShieldBackendRepository hostService HostService } func (service *formatterService) FormatBackendData(ctx context.Context, req *v1.GameShieldBackendArrayRequest, oldFormat map[string]v1.SendGameShieldBackend, keyCounter int) (string, error) { formData, err := service.TidyFormatBackendData(ctx, req, keyCounter) for _, v := range formData { v.Type = "" } if err != nil { return "", err } maps.Copy(formData, oldFormat) sortedOutput, err := service.Sort(ctx, formData) if err != nil { return "", err } jsonBytes, err := json.MarshalIndent(sortedOutput, "", " ") if err != nil { return "", err } return string(jsonBytes), nil } func (service *formatterService) FormatPort(ctx context.Context, req interface{}) []int { if req == nil { return []int{} } reqStr := cast.ToString(req) if reqStr == "" { return []int{} } reqStr = strings.ReplaceAll(reqStr, ",", ",") // 分割字符串并转换为整数 var res []int for _, v := range strings.Split(reqStr, ",") { // 去除空格 v = strings.TrimSpace(v) if v != "" { port := cast.ToInt(v) res = append(res, port) } } return res } func (service *formatterService) OldFormat(ctx context.Context, req *[]model.GameShieldBackend) (map[string]v1.SendGameShieldBackend, error) { res := make(map[string]v1.SendGameShieldBackend) var UdpSessionTimeout string for _, v := range *req { addr := fmt.Sprintf("%s:%s", v.SourceMachineIP, v.ConnectPort) sdkPort, err := strconv.Atoi(v.SdkPort) if err != nil { return nil, err } if v.Protocol == "udp" { UdpSessionTimeout = "300s" } else { UdpSessionTimeout = "" } keyName := fmt.Sprintf("key%d", v.KeySort) if v.Type != "pc" { v.SdkIp = "" } res[keyName] = v1.SendGameShieldBackend{ Addr: []string{addr}, Protocol: v.Protocol, ProxyAddr: v.ProxyAddr, SdkPort: sdkPort, UdpSessionTimeout: UdpSessionTimeout, SdkIp: v.SdkIp, } } return res, nil } func (service *formatterService) TidyFormatBackendData(ctx context.Context, req *v1.GameShieldBackendArrayRequest, keyCounter int) (map[string]v1.SendGameShieldBackend, error) { // 初始化输出映射 output := make(map[string]v1.SendGameShieldBackend) // 获取所需基础数据 userIp, err := service.gameShieldPublicIpService.GetUserIp(ctx, req.Uid) if err != nil { return nil, err } oldCount, err := service.gameShieldBackendRepository.GetGameShieldBackendConfigCountByHostId(ctx, req.HostId) if err != nil { return nil, err } oldMachineIp, err := service.gameShieldBackendRepository.GetGameShieldBackendSourceMachineIpByHostId(ctx, req.HostId) if err != nil { return nil, err } configCount, err := service.hostService.GetGameShieldConfig(ctx, req.HostId) if err != nil { return nil, err } // 遍历请求中的所有项目 for _, item := range req.Items { // 检查并验证源机器IP sourceIP := item.SourceMachineIP if sourceIP == "" { return nil, fmt.Errorf("没有有效源IP的配置") } // 检查源机器IP是否为新增 if !slices.Contains(oldMachineIp, sourceIP) { oldCount.SourceMachinesCount++ if oldCount.SourceMachinesCount > configCount.SourceMachinesCount { return nil, fmt.Errorf("超出最大源机数量") } } // 验证协议 protocol := item.Protocol if protocol == "" { return nil, fmt.Errorf("没有有效协议的配置") } // 获取并验证端口配置 conPorts := service.FormatPort(ctx, item.ConnectPort) sdkPorts := service.FormatPort(ctx, item.SdkPort) // 验证端口数量匹配 if len(sdkPorts) > 0 && len(conPorts) != len(sdkPorts) { return nil, fmt.Errorf("端口数量不匹配") } // 验证规则条目数量 oldCount.RuleEntriesCount += int64(len(conPorts)) if oldCount.RuleEntriesCount > configCount.RuleEntriesCount { return nil, fmt.Errorf("超出最大规则数量") } // 处理每一对端口 for i := 0; i < len(conPorts); i++ { keyCounter++ key := fmt.Sprintf("key%d", keyCounter) // 构建基本的后端配置项 itemMap := v1.SendGameShieldBackend{ Addr: []string{fmt.Sprintf("%s:%d", sourceIP, conPorts[i])}, Protocol: protocol, Type: item.Type, } // 根据协议类型设置属性 if protocol != "udp" { // 非UDP协议的配置 if item.Checked == "agent" { itemMap.AgentAddr = fmt.Sprintf("%s:%s", sourceIP, "23350") } itemMap.ProxyAddr = userIp + ":32353" } else { // UDP协议的配置 itemMap.ProxyAddr = "" itemMap.UdpSessionTimeout = "300s" } // 根据设备类型设置SDK IP if item.Type == "pc" { itemMap.SdkIp = item.SdkIp } else { itemMap.SdkIp = "" } // 处理最大带宽设置 if item.MaxBandwidth == 1 { oldCount.MaxBandwidthCount++ if oldCount.MaxBandwidthCount > configCount.MaxBandwidthCount { return nil, fmt.Errorf("超出最大带宽数量") } itemMap.MaxBandwidth = "50m" } else { itemMap.MaxBandwidth = "" } // 设置SDK端口(如果存在) if len(sdkPorts) > 0 { sdkPort := sdkPorts[i] // 检查移动端的SSH端口限制 if sdkPort <= 1024 && item.Type == "mobile" { return nil, fmt.Errorf("移动端不支持SSH端口") } itemMap.SdkPort = sdkPort } // 将配置项添加到输出映射 output[key] = itemMap } } return output, nil } func (service *formatterService) Sort(ctx context.Context, mapData map[string]v1.SendGameShieldBackend) (map[string]v1.SendGameShieldBackend, error) { var keys []int for key := range mapData { intKey, err := strconv.Atoi(strings.TrimPrefix(key, "key")) if err != nil { return nil, err } keys = append(keys, intKey) } // 2. 排序键 sort.Ints(keys) // 3. 创建一个新的 output 切片或 map 来存储排序后的值 sortedOutput := make(map[string]v1.SendGameShieldBackend) // 4. 按排序后的键遍历 map,并存储对应的值到 sortedOutput for _, key := range keys { sortedOutput["key"+strconv.Itoa(key)] = mapData["key"+strconv.Itoa(key)] } return sortedOutput, nil }