diff --git a/apis/admin/user.api b/apis/admin/user.api index bb585ff..3b3b069 100644 --- a/apis/admin/user.api +++ b/apis/admin/user.api @@ -76,29 +76,6 @@ type ( GiftAmount int64 `json:"gift_amount"` IsAdmin bool `json:"is_admin"` } - UserSubscribeDetail { - Id int64 `json:"id"` - UserId int64 `json:"user_id"` - User User `json:"user"` - OrderId int64 `json:"order_id"` - SubscribeId int64 `json:"subscribe_id"` - Subscribe Subscribe `json:"subscribe"` - NodeGroupId int64 `json:"node_group_id"` - GroupLocked bool `json:"group_locked"` - StartTime int64 `json:"start_time"` - ExpireTime int64 `json:"expire_time"` - ResetTime int64 `json:"reset_time"` - Traffic int64 `json:"traffic"` - Download int64 `json:"download"` - Upload int64 `json:"upload"` - Token string `json:"token"` - Status uint8 `json:"status"` - EffectiveSpeed int64 `json:"effective_speed"` - IsThrottled bool `json:"is_throttled"` - ThrottleRule string `json:"throttle_rule,omitempty"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - } BatchDeleteUserRequest { Ids []int64 `json:"ids" validate:"required"` } @@ -158,18 +135,22 @@ type ( Total int64 `json:"total"` } CreateUserSubscribeRequest { - UserId int64 `json:"user_id"` - ExpiredAt int64 `json:"expired_at"` - Traffic int64 `json:"traffic"` - SubscribeId int64 `json:"subscribe_id"` + UserId int64 `json:"user_id"` + ExpiredAt int64 `json:"expired_at"` + Traffic int64 `json:"traffic"` + SubscribeId int64 `json:"subscribe_id"` + SpeedLimit int64 `json:"speed_limit,optional"` + TrafficLimit string `json:"traffic_limit,optional"` } UpdateUserSubscribeRequest { - UserSubscribeId int64 `json:"user_subscribe_id"` - SubscribeId int64 `json:"subscribe_id"` - Traffic int64 `json:"traffic"` - ExpiredAt int64 `json:"expired_at"` - Upload int64 `json:"upload"` - Download int64 `json:"download"` + UserSubscribeId int64 `json:"user_subscribe_id"` + SubscribeId int64 `json:"subscribe_id"` + Traffic int64 `json:"traffic"` + ExpiredAt int64 `json:"expired_at"` + Upload int64 `json:"upload"` + Download int64 `json:"download"` + SpeedLimit *int64 `json:"speed_limit,omitempty"` + TrafficLimit *string `json:"traffic_limit,omitempty"` } GetUserLoginLogsRequest { Page int `form:"page"` diff --git a/apis/types.api b/apis/types.api index 88a347a..27a5716 100644 --- a/apis/types.api +++ b/apis/types.api @@ -171,13 +171,13 @@ type ( DeviceLimit int64 `json:"device_limit"` } VerifyConfig { - CaptchaType string `json:"captcha_type"` // local or turnstile - TurnstileSiteKey string `json:"turnstile_site_key"` - TurnstileSecret string `json:"turnstile_secret"` - EnableUserLoginCaptcha bool `json:"enable_user_login_captcha"` // User login captcha - EnableUserRegisterCaptcha bool `json:"enable_user_register_captcha"` // User register captcha - EnableAdminLoginCaptcha bool `json:"enable_admin_login_captcha"` // Admin login captcha - EnableUserResetPasswordCaptcha bool `json:"enable_user_reset_password_captcha"` // User reset password captcha + CaptchaType string `json:"captcha_type"` // local or turnstile + TurnstileSiteKey string `json:"turnstile_site_key"` + TurnstileSecret string `json:"turnstile_secret"` + EnableUserLoginCaptcha bool `json:"enable_user_login_captcha"` // User login captcha + EnableUserRegisterCaptcha bool `json:"enable_user_register_captcha"` // User register captcha + EnableAdminLoginCaptcha bool `json:"enable_admin_login_captcha"` // Admin login captcha + EnableUserResetPasswordCaptcha bool `json:"enable_user_reset_password_captcha"` // User reset password captcha } NodeConfig { NodeSecret string `json:"node_secret"` @@ -536,6 +536,35 @@ type ( CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } + UserSubscribeDetail { + Id int64 `json:"id"` + UserId int64 `json:"user_id"` + User User `json:"user"` + 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"` + GroupLocked bool `json:"group_locked"` + StartTime int64 `json:"start_time"` + ExpireTime int64 `json:"expire_time"` + ResetTime int64 `json:"reset_time"` + Traffic int64 `json:"traffic"` + Download int64 `json:"download"` + Upload int64 `json:"upload"` + SpeedLimit int64 `json:"speed_limit"` + TrafficLimit []TrafficLimit `json:"user_traffic_limit"` + PlanSpeedLimit int64 `json:"plan_speed_limit"` + Token string `json:"token"` + Status uint8 `json:"status"` + 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"` + } UserAffiliate { Avatar string `json:"avatar"` Identifier string `json:"identifier"` diff --git a/initialize/migrate/database/02153_user_subscribe_speed_limit.down.sql b/initialize/migrate/database/02153_user_subscribe_speed_limit.down.sql new file mode 100644 index 0000000..579a942 --- /dev/null +++ b/initialize/migrate/database/02153_user_subscribe_speed_limit.down.sql @@ -0,0 +1,5 @@ +-- Purpose: Rollback user-level speed limit overrides from user_subscribe + +ALTER TABLE `user_subscribe` + DROP COLUMN IF EXISTS `traffic_limit`, + DROP COLUMN IF EXISTS `speed_limit`; diff --git a/initialize/migrate/database/02153_user_subscribe_speed_limit.up.sql b/initialize/migrate/database/02153_user_subscribe_speed_limit.up.sql new file mode 100644 index 0000000..001c1fc --- /dev/null +++ b/initialize/migrate/database/02153_user_subscribe_speed_limit.up.sql @@ -0,0 +1,37 @@ +-- Purpose: Add user-level speed limit overrides to user_subscribe + +SET @column_exists = ( + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'user_subscribe' + AND COLUMN_NAME = 'speed_limit' +); + +SET @sql = IF( + @column_exists = 0, + 'ALTER TABLE `user_subscribe` ADD COLUMN `speed_limit` int NOT NULL DEFAULT 0 COMMENT ''User-level speed limit override (Mbps, 0=use plan default)'' AFTER `upload`', + 'SELECT ''Column speed_limit already exists in user_subscribe table''' +); + +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @column_exists = ( + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'user_subscribe' + AND COLUMN_NAME = 'traffic_limit' +); + +SET @sql = IF( + @column_exists = 0, + 'ALTER TABLE `user_subscribe` ADD COLUMN `traffic_limit` text DEFAULT NULL COMMENT ''User-level traffic limit rules override (JSON, NULL=use plan default)'' AFTER `speed_limit`', + 'SELECT ''Column traffic_limit already exists in user_subscribe table''' +); + +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/internal/model/user/user.go b/internal/model/user/user.go index caafcb0..bea2c6f 100644 --- a/internal/model/user/user.go +++ b/internal/model/user/user.go @@ -101,6 +101,8 @@ type Subscribe struct { Traffic int64 `gorm:"default:0;comment:Traffic"` Download int64 `gorm:"default:0;comment:Download Traffic"` Upload int64 `gorm:"default:0;comment:Upload Traffic"` + SpeedLimit int64 `gorm:"default:0;comment:User-level speed limit override (Mbps, 0=use plan default)"` + TrafficLimit string `gorm:"type:text;default:null;comment:User-level traffic limit rules override (JSON)"` ExpiredDownload int64 `gorm:"default:0;comment:Expired period download traffic (bytes)"` ExpiredUpload int64 `gorm:"default:0;comment:Expired period upload traffic (bytes)"` Token string `gorm:"index:idx_token;unique;type:varchar(255);default:'';comment:Token"` diff --git a/internal/types/types.go b/internal/types/types.go index 1717659..137a339 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -541,10 +541,12 @@ type CreateUserRequest struct { } type CreateUserSubscribeRequest struct { - UserId int64 `json:"user_id"` - ExpiredAt int64 `json:"expired_at"` - Traffic int64 `json:"traffic"` - SubscribeId int64 `json:"subscribe_id"` + UserId int64 `json:"user_id"` + ExpiredAt int64 `json:"expired_at"` + Traffic int64 `json:"traffic"` + SubscribeId int64 `json:"subscribe_id"` + SpeedLimit int64 `json:"speed_limit,optional"` + TrafficLimit string `json:"traffic_limit,optional"` } type CreateUserTicketFollowRequest struct { @@ -3317,12 +3319,14 @@ type UpdateUserSubscribeNoteRequest struct { } type UpdateUserSubscribeRequest struct { - UserSubscribeId int64 `json:"user_subscribe_id"` - SubscribeId int64 `json:"subscribe_id"` - Traffic int64 `json:"traffic"` - ExpiredAt int64 `json:"expired_at"` - Upload int64 `json:"upload"` - Download int64 `json:"download"` + UserSubscribeId int64 `json:"user_subscribe_id"` + SubscribeId int64 `json:"subscribe_id"` + Traffic int64 `json:"traffic"` + ExpiredAt int64 `json:"expired_at"` + Upload int64 `json:"upload"` + Download int64 `json:"download"` + SpeedLimit *int64 `json:"speed_limit,omitempty"` + TrafficLimit *string `json:"traffic_limit,omitempty"` } type UpdateUserTicketStatusRequest struct { @@ -3474,30 +3478,33 @@ type UserSubscribe struct { } type UserSubscribeDetail struct { - Id int64 `json:"id"` - UserId int64 `json:"user_id"` - User User `json:"user"` - 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"` - GroupLocked bool `json:"group_locked"` - StartTime int64 `json:"start_time"` - ExpireTime int64 `json:"expire_time"` - ResetTime int64 `json:"reset_time"` - Traffic int64 `json:"traffic"` - Download int64 `json:"download"` - Upload int64 `json:"upload"` - Token string `json:"token"` - Status uint8 `json:"status"` - 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"` + Id int64 `json:"id"` + UserId int64 `json:"user_id"` + User User `json:"user"` + 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"` + GroupLocked bool `json:"group_locked"` + StartTime int64 `json:"start_time"` + ExpireTime int64 `json:"expire_time"` + ResetTime int64 `json:"reset_time"` + Traffic int64 `json:"traffic"` + Download int64 `json:"download"` + Upload int64 `json:"upload"` + SpeedLimit int64 `json:"speed_limit"` + TrafficLimit []TrafficLimit `json:"user_traffic_limit"` + PlanSpeedLimit int64 `json:"plan_speed_limit"` + Token string `json:"token"` + Status uint8 `json:"status"` + 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"` } type UserSubscribeInfo struct {