From 32e56b1a0f51457b9a89e69d57aff73f8604bd04 Mon Sep 17 00:00:00 2001 From: luorijun Date: Sat, 23 May 2026 13:50:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8F=90=E5=8F=96=E5=87=BD?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E5=AE=9E=E7=8E=B0=E9=80=9A=E8=BF=87=E5=A5=97?= =?UTF-8?q?=E9=A4=90=E7=BC=96=E5=8F=B7=E6=8F=90=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/handlers/channel.go | 71 +++++++++++++++++++++++++++++++++- web/routes.go | 1 + web/services/channel.go | 29 +++++++++----- web/services/channel_baiyin.go | 14 +++---- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/web/handlers/channel.go b/web/handlers/channel.go index 5f5c4e3..dc06b35 100644 --- a/web/handlers/channel.go +++ b/web/handlers/channel.go @@ -102,13 +102,17 @@ func CreateChannel(c *fiber.Ctx) error { } // 创建通道 + no, err := s.FindResourceNoById(req.ResourceId) + if err != nil { + return err + } + var isp *m.EdgeISP if req.Isp != nil { isp = u.X(m.ToEdgeISP(*req.Isp)) } result, err := s.Channel.CreateChannels( - ip, - req.ResourceId, + ip, no, req.AuthType == s.ChannelAuthTypeIp, req.AuthType == s.ChannelAuthTypePass, req.Count, @@ -149,6 +153,69 @@ type CreateChannelReq struct { Isp *int `json:"isp"` } +// CreateChannelV2 创建新通道 v2,使用 resource_no 替代 resource_id +func CreateChannelV2(c *fiber.Ctx) error { + // 不检查权限,允许 api 调用 + + // 解析参数 + req := new(CreateChannelReqV2) + if err := g.Validator.ParseBody(c, req); err != nil { + return core.NewBizErr("解析参数失败", err) + } + + ip, err := netip.ParseAddr(c.IP()) + if err != nil { + return core.NewBizErr("获取客户端地址失败", err) + } + + // 创建通道 + var isp *m.EdgeISP + if req.Isp != nil { + isp = u.X(m.ToEdgeISP(*req.Isp)) + } + result, err := s.Channel.CreateChannels( + ip, + req.ResourceNo, + req.AuthType == s.ChannelAuthTypeIp, + req.AuthType == s.ChannelAuthTypePass, + req.Count, + &s.EdgeFilter{ + Isp: isp, + Prov: req.Prov, + City: req.City, + }, + ) + if err != nil { + return err + } + + // 返回结果 + var resp = make([]*CreateChannelRespItem, len(result)) + for i, channel := range result { + resp[i] = &CreateChannelRespItem{ + Proto: req.Protocol, + Host: channel.Host, + IP: channel.Proxy.IP.String(), + Port: channel.Port, + } + if req.AuthType == s.ChannelAuthTypePass { + resp[i].Username = channel.Username + resp[i].Password = channel.Password + } + } + return c.JSON(resp) +} + +type CreateChannelReqV2 struct { + ResourceNo string `json:"resource_no" validate:"required"` + AuthType s.ChannelAuthType `json:"auth_type" validate:"required"` + Protocol int `json:"protocol" validate:"required"` + Count int `json:"count" validate:"required"` + Prov *string `json:"prov"` + City *string `json:"city"` + Isp *int `json:"isp"` +} + type CreateChannelRespItem struct { Proto int `json:"-"` Host string `json:"host"` diff --git a/web/routes.go b/web/routes.go index 2142ac7..c0daf9b 100644 --- a/web/routes.go +++ b/web/routes.go @@ -95,6 +95,7 @@ func userRouter(api fiber.Router) { channel := api.Group("/channel") channel.Post("/list", handlers.ListChannel) channel.Post("/create", handlers.CreateChannel) + channel.Post("/create/v2", handlers.CreateChannelV2) // 交易 trade := api.Group("/trade") diff --git a/web/services/channel.go b/web/services/channel.go index fb35d67..cfbbae8 100644 --- a/web/services/channel.go +++ b/web/services/channel.go @@ -25,7 +25,7 @@ var Channel = &channelServer{ } type ChannelServiceProvider interface { - CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) + CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) RemoveChannels(batch string) error ClearExpiredChannels(proxyId int32) (int, error) } @@ -34,8 +34,8 @@ type channelServer struct { provider ChannelServiceProvider } -func (s *channelServer) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) { - return s.provider.CreateChannels(source, resourceId, authWhitelist, authPassword, count, edgeFilter) +func (s *channelServer) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, edgeFilter *EdgeFilter) ([]*m.Channel, error) { + return s.provider.CreateChannels(source, resourceNo, authWhitelist, authPassword, count, edgeFilter) } func (s *channelServer) RemoveChannels(batch string) error { @@ -115,12 +115,23 @@ func genPassPair() (string, string) { return string(username), string(password) } +func FindResourceNoById(resourceId int32) (string, error) { + resource, err := q.Resource. + Select(q.Resource.ResourceNo). + Where(q.Resource.ID.Eq(resourceId)). + Take() + if err != nil { + return "", ErrResourceNotExist + } + return u.Z(resource.ResourceNo), nil +} + // 查找资源 -func findResource(resourceId int32, now time.Time) (*ResourceView, error) { +func findResourceViewByNo(resourceNo string, now time.Time) (*ResourceView, error) { resource, err := q.Resource. Preload(field.Associations). Where( - q.Resource.ID.Eq(resourceId), + q.Resource.ResourceNo.Eq(resourceNo), q.Resource.Active.Is(true), ). Take() @@ -131,7 +142,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) { return nil, ErrResourceNotExist } var info = &ResourceView{ - Id: resource.ID, + ID: resource.ID, User: *resource.User, Active: resource.Active, Type: resource.Type, @@ -177,7 +188,7 @@ func findResource(resourceId int32, now time.Time) (*ResourceView, error) { // ResourceView 套餐数据的简化视图,便于直接获取主要数据 type ResourceView struct { - Id int32 + ID int32 User m.User Active bool Type m.ResourceType @@ -195,13 +206,13 @@ type ResourceView struct { } // 检查用户是否可提取 -func ensure(now time.Time, source netip.Addr, resourceId int32, authWhitelist bool, count int) (*ResourceView, []string, error) { +func ensure(now time.Time, source netip.Addr, resourceNo string, authWhitelist bool, count int) (*ResourceView, []string, error) { if count > 400 { return nil, nil, core.NewBizErr("单次最多提取 400 个") } // 获取用户套餐 - resource, err := findResource(resourceId, now) + resource, err := findResourceViewByNo(resourceNo, now) if err != nil { return nil, nil, err } diff --git a/web/services/channel_baiyin.go b/web/services/channel_baiyin.go index fabb80e..2600494 100644 --- a/web/services/channel_baiyin.go +++ b/web/services/channel_baiyin.go @@ -24,7 +24,7 @@ import ( type channelBaiyinProvider struct{} -func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int32, authWhitelist bool, authPassword bool, count int, filter *EdgeFilter) ([]*m.Channel, error) { +func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceNo string, authWhitelist bool, authPassword bool, count int, filter *EdgeFilter) ([]*m.Channel, error) { if filter == nil { return nil, core.NewBizErr("缺少节点过滤条件") } @@ -34,9 +34,9 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int channels := make([]*m.Channel, count) // 资源锁,防止并发扣减失败导致的端口悬空问题 - err := g.Redsync.WithLock(lockChannelCreateKey(resourceId), func() error { + err := g.Redsync.WithLock(lockChannelCreateKey(resourceNo), func() error { // 检查并获取套餐与白名单 - resource, whitelists, err := ensure(now, source, resourceId, authWhitelist, count) + resource, whitelists, err := ensure(now, source, resourceNo, authWhitelist, count) if err != nil { return err } @@ -65,7 +65,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int // 通道数据 channels[i] = &m.Channel{ UserID: user.ID, - ResourceID: resourceId, + ResourceID: resource.ID, BatchNo: batchNo, ProxyID: proxy.ID, Host: u.Else(proxy.Host, proxy.IP.String()), @@ -181,7 +181,7 @@ func (s *channelBaiyinProvider) CreateChannels(source netip.Addr, resourceId int // 保存提取记录 err = q.LogsUserUsage.Create(&m.LogsUserUsage{ UserID: user.ID, - ResourceID: resourceId, + ResourceID: resource.ID, BatchNo: batchNo, Count: int32(count), ISP: u.X(filter.Isp.String()), @@ -335,8 +335,8 @@ func (s *channelBaiyinProvider) ClearExpiredChannels(proxyId int32) (int, error) return len(batchSet), nil } -func lockChannelCreateKey(resourceId int32) string { - return fmt.Sprintf("platform:channel:create:%d", resourceId) +func lockChannelCreateKey(resourceNo string) string { + return fmt.Sprintf("platform:channel:create:%s", resourceNo) } func lockChannelRemoveKey(bid string) string {