From a9205cc3fc884789fb899e22fc7cc8dd7ece60f2 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Sun, 29 Mar 2026 10:07:17 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E8=8A=82=E7=82=B9=E5=88=86=E7=BB=84=E5=90=8D?= =?UTF-8?q?=E5=8F=8A=E9=99=90=E9=80=9F=E8=B5=B7=E6=AD=A2=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UserSubscribe/UserSubscribeDetail 新增 node_group_id/node_group_name 字段 - 管理员查询用户订阅列表批量填充分组名 - 管理员查询单个订阅详情填充分组名 - ThrottleResult/UserSubscribeDetail 新增 throttle_start/throttle_end 字段 - 限速时返回限速窗口起止时间(秒级 Unix 时间戳) Co-Authored-By: claude-flow --- .../admin/user/getUserSubscribeByIdLogic.go | 11 +++++++++ .../logic/admin/user/getUserSubscribeLogic.go | 24 +++++++++++++++++++ internal/types/types.go | 5 ++++ pkg/speedlimit/calculator.go | 14 +++++++---- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/internal/logic/admin/user/getUserSubscribeByIdLogic.go b/internal/logic/admin/user/getUserSubscribeByIdLogic.go index 1cc4984..5ef44e7 100644 --- a/internal/logic/admin/user/getUserSubscribeByIdLogic.go +++ b/internal/logic/admin/user/getUserSubscribeByIdLogic.go @@ -3,6 +3,7 @@ package user import ( "context" + "github.com/perfect-panel/server/internal/model/group" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/logger" @@ -36,12 +37,22 @@ func (l *GetUserSubscribeByIdLogic) GetUserSubscribeById(req *types.GetUserSubsc var subscribeDetails types.UserSubscribeDetail tool.DeepCopy(&subscribeDetails, sub) + // 填充分组名 + if sub.NodeGroupId > 0 { + var ng group.NodeGroup + if err := l.svcCtx.DB.WithContext(l.ctx).First(&ng, sub.NodeGroupId).Error; err == nil { + subscribeDetails.NodeGroupName = ng.Name + } + } + // Calculate speed limit status if sub.Subscribe != nil && sub.Status == 1 { result := speedlimit.Calculate(l.ctx, l.svcCtx.DB, sub.UserId, sub.Id, sub.Subscribe.SpeedLimit, sub.Subscribe.TrafficLimit) subscribeDetails.EffectiveSpeed = result.EffectiveSpeed subscribeDetails.IsThrottled = result.IsThrottled subscribeDetails.ThrottleRule = result.ThrottleRule + subscribeDetails.ThrottleStart = result.ThrottleStart + subscribeDetails.ThrottleEnd = result.ThrottleEnd } return &subscribeDetails, nil diff --git a/internal/logic/admin/user/getUserSubscribeLogic.go b/internal/logic/admin/user/getUserSubscribeLogic.go index cd1e733..c33040a 100644 --- a/internal/logic/admin/user/getUserSubscribeLogic.go +++ b/internal/logic/admin/user/getUserSubscribeLogic.go @@ -3,6 +3,7 @@ package user import ( "context" + "github.com/perfect-panel/server/internal/model/group" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/logger" @@ -38,10 +39,33 @@ func (l *GetUserSubscribeLogic) GetUserSubscribe(req *types.GetUserSubscribeList Total: int64(len(data)), } + // 收集所有 node_group_id,批量查分组名 + groupIdSet := make(map[int64]struct{}) + for _, item := range data { + if item.NodeGroupId > 0 { + groupIdSet[item.NodeGroupId] = struct{}{} + } + } + groupNames := make(map[int64]string) + if len(groupIdSet) > 0 { + ids := make([]int64, 0, len(groupIdSet)) + for id := range groupIdSet { + ids = append(ids, id) + } + var groups []group.NodeGroup + if err := l.svcCtx.DB.WithContext(l.ctx).Where("id IN ?", ids).Find(&groups).Error; err == nil { + for _, g := range groups { + groupNames[g.Id] = g.Name + } + } + } + for _, item := range data { var sub types.UserSubscribe tool.DeepCopy(&sub, item) sub.Short, _ = tool.FixedUniqueString(item.Token, 8, "") + sub.NodeGroupId = item.NodeGroupId + sub.NodeGroupName = groupNames[item.NodeGroupId] resp.List = append(resp.List, sub) } return diff --git a/internal/types/types.go b/internal/types/types.go index 7a96a47..149eef5 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -3259,6 +3259,8 @@ type UserSubscribe struct { OrderId int64 `json:"order_id"` SubscribeId int64 `json:"subscribe_id"` Subscribe Subscribe `json:"subscribe"` + NodeGroupId int64 `json:"node_group_id"` + NodeGroupName string `json:"node_group_name"` StartTime int64 `json:"start_time"` ExpireTime int64 `json:"expire_time"` FinishedAt int64 `json:"finished_at"` @@ -3285,6 +3287,7 @@ type UserSubscribeDetail struct { SubscribeId int64 `json:"subscribe_id"` Subscribe Subscribe `json:"subscribe"` NodeGroupId int64 `json:"node_group_id"` + NodeGroupName string `json:"node_group_name"` GroupLocked bool `json:"group_locked"` StartTime int64 `json:"start_time"` ExpireTime int64 `json:"expire_time"` @@ -3297,6 +3300,8 @@ type UserSubscribeDetail struct { EffectiveSpeed int64 `json:"effective_speed"` IsThrottled bool `json:"is_throttled"` ThrottleRule string `json:"throttle_rule,omitempty"` + ThrottleStart int64 `json:"throttle_start,omitempty"` + ThrottleEnd int64 `json:"throttle_end,omitempty"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } diff --git a/pkg/speedlimit/calculator.go b/pkg/speedlimit/calculator.go index 78f485e..287d6c1 100644 --- a/pkg/speedlimit/calculator.go +++ b/pkg/speedlimit/calculator.go @@ -19,11 +19,13 @@ type TrafficLimitRule struct { // ThrottleResult contains the computed speed limit status for a user subscription. type ThrottleResult struct { - BaseSpeed int64 `json:"base_speed"` // Plan base speed limit (Mbps, 0=unlimited) - EffectiveSpeed int64 `json:"effective_speed"` // Current effective speed limit (Mbps) - IsThrottled bool `json:"is_throttled"` // Whether the user is currently throttled - ThrottleRule string `json:"throttle_rule"` // Description of the matched rule (empty if not throttled) - UsedTrafficGB float64 `json:"used_traffic_gb"` // Traffic used in the matched rule's window (GB) + BaseSpeed int64 `json:"base_speed"` // Plan base speed limit (Mbps, 0=unlimited) + EffectiveSpeed int64 `json:"effective_speed"` // Current effective speed limit (Mbps) + IsThrottled bool `json:"is_throttled"` // Whether the user is currently throttled + ThrottleRule string `json:"throttle_rule"` // Description of the matched rule (empty if not throttled) + UsedTrafficGB float64 `json:"used_traffic_gb"` // Traffic used in the matched rule's window (GB) + ThrottleStart int64 `json:"throttle_start"` // Window start Unix timestamp (seconds), 0 if not throttled + ThrottleEnd int64 `json:"throttle_end"` // Window end Unix timestamp (seconds), 0 if not throttled } // Calculate computes the effective speed limit for a user subscription, @@ -89,6 +91,8 @@ func Calculate(ctx context.Context, db *gorm.DB, userId, subscribeId, baseSpeedL result.EffectiveSpeed = rule.SpeedLimit result.IsThrottled = true result.UsedTrafficGB = usedGB + result.ThrottleStart = startTime.Unix() + result.ThrottleEnd = now.Unix() statLabel := "小时" if rule.StatType == "day" {