Преглед изворни кода

feat(service): 增加主机配置获取功能

- 新增 HostService 接口和 hostService 结构体
- 实现 GetHost 和 GetGameShieldConfig 方法
- 添加主机配置相关模型和仓库- 更新 FormatterService 以使用新的主机配置信息
fusu пре 2 месеци
родитељ
комит
acb9496c5c

+ 1 - 1
api/v1/GameShield.go

@@ -1,7 +1,7 @@
 package v1
 
 type GameShieldSubmitRequest struct {
-	HostId  string `json:"host_id" form:"host_id" binding:"required"`
+	HostId  int    `json:"host_id" form:"host_id" binding:"required"`
 	AppName string `json:"app_name" form:"app_name" binding:"required"`
 	RuleId  int    `json:"rule_id" form:"rule_id"`
 	Id      int    `json:"id" form:"id"`

+ 1 - 1
api/v1/gameShieldBackend.go

@@ -12,7 +12,7 @@ type GameShieldBackendRequest struct {
 	SdkPort         string    `json:"sdk_port" form:"sdk_port"`
 	SdkIp           string    `json:"sdk_ip" form:"sdk_ip"`
 	Type            string    `json:"type" form:"type"`
-	MaxBandwidth    string    `json:"max_bandwidth" form:"max_bandwidth"`
+	MaxBandwidth    int       `json:"max_bandwidth" form:"max_bandwidth"`
 	Checked         string    `json:"checked" form:"checked"`
 	CreatedAt       time.Time `json:"created_at" form:"created_at"`
 	UpdatedAt       time.Time `json:"updated_at" form:"updated_at"`

+ 24 - 0
api/v1/host.go

@@ -0,0 +1,24 @@
+package v1
+
+type HostConfigOption struct {
+	RelID    int
+	ConfigID int
+	OptionID int
+}
+
+type ProductConfigOption struct {
+	ID         int
+	OptionName string
+}
+
+type ProductConfigOptionSub struct {
+	ID         int
+	OptionName string
+}
+
+type GameShieldHostBackendConfigResponse struct {
+	RuleEntriesCount    int64
+	SourceMachinesCount int64
+	MaxBandwidthCount   int64
+	OnlineDevicesCount  int64
+}

+ 3 - 0
cmd/server/wire/wire.go

@@ -37,6 +37,7 @@ var repositorySet = wire.NewSet(
 	repository.NewUdpLimitRepository,
 	repository.NewGameShieldBackendRepository,
 	repository.NewGameShieldSdkIpRepository,
+	repository.NewHostRepository,
 )
 
 var serviceSet = wire.NewSet(
@@ -58,6 +59,7 @@ var serviceSet = wire.NewSet(
 	service.NewUdpLimitService,
 	service.NewGameShieldBackendService,
 	service.NewGameShieldSdkIpService,
+	service.NewHostService,
 )
 
 var handlerSet = wire.NewSet(
@@ -74,6 +76,7 @@ var handlerSet = wire.NewSet(
 	handler.NewUdpLimitHandler,
 	handler.NewGameShieldBackendHandler,
 	handler.NewGameShieldSdkIpHandler,
+	handler.NewHostHandler,
 )
 
 var jobSet = wire.NewSet(

+ 7 - 5
cmd/server/wire/wire_gen.go

@@ -45,13 +45,15 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	gameShieldUserIpRepository := repository.NewGameShieldUserIpRepository(repositoryRepository)
 	gameShieldPublicIpService := service.NewGameShieldPublicIpService(serviceService, gameShieldPublicIpRepository, gameShieldUserIpRepository)
 	duedateService := service.NewDuedateService(serviceService, gameShieldRepository)
-	formatterService := service.NewFormatterService(serviceService, gameShieldPublicIpService)
+	gameShieldBackendRepository := repository.NewGameShieldBackendRepository(repositoryRepository)
+	hostRepository := repository.NewHostRepository(repositoryRepository)
+	hostService := service.NewHostService(serviceService, hostRepository)
+	formatterService := service.NewFormatterService(serviceService, gameShieldPublicIpService, gameShieldBackendRepository, hostService)
 	requiredService := service.NewRequiredService(serviceService, crawlerService, viperViper)
 	gameShieldSdkIpRepository := repository.NewGameShieldSdkIpRepository(repositoryRepository)
 	gameShieldSdkIpService := service.NewGameShieldSdkIpService(serviceService, gameShieldSdkIpRepository)
 	gameShieldService := service.NewGameShieldService(serviceService, gameShieldRepository, crawlerService, gameShieldPublicIpService, duedateService, formatterService, parserService, requiredService, viperViper, gameShieldSdkIpService)
 	gameShieldHandler := handler.NewGameShieldHandler(handlerHandler, gameShieldService, crawlerService)
-	gameShieldBackendRepository := repository.NewGameShieldBackendRepository(repositoryRepository)
 	gameShieldBackendService := service.NewGameShieldBackendService(serviceService, gameShieldBackendRepository, gameShieldRepository, crawlerService, gameShieldPublicIpService, duedateService, formatterService, parserService, requiredService, viperViper, gameShieldService)
 	gameShieldBackendHandler := handler.NewGameShieldBackendHandler(handlerHandler, gameShieldBackendService)
 	webForwardingRepository := repository.NewWebForwardingRepository(repositoryRepository)
@@ -83,11 +85,11 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 
 // wire.go:
 
-var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewTransaction, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository)
+var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewTransaction, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository)
 
-var serviceSet = wire.NewSet(service.NewService, service.NewUserService, service.NewGameShieldService, service.NewCrawlerService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewWebForwardingService, service.NewTcpforwardingService, service.NewUdpForWardingService, service.NewGameShieldUserIpService, service.NewWebLimitService, service.NewTcpLimitService, service.NewUdpLimitService, service.NewGameShieldBackendService, service.NewGameShieldSdkIpService)
+var serviceSet = wire.NewSet(service.NewService, service.NewUserService, service.NewGameShieldService, service.NewCrawlerService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewWebForwardingService, service.NewTcpforwardingService, service.NewUdpForWardingService, service.NewGameShieldUserIpService, service.NewWebLimitService, service.NewTcpLimitService, service.NewUdpLimitService, service.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService)
 
-var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewGameShieldHandler, handler.NewGameShieldPublicIpHandler, handler.NewWebForwardingHandler, handler.NewTcpforwardingHandler, handler.NewUdpForWardingHandler, handler.NewGameShieldUserIpHandler, handler.NewWebLimitHandler, handler.NewTcpLimitHandler, handler.NewUdpLimitHandler, handler.NewGameShieldBackendHandler, handler.NewGameShieldSdkIpHandler)
+var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewGameShieldHandler, handler.NewGameShieldPublicIpHandler, handler.NewWebForwardingHandler, handler.NewTcpforwardingHandler, handler.NewUdpForWardingHandler, handler.NewGameShieldUserIpHandler, handler.NewWebLimitHandler, handler.NewTcpLimitHandler, handler.NewUdpLimitHandler, handler.NewGameShieldBackendHandler, handler.NewGameShieldSdkIpHandler, handler.NewHostHandler)
 
 var jobSet = wire.NewSet(job.NewJob, job.NewUserJob)
 

+ 25 - 0
internal/handler/host.go

@@ -0,0 +1,25 @@
+package handler
+
+import (
+	"github.com/gin-gonic/gin"
+	"github.com/go-nunu/nunu-layout-advanced/internal/service"
+)
+
+type HostHandler struct {
+	*Handler
+	hostService service.HostService
+}
+
+func NewHostHandler(
+	handler *Handler,
+	hostService service.HostService,
+) *HostHandler {
+	return &HostHandler{
+		Handler:     handler,
+		hostService: hostService,
+	}
+}
+
+func (h *HostHandler) GetHost(ctx *gin.Context) {
+
+}

+ 1 - 1
internal/model/gameshield.go

@@ -10,7 +10,7 @@ type GameShield struct {
 	Key            string
 	AddTime        time.Time
 	Uid            int
-	HostId         string
+	HostId         int
 	Checked        int
 	DunName        string
 	ExpireTime     int64 `gorm:"type:bigint"`

+ 1 - 1
internal/model/gameshieldbackend.go

@@ -13,7 +13,7 @@ type GameShieldBackend struct {
 	SdkPort         string
 	SdkIp           string `gorm:"null"`
 	Type            string
-	MaxBandwidth    string
+	MaxBandwidth    int `gorm:"not null;type:tinyint"`
 	CreatedAt       time.Time
 	UpdatedAt       time.Time
 }

+ 13 - 0
internal/model/host.go

@@ -0,0 +1,13 @@
+package model
+
+type Host struct {
+	Id       int `gorm:"primarykey"`
+	Relid    int
+	Configid int
+	Optionid int
+	Qty      int
+}
+
+func (m *Host) TableName() string {
+	return "shd_host_config_options"
+}

+ 4 - 4
internal/repository/gameshield.go

@@ -13,7 +13,7 @@ type GameShieldRepository interface {
 	UpdateGameShield(ctx context.Context, gameShield *model.GameShield) error
 	DeleteGameShield(ctx context.Context, ruleId int) error
 	GetGameShieldIsBuy(ctx context.Context, uid int64) (int64, error)
-	GetGameShieldNextduedate(ctx context.Context, uid int64, productID string) (string, error)
+	GetGameShieldNextduedate(ctx context.Context, uid int64, productID int) (string, error)
 	GetGameShieldNameByDunName(ctx context.Context, appName string) (string, error)
 	GetGameShieldHostIdByDunName(ctx context.Context, hostId string) (string, error)
 	GetGameShieldRuleIdByAppName(ctx context.Context, appName string) (int, error)
@@ -22,7 +22,7 @@ type GameShieldRepository interface {
 	GetHostById(ctx context.Context, id int) (string, error)
 	GetSoonExpiredGameShields(ctx context.Context, daysThreshold int) ([]*model.GameShield, error)
 	GetExpiredGameShields(ctx context.Context) ([]*model.GameShield, error)
-	SyncExpireTimeFromHostNextDueDate(ctx context.Context, uid int, hostId string) error
+	SyncExpireTimeFromHostNextDueDate(ctx context.Context, uid int, hostId int) error
 	GetAllGameShield(ctx context.Context) ([]*model.GameShield, error)
 }
 
@@ -87,7 +87,7 @@ func (r *gameShieldRepository) GetGameShieldIsBuy(ctx context.Context, uid int64
 	return count, nil
 }
 
-func (r *gameShieldRepository) GetGameShieldNextduedate(ctx context.Context, uid int64, productID string) (string, error) {
+func (r *gameShieldRepository) GetGameShieldNextduedate(ctx context.Context, uid int64, productID int) (string, error) {
 	var nextDueDate string
 	err := r.DB(ctx).Table("shd_host").
 		Select("nextduedate").
@@ -195,7 +195,7 @@ func (r *gameShieldRepository) GetExpiredGameShields(ctx context.Context) ([]*mo
 }
 
 // SyncExpireTimeFromHostNextDueDate 同步host表的nextduedate到game_shield表的ExpireTime
-func (r *gameShieldRepository) SyncExpireTimeFromHostNextDueDate(ctx context.Context, uid int, hostId string) error {
+func (r *gameShieldRepository) SyncExpireTimeFromHostNextDueDate(ctx context.Context, uid int, hostId int) error {
 	var nextduedate int64
 
 	// 先从shd_host表获取nextduedate

+ 24 - 0
internal/repository/gameshieldbackend.go

@@ -12,6 +12,8 @@ type GameShieldBackendRepository interface {
 	EditGameShieldBackend(ctx context.Context, req *v1.GameShieldBackendRequest) error
 	DeleteGameShieldBackend(ctx context.Context, id int64) error
 	GetGameShieldBackendByHostId(ctx context.Context, hostId int) ([]model.GameShieldBackend, error)
+	GetGameShieldBackendConfigCountByHostId(ctx context.Context, hostId int) (*v1.GameShieldHostBackendConfigResponse, error)
+	GetGameShieldBackendSourceMachineIpByHostId(ctx context.Context, hostId int) ([]string, error)
 }
 
 func NewGameShieldBackendRepository(
@@ -62,3 +64,25 @@ func (r *gameShieldBackendRepository) GetGameShieldBackendByHostId(ctx context.C
 	}
 	return gameShieldBackend, nil
 }
+
+func (r *gameShieldBackendRepository) GetGameShieldBackendConfigCountByHostId(ctx context.Context, hostId int) (*v1.GameShieldHostBackendConfigResponse, error) {
+	var res v1.GameShieldHostBackendConfigResponse
+	if err := r.DB(ctx).Model(&model.GameShieldBackend{}).Where("host_id = ?", hostId).Count(&res.RuleEntriesCount).Error; err != nil {
+		return nil, err
+	}
+	if err := r.DB(ctx).Model(&model.GameShieldBackend{}).Where("host_id = ?", hostId).Distinct("source_machine_ip").Count(&res.SourceMachinesCount).Error; err != nil {
+		return nil, err
+	}
+	if err := r.DB(ctx).Model(&model.GameShieldBackend{}).Where("host_id = ?", hostId).Where("max_bandwidth = ?", 1).Count(&res.MaxBandwidthCount).Error; err != nil {
+		return nil, err
+	}
+	return &res, nil
+}
+
+func (r *gameShieldBackendRepository) GetGameShieldBackendSourceMachineIpByHostId(ctx context.Context, hostId int) ([]string, error) {
+	var res []string
+	if err := r.DB(ctx).Model(&model.GameShieldBackend{}).Where("host_id = ?", hostId).Distinct("source_machine_ip").Pluck("source_machine_ip", &res).Error; err != nil {
+		return nil, err
+	}
+	return res, nil
+}

+ 56 - 0
internal/repository/host.go

@@ -0,0 +1,56 @@
+package repository
+
+import (
+	"context"
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/model"
+)
+
+type HostRepository interface {
+	GetHost(ctx context.Context, id int64) (*model.Host, error)
+	GetHostConfig(ctx context.Context, hostId int) ([]*model.Host, error)
+	GetProductConfigOption(ctx context.Context, id []int) ([]v1.ProductConfigOption, error)
+	GetProductConfigOptionSub(ctx context.Context, id []int) ([]v1.ProductConfigOptionSub, error)
+}
+
+func NewHostRepository(
+	repository *Repository,
+) HostRepository {
+	return &hostRepository{
+		Repository: repository,
+	}
+}
+
+type hostRepository struct {
+	*Repository
+}
+
+func (r *hostRepository) GetHost(ctx context.Context, id int64) (*model.Host, error) {
+	var host model.Host
+
+	return &host, nil
+}
+
+func (r *hostRepository) GetHostConfig(ctx context.Context, hostId int) ([]*model.Host, error) {
+	var res []*model.Host
+	if err := r.DB(ctx).Where("relid = ?", hostId).Find(&res).Error; err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
+func (r *hostRepository) GetProductConfigOption(ctx context.Context, id []int) ([]v1.ProductConfigOption, error) {
+	var res []v1.ProductConfigOption
+	if err := r.DB(ctx).Table("shd_product_config_options").Where("id IN ?", id).Find(&res).Error; err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
+func (r *hostRepository) GetProductConfigOptionSub(ctx context.Context, id []int) ([]v1.ProductConfigOptionSub, error) {
+	var res []v1.ProductConfigOptionSub
+	if err := r.DB(ctx).Table("shd_product_config_options_sub").Where("id IN ?", id).Find(&res).Error; err != nil {
+		return nil, err
+	}
+	return res, nil
+}

+ 2 - 2
internal/service/duedate.go

@@ -9,7 +9,7 @@ import (
 )
 
 type DuedateService interface {
-	NextDueDate(ctx context.Context, uid int, hostId string) (string, error)
+	NextDueDate(ctx context.Context, uid int, hostId int) (string, error)
 }
 
 func NewDuedateService(
@@ -28,7 +28,7 @@ type duedateService struct {
 	gameShieldRepository repository.GameShieldRepository
 }
 
-func (service *duedateService) NextDueDate(ctx context.Context, uid int, productID string) (string, error) {
+func (service *duedateService) NextDueDate(ctx context.Context, uid int, productID int) (string, error) {
 	timeStr, err := service.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(uid), productID)
 	if timeStr == "0" || timeStr == "" {
 		return "", err

+ 83 - 33
internal/service/formatter.go

@@ -6,7 +6,9 @@ import (
 	"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"
@@ -25,17 +27,23 @@ type FormatterService interface {
 func NewFormatterService(
 	service *Service,
 	gameShieldPublicIpService GameShieldPublicIpService,
+	gameShieldBackendRepository repository.GameShieldBackendRepository,
+	hostService HostService,
 
 ) FormatterService {
 	return &formatterService{
-		Service:                   service,
-		gameShieldPublicIpService: gameShieldPublicIpService,
+		Service:                     service,
+		gameShieldPublicIpService:   gameShieldPublicIpService,
+		gameShieldBackendRepository: gameShieldBackendRepository,
+		hostService:                 hostService,
 	}
 }
 
 type formatterService struct {
 	*Service
-	gameShieldPublicIpService GameShieldPublicIpService
+	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) {
@@ -117,84 +125,126 @@ func (service *formatterService) OldFormat(ctx context.Context, req *[]model.Gam
 }
 
 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 {
-		// 提取必要字段
-		sourceIP := item.SourceMachineIP // 假设结构体中有这个字段
+		// 检查并验证源机器IP
+		sourceIP := item.SourceMachineIP
 		if sourceIP == "" {
-			return nil, fmt.Errorf("没有有效源IP的配置") // 跳过没有有效源IP的配置
+			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 // 假设结构体中有这个字段
+		// 验证协议
+		protocol := item.Protocol
 		if protocol == "" {
-			return nil, fmt.Errorf("没有有效协议的配置") // 跳过没有有效协议的配置
+			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)
 
-			// 使用数组中的具体端口
-			addr := fmt.Sprintf("%s:%d", sourceIP, conPorts[i])
-
+			// 构建基本的后端配置项
 			itemMap := v1.SendGameShieldBackend{
-				Addr:     []string{addr},
+				Addr:     []string{fmt.Sprintf("%s:%d", sourceIP, conPorts[i])},
 				Protocol: protocol,
+				Type:     item.Type,
 			}
 
-			//// 设置主机名(如果存在)
-			//if item.Host != "" {
-			//	itemMap["host"] = item.Host
-			//}
-
-			// 根据协议设置不同属性
+			// 根据协议类型设置属性
 			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"
 			}
-			itemMap.Type = item.Type
-			if item.Type != "pc" {
-				itemMap.SdkIp = ""
-			} else {
+
+			// 根据设备类型设置SDK IP
+			if item.Type == "pc" {
 				itemMap.SdkIp = item.SdkIp
+			} else {
+				itemMap.SdkIp = ""
 			}
 
-			if item.MaxBandwidth == "1" {
+			// 处理最大带宽设置
+			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 {
-				if sdkPorts[i] <= 1024 {
-					if item.Type == "mobile" {
-						return nil, fmt.Errorf("移动端不支持SSH端口")
-					}
+
+			// 设置SDK端口(如果存在)
+			if len(sdkPorts) > 0 {
+				sdkPort := sdkPorts[i]
+				// 检查移动端的SSH端口限制
+				if sdkPort <= 1024 && item.Type == "mobile" {
+					return nil, fmt.Errorf("移动端不支持SSH端口")
 				}
-				itemMap.SdkPort = sdkPorts[i]
+				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 {

+ 4 - 9
internal/service/gameshield.go

@@ -16,7 +16,7 @@ type GameShieldService interface {
 	EditGameShield(ctx context.Context, req *v1.GameShieldSubmitRequest) (string, error)
 	DeleteGameShield(ctx context.Context, req int) (string, error)
 	GetGameShieldKey(ctx context.Context, id int) (string, error)
-	GetKeyAndEditGameShield(ctx context.Context, hostId string, dunName string) (string, error)
+	GetKeyAndEditGameShield(ctx context.Context, hostId int, dunName string) (string, error)
 }
 
 func NewGameShieldService(
@@ -68,17 +68,12 @@ func (service *gameShieldService) SubmitGameShield(ctx context.Context, req *v1.
 		return "", fmt.Errorf("应用名称已存在")
 	}
 
-	hostIdInt, err := strconv.Atoi(req.HostId)
-	if err != nil {
-		return "", fmt.Errorf("转换hostId失败: %v", err)
-	}
-
 	// 调用IP生成服务
-	if err := service.gameShieldSdkIp.GenerateMultipleLoopbackIps(ctx, req.Uid, hostIdInt); err != nil {
+	if err := service.gameShieldSdkIp.GenerateMultipleLoopbackIps(ctx, req.Uid, req.HostId); err != nil {
 		return "", fmt.Errorf("生成IP地址失败: %w", err)
 	}
 
-	dunName := strconv.Itoa(req.Uid) + "_hostId" + req.HostId
+	dunName := strconv.Itoa(req.Uid) + "_hostId" + strconv.Itoa(req.HostId)
 	formData := map[string]interface{}{
 		"app_name":         dunName,
 		"gateway_group_id": 4,
@@ -158,7 +153,7 @@ func (service *gameShieldService) GetGameShieldKey(ctx context.Context, hostId i
 	return res, nil
 }
 
-func (service *gameShieldService) GetKeyAndEditGameShield(ctx context.Context, hostId string, dunName string) (string, error) {
+func (service *gameShieldService) GetKeyAndEditGameShield(ctx context.Context, hostId int, dunName string) (string, error) {
 	key, err := service.crawlerService.GetKey(ctx, dunName)
 	if err != nil {
 		return "", err

+ 3 - 3
internal/service/gameshieldbackend.go

@@ -69,7 +69,7 @@ func (s *gameShieldBackendService) GetGameShieldRequired(ctx context.Context, re
 	if req.Uid == 0 {
 		return nil, 0, fmt.Errorf("uid is required")
 	}
-	res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, strconv.Itoa(req.HostId))
+	res.ExpiredAt, err = s.duedate.NextDueDate(ctx, req.Uid, req.HostId)
 	if err != nil {
 		return nil, 0, err
 	}
@@ -143,7 +143,7 @@ func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v
 	if err != nil {
 		return "", 0, err
 	}
-	timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), strconv.Itoa(req.HostId))
+	timeBase, err := s.gameShieldRepository.GetGameShieldNextduedate(ctx, int64(req.Uid), req.HostId)
 	if err != nil {
 		return "", 0, err
 	}
@@ -151,7 +151,7 @@ func (s *gameShieldBackendService) GameShieldBackend(ctx context.Context, req *v
 	if err != nil {
 		return "", 0, err
 	}
-	if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: strconv.Itoa(req.HostId), Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
+	if err := s.gameShieldRepository.UpdateGameShieldByHostId(ctx, &model.GameShield{HostId: req.HostId, Key: KeyAndField.Key, ExpireTime: timestampSec}); err != nil {
 		return "", 0, err
 	}
 	return res, count, nil

+ 138 - 0
internal/service/host.go

@@ -0,0 +1,138 @@
+package service
+
+import (
+	"context"
+	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"
+	"github.com/spf13/cast"
+	"strings"
+)
+
+type HostService interface {
+	GetHost(ctx context.Context, id int64) (*model.Host, error)
+	GetGameShieldConfig(ctx context.Context, hostId int) (v1.GameShieldHostBackendConfigResponse, error)
+}
+
+func NewHostService(
+	service *Service,
+	hostRepository repository.HostRepository,
+) HostService {
+	return &hostService{
+		Service:        service,
+		hostRepository: hostRepository,
+	}
+}
+
+const (
+	ConfigOnlineDevices  = "在线设备数"
+	ConfigRuleEntries    = "规则条目"
+	ConfigMaxBandwidth   = "高带宽转发规则条目"
+	ConfigSourceMachines = "支持源机"
+)
+
+type hostService struct {
+	*Service
+	hostRepository repository.HostRepository
+}
+
+func (s *hostService) GetHost(ctx context.Context, id int64) (*model.Host, error) {
+	return s.hostRepository.GetHost(ctx, id)
+}
+
+func (s *hostService) GetHostConfig(ctx context.Context, hostId int) ([]map[string]string, error) {
+	configOptions, err := s.hostRepository.GetHostConfig(ctx, hostId)
+	if err != nil {
+		return nil, err
+	}
+	// 2. 收集ID和建立映射关系
+	var configIDs []int
+	var optionIDs []int
+	optionMap := make(map[int]int)
+	for _, item := range configOptions {
+		configIDs = append(configIDs, item.Configid)
+		optionIDs = append(optionIDs, item.Optionid)
+		optionMap[item.Configid] = item.Optionid
+	}
+	// 3. 获取配置和选项数据
+	var configs []v1.ProductConfigOption
+	if len(configIDs) > 0 {
+		configs, err = s.hostRepository.GetProductConfigOption(ctx, configIDs)
+		if err != nil {
+			return nil, err
+		}
+	}
+	var options []v1.ProductConfigOptionSub
+	if len(optionIDs) > 0 {
+		options, err = s.hostRepository.GetProductConfigOptionSub(ctx, optionIDs)
+		if err != nil {
+			return nil, err
+		}
+	}
+	// 4. 转换选项为关联数组
+	optionsByID := make(map[int]v1.ProductConfigOptionSub)
+	for _, option := range options {
+		optionsByID[option.ID] = option
+	}
+
+	// 5. 构建结果数据
+	var data []map[string]string
+	for _, config := range configs {
+		optionID := optionMap[config.ID]
+
+		var optionName string
+		if opt, ok := optionsByID[optionID]; ok {
+			optionName = opt.OptionName
+		}
+
+		data = append(data, map[string]string{
+			"config_name": config.OptionName,
+			"option_name": optionName,
+		})
+	}
+	return data, nil
+}
+
+func (s *hostService) tidyGetGameShieldConfig(ctx context.Context, configName string, optionName string) (map[string]int, error) {
+	// 根据配置名称去除相应的单位后缀
+	switch configName {
+	case ConfigOnlineDevices, ConfigRuleEntries:
+		optionName = strings.TrimSuffix(optionName, "个")
+	case ConfigMaxBandwidth:
+		optionName = strings.TrimSuffix(optionName, "条")
+	}
+
+	// 转换为整数并返回
+	return map[string]int{configName: cast.ToInt(optionName)}, nil
+}
+func (s *hostService) GetGameShieldConfig(ctx context.Context, hostId int) (v1.GameShieldHostBackendConfigResponse, error) {
+	baseData, err := s.GetHostConfig(ctx, hostId)
+	if err != nil {
+		return v1.GameShieldHostBackendConfigResponse{}, err
+	}
+
+	var data v1.GameShieldHostBackendConfigResponse
+
+	for _, item := range baseData {
+		configName := item["config_name"]
+		optionName := item["option_name"]
+
+		res, err := s.tidyGetGameShieldConfig(ctx, configName, optionName)
+		if err != nil {
+			return v1.GameShieldHostBackendConfigResponse{}, err
+		}
+
+		// 使用switch直接设置对应字段
+		switch configName {
+		case ConfigOnlineDevices:
+			data.OnlineDevicesCount = int64(res[configName])
+		case ConfigRuleEntries:
+			data.RuleEntriesCount = int64(res[configName])
+		case ConfigMaxBandwidth:
+			data.MaxBandwidthCount = int64(res[configName])
+		case ConfigSourceMachines:
+			data.SourceMachinesCount = int64(res[configName])
+		}
+	}
+	return data, nil
+}