feat(subscribe): add traffic limit rules and user traffic stats

- Add subscribe traffic_limit schema and migration\n- Support traffic_limit in admin create/update and list/details\n- Apply traffic_limit when building server user list speed limits\n- Add public user traffic stats API
This commit is contained in:
EUForest
2026-03-14 12:41:52 +08:00
parent 17163486f6
commit 06a2425474
34 changed files with 974 additions and 153 deletions
@@ -2,6 +2,7 @@ package group
import (
"context"
"errors"
"time"
"github.com/perfect-panel/server/internal/model/group"
@@ -25,17 +26,51 @@ func NewCreateNodeGroupLogic(ctx context.Context, svcCtx *svc.ServiceContext) *C
}
func (l *CreateNodeGroupLogic) CreateNodeGroup(req *types.CreateNodeGroupRequest) error {
// 验证:系统中只能有一个过期节点组
if req.IsExpiredGroup != nil && *req.IsExpiredGroup {
var count int64
err := l.svcCtx.DB.Model(&group.NodeGroup{}).
Where("is_expired_group = ?", true).
Count(&count).Error
if err != nil {
logger.Errorf("failed to check expired group count: %v", err)
return err
}
if count > 0 {
return errors.New("system already has an expired node group, cannot create multiple")
}
}
// 创建节点组
nodeGroup := &group.NodeGroup{
Name: req.Name,
Description: req.Description,
Sort: req.Sort,
ForCalculation: req.ForCalculation,
MinTrafficGB: req.MinTrafficGB,
MaxTrafficGB: req.MaxTrafficGB,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Name: req.Name,
Description: req.Description,
Sort: req.Sort,
ForCalculation: req.ForCalculation,
IsExpiredGroup: req.IsExpiredGroup,
MaxTrafficGBExpired: req.MaxTrafficGBExpired,
MinTrafficGB: req.MinTrafficGB,
MaxTrafficGB: req.MaxTrafficGB,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 设置过期节点组的默认值
if req.IsExpiredGroup != nil && *req.IsExpiredGroup {
// 过期节点组不参与分组计算
falseValue := false
nodeGroup.ForCalculation = &falseValue
if req.ExpiredDaysLimit != nil {
nodeGroup.ExpiredDaysLimit = *req.ExpiredDaysLimit
} else {
nodeGroup.ExpiredDaysLimit = 7 // 默认7天
}
if req.SpeedLimit != nil {
nodeGroup.SpeedLimit = *req.SpeedLimit
}
}
if err := l.svcCtx.DB.Create(nodeGroup).Error; err != nil {
logger.Errorf("failed to create node group: %v", err)
return err
@@ -6,6 +6,7 @@ import (
"fmt"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -37,9 +38,9 @@ func (l *DeleteNodeGroupLogic) DeleteNodeGroup(req *types.DeleteNodeGroupRequest
return err
}
// 检查是否有关联节点
// 检查是否有关联节点(使用JSON_CONTAINS查询node_group_ids数组)
var nodeCount int64
if err := l.svcCtx.DB.Table("nodes").Where("node_group_id = ?", nodeGroup.Id).Count(&nodeCount).Error; err != nil {
if err := l.svcCtx.DB.Model(&node.Node{}).Where("JSON_CONTAINS(node_group_ids, ?)", fmt.Sprintf("[%d]", nodeGroup.Id)).Count(&nodeCount).Error; err != nil {
logger.Errorf("failed to count nodes in group: %v", err)
return err
}
@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -77,7 +78,7 @@ func (l *ExportGroupResultLogic) ExportGroupResult(req *types.ExportGroupResultR
NodeGroupId int64 `json:"node_group_id"`
}
var userSubscribes []UserNodeGroupInfo
if err := l.svcCtx.DB.Table("user_subscribe").
if err := l.svcCtx.DB.Model(&user.Subscribe{}).
Select("DISTINCT user_id as id, node_group_id").
Where("node_group_id > ?", 0).
Find(&userSubscribes).Error; err != nil {
@@ -2,8 +2,10 @@ package group
import (
"context"
"fmt"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -46,9 +48,9 @@ func (l *GetNodeGroupListLogic) GetNodeGroupList(req *types.GetNodeGroupListRequ
// 转换为响应格式
var list []types.NodeGroup
for _, ng := range nodeGroups {
// 统计该组的节点数
// 统计该组的节点数JSON数组查询)
var nodeCount int64
l.svcCtx.DB.Table("nodes").Where("node_group_id = ?", ng.Id).Count(&nodeCount)
l.svcCtx.DB.Model(&node.Node{}).Where("JSON_CONTAINS(node_group_ids, ?)", fmt.Sprintf("[%d]", ng.Id)).Count(&nodeCount)
// 处理指针类型的字段
var forCalculation bool
@@ -58,25 +60,37 @@ func (l *GetNodeGroupListLogic) GetNodeGroupList(req *types.GetNodeGroupListRequ
forCalculation = true // 默认值
}
var minTrafficGB, maxTrafficGB int64
var isExpiredGroup bool
if ng.IsExpiredGroup != nil {
isExpiredGroup = *ng.IsExpiredGroup
}
var minTrafficGB, maxTrafficGB, maxTrafficGBExpired int64
if ng.MinTrafficGB != nil {
minTrafficGB = *ng.MinTrafficGB
}
if ng.MaxTrafficGB != nil {
maxTrafficGB = *ng.MaxTrafficGB
}
if ng.MaxTrafficGBExpired != nil {
maxTrafficGBExpired = *ng.MaxTrafficGBExpired
}
list = append(list, types.NodeGroup{
Id: ng.Id,
Name: ng.Name,
Description: ng.Description,
Sort: ng.Sort,
ForCalculation: forCalculation,
MinTrafficGB: minTrafficGB,
MaxTrafficGB: maxTrafficGB,
NodeCount: nodeCount,
CreatedAt: ng.CreatedAt.Unix(),
UpdatedAt: ng.UpdatedAt.Unix(),
Id: ng.Id,
Name: ng.Name,
Description: ng.Description,
Sort: ng.Sort,
ForCalculation: forCalculation,
IsExpiredGroup: isExpiredGroup,
ExpiredDaysLimit: ng.ExpiredDaysLimit,
MaxTrafficGBExpired: maxTrafficGBExpired,
SpeedLimit: ng.SpeedLimit,
MinTrafficGB: minTrafficGB,
MaxTrafficGB: maxTrafficGB,
NodeCount: nodeCount,
CreatedAt: ng.CreatedAt.Unix(),
UpdatedAt: ng.UpdatedAt.Unix(),
})
}
@@ -28,14 +28,14 @@ func NewGetSubscribeGroupMappingLogic(ctx context.Context, svcCtx *svc.ServiceCo
func (l *GetSubscribeGroupMappingLogic) GetSubscribeGroupMapping(req *types.GetSubscribeGroupMappingRequest) (resp *types.GetSubscribeGroupMappingResponse, err error) {
// 1. 查询所有订阅套餐
var subscribes []subscribe.Subscribe
if err := l.svcCtx.DB.Table("subscribe").Find(&subscribes).Error; err != nil {
if err := l.svcCtx.DB.Model(&subscribe.Subscribe{}).Find(&subscribes).Error; err != nil {
l.Errorw("[GetSubscribeGroupMapping] failed to query subscribes", logger.Field("error", err.Error()))
return nil, err
}
// 2. 查询所有节点组
var nodeGroups []group.NodeGroup
if err := l.svcCtx.DB.Table("node_group").Find(&nodeGroups).Error; err != nil {
if err := l.svcCtx.DB.Model(&group.NodeGroup{}).Find(&nodeGroups).Error; err != nil {
l.Errorw("[GetSubscribeGroupMapping] failed to query node groups", logger.Field("error", err.Error()))
return nil, err
}
@@ -6,7 +6,10 @@ import (
"fmt"
"strings"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -38,7 +41,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
NodeGroupId int64 // 用户订阅的 node_group_id(单个ID
}
var userSubscribes []UserSubscribe
err = l.svcCtx.DB.Table("user_subscribe").
err = l.svcCtx.DB.Model(&user.Subscribe{}).
Select("id, user_id, subscribe_id, node_group_id").
Where("user_id = ? AND status IN ?", req.UserId, []int8{0, 1}).
Find(&userSubscribes).Error
@@ -74,7 +77,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
NodeTags string // 节点标签
}
var subscribeInfos []SubscribeInfo
err = l.svcCtx.DB.Table("subscribe").
err = l.svcCtx.DB.Model(&subscribe.Subscribe{}).
Select("id, node_group_id, node_group_ids, nodes, node_tags").
Where("id IN ?", subscribeIds).
Find(&subscribeInfos).Error
@@ -149,15 +152,23 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
logger.Infof("[PreviewUserNodes] collected direct node_ids: %v", allDirectNodeIds)
// 4. 判断分组功能是否启用
var groupEnabled string
l.svcCtx.DB.Table("system").
type SystemConfig struct {
Value string
}
var config SystemConfig
l.svcCtx.DB.Model(&struct {
Category string `gorm:"column:category"`
Key string `gorm:"column:key"`
Value string `gorm:"column:value"`
}{}).
Table("system").
Where("`category` = ? AND `key` = ?", "group", "enabled").
Select("value").
Scan(&groupEnabled)
Scan(&config)
logger.Infof("[PreviewUserNodes] groupEnabled: %v", groupEnabled)
logger.Infof("[PreviewUserNodes] groupEnabled: %v", config.Value)
isGroupEnabled := groupEnabled == "true" || groupEnabled == "1"
isGroupEnabled := config.Value == "true" || config.Value == "1"
var filteredNodes []node.Node
@@ -177,7 +188,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
// 5. 查询所有启用的节点(只有当有节点组时才查询)
if len(allNodeGroupIds) > 0 {
var dbNodes []node.Node
err = l.svcCtx.DB.Table("nodes").
err = l.svcCtx.DB.Model(&node.Node{}).
Where("enabled = ?", true).
Find(&dbNodes).Error
if err != nil {
@@ -238,7 +249,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
// 8. 查询所有启用的节点(只有当有 tags 时才查询)
if len(allTags) > 0 {
var dbNodes []node.Node
err = l.svcCtx.DB.Table("nodes").
err = l.svcCtx.DB.Model(&node.Node{}).
Where("enabled = ?", true).
Find(&dbNodes).Error
if err != nil {
@@ -370,7 +381,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
Name string
}
var nodeGroupInfos []NodeGroupInfo
err = l.svcCtx.DB.Table("node_group").
err = l.svcCtx.DB.Model(&group.NodeGroup{}).
Select("id, name").
Where("id IN ?", allGroupIds).
Find(&nodeGroupInfos).Error
@@ -508,7 +519,7 @@ func (l *PreviewUserNodesLogic) PreviewUserNodes(req *types.PreviewUserNodesRequ
if len(allDirectNodeIds) > 0 {
// 查询直接分配的节点详情
var directNodes []node.Node
err = l.svcCtx.DB.Table("nodes").
err = l.svcCtx.DB.Model(&node.Node{}).
Where("id IN ? AND enabled = ?", allDirectNodeIds, true).
Find(&directNodes).Error
if err != nil {
@@ -3,9 +3,13 @@ package group
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -131,7 +135,7 @@ func (l *RecalculateGroupLogic) getUserEmail(tx *gorm.DB, userId int64) string {
}
var authMethod UserAuthMethod
if err := tx.Table("user_auth_methods").
if err := tx.Model(&user.AuthMethods{}).
Select("auth_identifier").
Where("user_id = ? AND (auth_type = ? OR auth_type = ?)", userId, "email", "6").
First(&authMethod).Error; err != nil {
@@ -152,7 +156,7 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
}
var userSubscribes []UserSubscribeInfo
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Select("id, user_id, subscribe_id").
Where("group_locked = ? AND status IN (0, 1)", 0). // 只查询未锁定且有效的用户订阅
Scan(&userSubscribes).Error; err != nil {
@@ -168,7 +172,7 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
// 1.5 查询所有参与计算的节点组ID
var calculationNodeGroups []group.NodeGroup
if err := tx.Table("node_group").
if err := tx.Model(&group.NodeGroup{}).
Select("id").
Where("for_calculation = ?", true).
Scan(&calculationNodeGroups).Error; err != nil {
@@ -195,7 +199,7 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
NodeGroupIds string `json:"node_group_ids"` // JSON string
}
var subscribeInfos []SubscribeInfo
if err := tx.Table("subscribe").
if err := tx.Model(&subscribe.Subscribe{}).
Select("id, node_group_ids").
Where("id IN ?", subscribeIds).
Find(&subscribeInfos).Error; err != nil {
@@ -261,10 +265,10 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
}
}
// 如果没有节点组ID跳过
// 如果没有节点组ID,跳过
if len(nodeGroupIds) == 0 {
l.Debugf("no valid node_group_ids for subscribe_id=%d, setting to 0", subInfo.Id)
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Where("id = ?", us.Id).
Update("node_group_id", 0).Error; err != nil {
l.Errorw("failed to update user_subscribe node_group_id",
@@ -290,7 +294,7 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
}
// 更新 user_subscribe 的 node_group_id 字段(单个ID
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Where("id = ?", us.Id).
Update("node_group_id", selectedNodeGroupId).Error; err != nil {
l.Errorw("failed to update user_subscribe node_group_id",
@@ -329,8 +333,8 @@ func (l *RecalculateGroupLogic) executeAverageGrouping(tx *gorm.DB, historyId in
// 统计该节点组的节点数
var nodeCount int64 = 0
if nodeGroupId > 0 {
if err := tx.Table("nodes").
Where("JSON_CONTAINS(node_group_ids, ?)", nodeGroupId).
if err := tx.Model(&node.Node{}).
Where("JSON_CONTAINS(node_group_ids, ?)", fmt.Sprintf("[%d]", nodeGroupId)).
Count(&nodeCount).Error; err != nil {
l.Errorw("failed to count nodes",
logger.Field("node_group_id", nodeGroupId),
@@ -383,7 +387,7 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
}
var userSubscribes []UserSubscribeInfo
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Select("id, user_id, subscribe_id").
Where("group_locked = ? AND status IN (0, 1)", 0).
Scan(&userSubscribes).Error; err != nil {
@@ -400,7 +404,7 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
// 1.5 查询所有参与计算的节点组ID
var calculationNodeGroups []group.NodeGroup
if err := tx.Table("node_group").
if err := tx.Model(&group.NodeGroup{}).
Select("id").
Where("for_calculation = ?", true).
Scan(&calculationNodeGroups).Error; err != nil {
@@ -427,7 +431,7 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
NodeGroupIds string `json:"node_group_ids"` // JSON string
}
var subscribeInfos []SubscribeInfo
if err := tx.Table("subscribe").
if err := tx.Model(&subscribe.Subscribe{}).
Select("id, node_group_ids").
Where("id IN ?", subscribeIds).
Find(&subscribeInfos).Error; err != nil {
@@ -501,7 +505,7 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
us.Id, us.SubscribeId, selectedNodeGroupId, len(nodeGroupIds))
// 更新 user_subscribe 的 node_group_id 字段
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Where("id = ?", us.Id).
Update("node_group_id", selectedNodeGroupId).Error; err != nil {
l.Errorw("failed to update user_subscribe node_group_id",
@@ -548,7 +552,7 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
expiredAffectedCount := 0
for _, eu := range expiredUserSubscribes {
// 更新 user_subscribe 表的 node_group_id 字段到 0
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Where("id = ?", eu.Id).
Update("node_group_id", 0).Error; err != nil {
l.Errorw("failed to update expired user subscribe node_group_id",
@@ -573,8 +577,8 @@ func (l *RecalculateGroupLogic) executeSubscribeGrouping(tx *gorm.DB, historyId
// 统计该节点组的节点数
var nodeCount int64 = 0
if nodeGroupId > 0 {
if err := tx.Table("nodes").
Where("JSON_CONTAINS(node_group_ids, ?)", nodeGroupId).
if err := tx.Model(&node.Node{}).
Where("JSON_CONTAINS(node_group_ids, ?)", fmt.Sprintf("[%d]", nodeGroupId)).
Count(&nodeCount).Error; err != nil {
l.Errorw("failed to count nodes",
logger.Field("node_group_id", nodeGroupId),
@@ -652,7 +656,7 @@ func (l *RecalculateGroupLogic) executeTrafficGrouping(tx *gorm.DB, historyId in
}
var userSubscribes []UserSubscribeInfo
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Select("id, user_id, upload, download, (upload + download) as used_traffic").
Where("group_locked = ? AND status IN (0, 1)", 0). // 只查询有效且未锁定的用户订阅
Scan(&userSubscribes).Error; err != nil {
@@ -694,7 +698,7 @@ func (l *RecalculateGroupLogic) executeTrafficGrouping(tx *gorm.DB, historyId in
// 如果没有匹配到任何范围,targetNodeGroupId 保持为 0(不分配节点组)
// 更新 user_subscribe 的 node_group_id 字段
if err := tx.Table("user_subscribe").
if err := tx.Model(&user.Subscribe{}).
Where("id = ?", us.Id).
Update("node_group_id", targetNodeGroupId).Error; err != nil {
l.Errorw("failed to update user subscribe node_group_id",
@@ -6,6 +6,7 @@ import (
"time"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger"
@@ -37,6 +38,34 @@ func (l *UpdateNodeGroupLogic) UpdateNodeGroup(req *types.UpdateNodeGroupRequest
return err
}
// 验证:系统中只能有一个过期节点组
if req.IsExpiredGroup != nil && *req.IsExpiredGroup {
var count int64
err := l.svcCtx.DB.Model(&group.NodeGroup{}).
Where("is_expired_group = ? AND id != ?", true, req.Id).
Count(&count).Error
if err != nil {
logger.Errorf("failed to check expired group count: %v", err)
return err
}
if count > 0 {
return errors.New("system already has an expired node group, cannot create multiple")
}
// 验证:被订阅商品设置为默认节点组的不能设置为过期节点组
var subscribeCount int64
err = l.svcCtx.DB.Model(&subscribe.Subscribe{}).
Where("node_group_id = ?", req.Id).
Count(&subscribeCount).Error
if err != nil {
logger.Errorf("failed to check subscribe usage: %v", err)
return err
}
if subscribeCount > 0 {
return errors.New("this node group is used as default node group in subscription products, cannot set as expired group")
}
}
// 构建更新数据
updates := map[string]interface{}{
"updated_at": time.Now(),
@@ -53,6 +82,22 @@ func (l *UpdateNodeGroupLogic) UpdateNodeGroup(req *types.UpdateNodeGroupRequest
if req.ForCalculation != nil {
updates["for_calculation"] = *req.ForCalculation
}
if req.IsExpiredGroup != nil {
updates["is_expired_group"] = *req.IsExpiredGroup
// 过期节点组不参与分组计算
if *req.IsExpiredGroup {
updates["for_calculation"] = false
}
}
if req.ExpiredDaysLimit != nil {
updates["expired_days_limit"] = *req.ExpiredDaysLimit
}
if req.MaxTrafficGBExpired != nil {
updates["max_traffic_gb_expired"] = *req.MaxTrafficGBExpired
}
if req.SpeedLimit != nil {
updates["speed_limit"] = *req.SpeedLimit
}
// 获取新的流量区间值
newMinTraffic := nodeGroup.MinTrafficGB
@@ -34,6 +34,12 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
val, _ := json.Marshal(req.Discount)
discount = string(val)
}
trafficLimit := ""
if len(req.TrafficLimit) > 0 {
val, _ := json.Marshal(req.TrafficLimit)
trafficLimit = string(val)
}
sub := &subscribe.Subscribe{
Id: 0,
Name: req.Name,
@@ -52,6 +58,7 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
NodeTags: tool.StringSliceToString(req.NodeTags),
NodeGroupIds: subscribe.JSONInt64Slice(req.NodeGroupIds),
NodeGroupId: req.NodeGroupId,
TrafficLimit: trafficLimit,
Show: req.Show,
Sell: req.Sell,
Sort: 0,
@@ -42,6 +42,12 @@ func (l *GetSubscribeDetailsLogic) GetSubscribeDetails(req *types.GetSubscribeDe
l.Logger.Error("[GetSubscribeDetailsLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("discount", sub.Discount))
}
}
if sub.TrafficLimit != "" {
err = json.Unmarshal([]byte(sub.TrafficLimit), &resp.TrafficLimit)
if err != nil {
l.Logger.Error("[GetSubscribeDetailsLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("traffic_limit", sub.TrafficLimit))
}
}
resp.Nodes = tool.StringToInt64Slice(sub.Nodes)
resp.NodeTags = strings.Split(sub.NodeTags, ",")
return resp, nil
@@ -62,6 +62,12 @@ func (l *GetSubscribeListLogic) GetSubscribeList(req *types.GetSubscribeListRequ
l.Logger.Error("[GetSubscribeListLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("discount", item.Discount))
}
}
if item.TrafficLimit != "" {
err = json.Unmarshal([]byte(item.TrafficLimit), &sub.TrafficLimit)
if err != nil {
l.Logger.Error("[GetSubscribeListLogic] JSON unmarshal failed: ", logger.Field("error", err.Error()), logger.Field("traffic_limit", item.TrafficLimit))
}
}
sub.Nodes = tool.StringToInt64Slice(item.Nodes)
sub.NodeTags = strings.Split(item.NodeTags, ",")
// Handle NodeGroupIds - convert from JSONInt64Slice to []int64
@@ -42,6 +42,12 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
val, _ := json.Marshal(req.Discount)
discount = string(val)
}
trafficLimit := ""
if len(req.TrafficLimit) > 0 {
val, _ := json.Marshal(req.TrafficLimit)
trafficLimit = string(val)
}
sub := &subscribe.Subscribe{
Id: req.Id,
Name: req.Name,
@@ -60,6 +66,7 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
NodeTags: tool.StringSliceToString(req.NodeTags),
NodeGroupIds: subscribe.JSONInt64Slice(req.NodeGroupIds),
NodeGroupId: req.NodeGroupId,
TrafficLimit: trafficLimit,
Show: req.Show,
Sell: req.Sell,
Sort: req.Sort,