分组概念
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m58s

This commit is contained in:
shanshanzhong 2026-03-19 03:21:47 -07:00
parent e5e9f93f68
commit f703b5089a
15 changed files with 101 additions and 17 deletions

View File

@ -15,10 +15,10 @@ Logger: # 日志配置
Level: debug # 日志级别: debug, info, warn, error, panic, fatal
MySQL:
Addr: 103.150.215.44:3306 # host 网络模式; bridge 模式改为 mysql:3306
Addr: 154.12.35.103:3306 # host 网络模式; bridge 模式改为 mysql:3306
Username: root # MySQL用户名
Password: jpcV41ppanel # MySQL密码与 .env MYSQL_ROOT_PASSWORD 一致
Dbname: hifast # MySQL数据库名
Dbname: ppanel # MySQL数据库名
Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
MaxIdleConns: 10
MaxOpenConns: 100

View File

@ -1,11 +1,16 @@
package auth
import (
"time"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/auth"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
"github.com/perfect-panel/server/pkg/turnstile"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
// User Telephone login

View File

@ -6,6 +6,7 @@ import (
"time"
commonLogic "github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/model/group"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
@ -117,20 +118,24 @@ func (l *QueryUserSubscribeNodeListLogic) getServers(userSub *user.Subscribe) (u
l.Debugw("[GetServers] Failed to check group enabled", logger.Field("error", err.Error()))
// Continue with tag-based filtering
}
nodeIds := tool.StringToInt64Slice(subDetails.Nodes)
tags := normalizeSubscribeNodeTags(subDetails.NodeTags)
isGroupEnabled := (groupEnabled == "true" || groupEnabled == "1")
enable := true
_, nodes, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
NodeId: nodeIds,
Tag: tags,
Enabled: &enable, // Only get enabled nodes
})
var nodes []*node.Node
if isGroupEnabled {
// Group mode: use group_ids to filter nodes
nodes, err = l.getNodesByGroup(userSub)
if err != nil {
l.Errorw("[GetServers] Failed to get nodes by group", logger.Field("error", err.Error()))
return nil, err
}
} else {
// Tag mode: use node_ids and tags to filter nodes
nodes, err = l.getNodesByTag(userSub)
if err != nil {
l.Errorw("[GetServers] Failed to get nodes by tag", logger.Field("error", err.Error()))
return nil, err
}
}
// Process nodes and create response
if len(nodes) > 0 {

View File

@ -70,6 +70,9 @@ type Subscribe struct {
NewUserOnly *bool `gorm:"type:tinyint(1);default:0;comment:New user only: allow purchase within 24h of registration, once per user"`
Nodes string `gorm:"type:varchar(255);comment:Node Ids"`
NodeTags string `gorm:"type:varchar(255);comment:Node Tags"`
NodeGroupIds JSONInt64Slice `gorm:"type:json;comment:Node Group IDs (JSON array, multiple groups)"`
NodeGroupId int64 `gorm:"default:0;index:idx_node_group_id;comment:Default Node Group ID (single ID)"`
TrafficLimit string `gorm:"type:text;comment:Traffic Limit Rules"`
Show *bool `gorm:"type:tinyint(1);not null;default:0;comment:Show portal page"`
Sell *bool `gorm:"type:tinyint(1);not null;default:0;comment:Sell"`
Sort int64 `gorm:"type:int;not null;default:0;comment:Sort"`

View File

@ -561,6 +561,13 @@ type DeleteAccountResponse struct {
Code int64 `json:"code"`
}
type DailyTrafficStats struct {
Date string `json:"date"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
Total int64 `json:"total"`
}
type DeleteAdsRequest struct {
Id int64 `json:"id"`
}
@ -681,6 +688,10 @@ type EmailAuthticateConfig struct {
DomainSuffixList string `json:"domain_suffix_list"`
}
type ExportGroupResultRequest struct {
HistoryId *int64 `form:"history_id,omitempty"`
}
type ErrorLogMessage struct {
Id int64 `json:"id"`
Platform string `json:"platform"`
@ -1114,6 +1125,37 @@ type GetInviteSalesResponse struct {
List []InvitedUserSale `json:"list"`
}
type GetGroupConfigRequest struct {
Keys []string `form:"keys,omitempty"`
}
type GetGroupConfigResponse struct {
Enabled bool `json:"enabled"`
Mode string `json:"mode"`
Config map[string]interface{} `json:"config"`
State RecalculationState `json:"state"`
}
type GetGroupHistoryDetailRequest struct {
Id int64 `form:"id" validate:"required"`
}
type GetGroupHistoryDetailResponse struct {
GroupHistoryDetail
}
type GetGroupHistoryRequest struct {
Page int `form:"page"`
Size int `form:"size"`
GroupMode string `form:"group_mode,omitempty"`
TriggerType string `form:"trigger_type,omitempty"`
}
type GetGroupHistoryResponse struct {
Total int64 `json:"total"`
List []GroupHistory `json:"list"`
}
type GetLoginLogRequest struct {
Page int `form:"page"`
Size int `form:"size"`
@ -2881,6 +2923,26 @@ type UpdateFamilyMaxMembersRequest struct {
MaxMembers int64 `json:"max_members" validate:"required,gt=0"`
}
type UpdateGroupConfigRequest struct {
Enabled bool `json:"enabled"`
Mode string `json:"mode"`
Config map[string]interface{} `json:"config"`
}
type UpdateNodeGroupRequest struct {
Id int64 `json:"id" validate:"required"`
Name string `json:"name"`
Description string `json:"description"`
Sort int `json:"sort"`
ForCalculation *bool `json:"for_calculation"`
IsExpiredGroup *bool `json:"is_expired_group"`
ExpiredDaysLimit *int `json:"expired_days_limit"`
MaxTrafficGBExpired *int64 `json:"max_traffic_gb_expired,omitempty"`
SpeedLimit *int `json:"speed_limit"`
MinTrafficGB *int64 `json:"min_traffic_gb,omitempty"`
MaxTrafficGB *int64 `json:"max_traffic_gb,omitempty"`
}
type UpdateNodeRequest struct {
Id int64 `json:"id"`
Name string `json:"name"`
@ -3171,6 +3233,7 @@ type UserStatisticsResponse struct {
type UserSubscribe struct {
Id int64 `json:"id"`
IdStr string `json:"id_str"`
UserId int64 `json:"user_id"`
OrderId int64 `json:"order_id"`
SubscribeId int64 `json:"subscribe_id"`

View File

@ -134,6 +134,11 @@ const (
DeviceBindLimitExceeded uint32 = 90019
)
// Permission error
const (
PermissionDenied uint32 = 40300
)
const (
OrderNotExist uint32 = 61001
PaymentMethodNotFound uint32 = 61002

View File

@ -102,6 +102,9 @@ func init() {
PaymentMethodNotFound: "Payment method not found",
OrderStatusError: "Order status error",
InsufficientOfPeriod: "Insufficient number of period",
// Permission error
PermissionDenied: "Permission denied",
}
}