Compare commits
11 Commits
429e535dd4
...
d94cbc09b0
| Author | SHA1 | Date | |
|---|---|---|---|
| d94cbc09b0 | |||
|
|
0e7cbf4396 | ||
|
|
5d632608ab | ||
|
|
338d962618 | ||
|
|
f1794b26b1 | ||
|
|
4cd24e7600 | ||
|
|
e18809f9b7 | ||
|
|
143445a2fc | ||
|
|
7277438b07 | ||
|
|
5c2d0be8e2 | ||
| a52c7142ee |
@ -127,12 +127,12 @@ func (adapter *Adapter) Proxies(servers []*node.Node) ([]Proxy, error) {
|
|||||||
HopPorts: protocol.HopPorts,
|
HopPorts: protocol.HopPorts,
|
||||||
HopInterval: protocol.HopInterval,
|
HopInterval: protocol.HopInterval,
|
||||||
ObfsPassword: protocol.ObfsPassword,
|
ObfsPassword: protocol.ObfsPassword,
|
||||||
|
UpMbps: protocol.UpMbps,
|
||||||
|
DownMbps: protocol.DownMbps,
|
||||||
DisableSNI: protocol.DisableSNI,
|
DisableSNI: protocol.DisableSNI,
|
||||||
ReduceRtt: protocol.ReduceRtt,
|
ReduceRtt: protocol.ReduceRtt,
|
||||||
UDPRelayMode: protocol.UDPRelayMode,
|
UDPRelayMode: protocol.UDPRelayMode,
|
||||||
CongestionController: protocol.CongestionController,
|
CongestionController: protocol.CongestionController,
|
||||||
UpMbps: protocol.UpMbps,
|
|
||||||
DownMbps: protocol.DownMbps,
|
|
||||||
PaddingScheme: protocol.PaddingScheme,
|
PaddingScheme: protocol.PaddingScheme,
|
||||||
Multiplex: protocol.Multiplex,
|
Multiplex: protocol.Multiplex,
|
||||||
XhttpMode: protocol.XhttpMode,
|
XhttpMode: protocol.XhttpMode,
|
||||||
@ -145,6 +145,10 @@ func (adapter *Adapter) Proxies(servers []*node.Node) ([]Proxy, error) {
|
|||||||
EncryptionPrivateKey: protocol.EncryptionPrivateKey,
|
EncryptionPrivateKey: protocol.EncryptionPrivateKey,
|
||||||
EncryptionClientPadding: protocol.EncryptionClientPadding,
|
EncryptionClientPadding: protocol.EncryptionClientPadding,
|
||||||
EncryptionPassword: protocol.EncryptionPassword,
|
EncryptionPassword: protocol.EncryptionPassword,
|
||||||
|
Ratio: protocol.Ratio,
|
||||||
|
CertMode: protocol.CertMode,
|
||||||
|
CertDNSProvider: protocol.CertDNSProvider,
|
||||||
|
CertDNSEnv: protocol.CertDNSEnv,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,6 +111,28 @@ type (
|
|||||||
UpdateUserRulesRequest {
|
UpdateUserRulesRequest {
|
||||||
Rules []string `json:"rules" validate:"required"`
|
Rules []string `json:"rules" validate:"required"`
|
||||||
}
|
}
|
||||||
|
CommissionWithdrawRequest {
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
WithdrawalLog {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId int64 `json:"user_id"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
QueryWithdrawalLogListRequest {
|
||||||
|
Page int `form:"page"`
|
||||||
|
Size int `form:"size"`
|
||||||
|
}
|
||||||
|
QueryWithdrawalLogListResponse {
|
||||||
|
List []WithdrawalLog `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@server (
|
@server (
|
||||||
@ -222,5 +244,13 @@ service ppanel {
|
|||||||
@doc "Update User Rules"
|
@doc "Update User Rules"
|
||||||
@handler UpdateUserRules
|
@handler UpdateUserRules
|
||||||
put /rules (UpdateUserRulesRequest)
|
put /rules (UpdateUserRulesRequest)
|
||||||
|
|
||||||
|
@doc "Commission Withdraw"
|
||||||
|
@handler CommissionWithdraw
|
||||||
|
post /commission_withdraw (CommissionWithdrawRequest) returns (WithdrawalLog)
|
||||||
|
|
||||||
|
@doc "Query Withdrawal Log"
|
||||||
|
@handler QueryWithdrawalLog
|
||||||
|
get /withdrawal_log (QueryWithdrawalLogListRequest) returns (QueryWithdrawalLogListResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -183,9 +183,10 @@ type (
|
|||||||
Rules []string `json:"rules"`
|
Rules []string `json:"rules"`
|
||||||
}
|
}
|
||||||
InviteConfig {
|
InviteConfig {
|
||||||
ForcedInvite bool `json:"forced_invite"`
|
ForcedInvite bool `json:"forced_invite"`
|
||||||
ReferralPercentage int64 `json:"referral_percentage"`
|
FirstPurchasePercentage int64 `json:"first_purchase_percentage"`
|
||||||
OnlyFirstPurchase bool `json:"only_first_purchase"`
|
FirstYearlyPurchasePercentage int64 `json:"first_yearly_purchase_percentage"`
|
||||||
|
NonFirstPurchasePercentage int64 `json:"non_first_purchase_percentage"`
|
||||||
}
|
}
|
||||||
TelegramConfig {
|
TelegramConfig {
|
||||||
TelegramBotToken string `json:"telegram_bot_token"`
|
TelegramBotToken string `json:"telegram_bot_token"`
|
||||||
@ -656,7 +657,7 @@ type (
|
|||||||
// public announcement
|
// public announcement
|
||||||
QueryAnnouncementRequest {
|
QueryAnnouncementRequest {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size,default=15"`
|
Size int `form:"size"`
|
||||||
Pinned *bool `form:"pinned"`
|
Pinned *bool `form:"pinned"`
|
||||||
Popup *bool `form:"popup"`
|
Popup *bool `form:"popup"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
Host: 0.0.0.0
|
||||||
|
Port: 8080
|
||||||
|
Debug: false
|
||||||
|
JwtAuth:
|
||||||
|
AccessSecret: 1234567890
|
||||||
|
AccessExpire: 604800
|
||||||
|
Logger:
|
||||||
|
ServiceName: PPanel
|
||||||
|
Mode: console
|
||||||
|
Encoding: plain
|
||||||
|
TimeFormat: '2025-01-01 00:00:00.000'
|
||||||
|
Path: logs
|
||||||
|
Level: debug
|
||||||
|
MaxContentLength: 0
|
||||||
|
Compress: false
|
||||||
|
Stat: true
|
||||||
|
KeepDays: 0
|
||||||
|
StackCooldownMillis: 100
|
||||||
|
MaxBackups: 0
|
||||||
|
MaxSize: 0
|
||||||
|
Rotation: daily
|
||||||
|
FileTimeFormat: 2025-01-01T00:00:00.000Z00:00
|
||||||
|
MySQL:
|
||||||
|
Addr: 172.245.180.199:3306
|
||||||
|
Dbname: ppanel
|
||||||
|
Username: ppanel
|
||||||
|
Password: ppanelpassword
|
||||||
|
Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
|
||||||
|
MaxIdleConns: 10
|
||||||
|
MaxOpenConns: 10
|
||||||
|
SlowThreshold: 1000
|
||||||
|
Redis:
|
||||||
|
Host: ppanel-cache:6379
|
||||||
|
Pass:
|
||||||
|
DB: 0
|
||||||
|
Administrator:
|
||||||
|
Password: password
|
||||||
|
Email: admin@ppanel.dev
|
||||||
@ -90,11 +90,15 @@ VALUES (1, 'site', 'SiteLogo', '/favicon.svg', 'string', 'Site Logo', '2025-04-2
|
|||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
(23, 'invite', 'ForcedInvite', 'false', 'bool', 'Forced invite', '2025-04-22 14:25:16.640',
|
(23, 'invite', 'ForcedInvite', 'false', 'bool', 'Forced invite', '2025-04-22 14:25:16.640',
|
||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
(24, 'invite', 'ReferralPercentage', '20', 'int', 'Referral percentage', '2025-04-22 14:25:16.640',
|
(24, 'invite', 'FirstPurchasePercentage', '20', 'int', 'First purchase commission percentage', '2025-04-22 14:25:16.640',
|
||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
(25, 'invite', 'OnlyFirstPurchase', 'false', 'bool', 'Only first purchase', '2025-04-22 14:25:16.640',
|
(25, 'invite', 'NonFirstPurchasePercentage', '10', 'int', 'Non-first purchase commission percentage', '2025-04-22 14:25:16.640',
|
||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
(26, 'register', 'StopRegister', 'false', 'bool', 'is stop register', '2025-04-22 14:25:16.640',
|
(26, 'invite', 'ForcedInvite', 'false', 'bool', 'Forced invite', '2025-04-22 14:25:16.640',
|
||||||
|
'2025-04-22 14:25:16.640'),
|
||||||
|
(42, 'invite', 'FirstYearlyPurchasePercentage', '25', 'int', 'First yearly purchase commission percentage', '2025-04-22 14:25:16.640',
|
||||||
|
'2025-04-22 14:25:16.640'),
|
||||||
|
(27, 'register', 'StopRegister', 'false', 'bool', 'is stop register', '2025-04-22 14:25:16.640',
|
||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
(27, 'register', 'EnableTrial', 'false', 'bool', 'is enable trial', '2025-04-22 14:25:16.640',
|
(27, 'register', 'EnableTrial', 'false', 'bool', 'is enable trial', '2025-04-22 14:25:16.640',
|
||||||
'2025-04-22 14:25:16.640'),
|
'2025-04-22 14:25:16.640'),
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
DROP TABLE IF EXISTS `withdrawals`;
|
||||||
|
|
||||||
|
DELETE FROM `system`
|
||||||
|
WHERE `category` = 'invite'
|
||||||
|
AND `key` = 'WithdrawalMethod';
|
||||||
16
initialize/migrate/database/02121_user_withdrawal.up.sql
Normal file
16
initialize/migrate/database/02121_user_withdrawal.up.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `withdrawals` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
|
||||||
|
`user_id` BIGINT NOT NULL COMMENT 'User ID',
|
||||||
|
`amount` BIGINT NOT NULL COMMENT 'Withdrawal Amount',
|
||||||
|
`content` TEXT COMMENT 'Withdrawal Content',
|
||||||
|
`status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Withdrawal Status',
|
||||||
|
`reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT 'Rejection Reason',
|
||||||
|
`created_at` DATETIME NOT NULL COMMENT 'Creation Time',
|
||||||
|
`updated_at` DATETIME NOT NULL COMMENT 'Update Time',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_user_id` (`user_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
|
||||||
|
VALUES
|
||||||
|
('invite', 'WithdrawalMethod', '', 'string', 'withdrawal method', '2025-04-22 14:25:16.637', '2025-04-22 14:25:16.637');
|
||||||
@ -200,9 +200,10 @@ type File struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InviteConfig struct {
|
type InviteConfig struct {
|
||||||
ForcedInvite bool `yaml:"ForcedInvite" default:"false"`
|
ForcedInvite bool `yaml:"ForcedInvite" default:"false"`
|
||||||
ReferralPercentage int64 `yaml:"ReferralPercentage" default:"0"`
|
FirstPurchasePercentage int64 `yaml:"FirstPurchasePercentage" default:"20"`
|
||||||
OnlyFirstPurchase bool `yaml:"OnlyFirstPurchase" default:"false"`
|
FirstYearlyPurchasePercentage int64 `yaml:"FirstYearlyPurchasePercentage" default:"25"`
|
||||||
|
NonFirstPurchasePercentage int64 `yaml:"NonFirstPurchasePercentage" default:"10"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Telegram struct {
|
type Telegram struct {
|
||||||
|
|||||||
26
internal/handler/public/user/commissionWithdrawHandler.go
Normal file
26
internal/handler/public/user/commissionWithdrawHandler.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/public/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commission Withdraw
|
||||||
|
func CommissionWithdrawHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.CommissionWithdrawRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := user.NewCommissionWithdrawLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.CommissionWithdraw(&req)
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
internal/handler/public/user/queryWithdrawalLogHandler.go
Normal file
26
internal/handler/public/user/queryWithdrawalLogHandler.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/public/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query Withdrawal Log
|
||||||
|
func QueryWithdrawalLogHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var req types.QueryWithdrawalLogListRequest
|
||||||
|
_ = c.ShouldBind(&req)
|
||||||
|
validateErr := svcCtx.Validate(&req)
|
||||||
|
if validateErr != nil {
|
||||||
|
result.ParamErrorResult(c, validateErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := user.NewQueryWithdrawalLogLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.QueryWithdrawalLog(&req)
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -807,6 +807,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
// Query User Commission Log
|
// Query User Commission Log
|
||||||
publicUserGroupRouter.GET("/commission_log", publicUser.QueryUserCommissionLogHandler(serverCtx))
|
publicUserGroupRouter.GET("/commission_log", publicUser.QueryUserCommissionLogHandler(serverCtx))
|
||||||
|
|
||||||
|
// Commission Withdraw
|
||||||
|
publicUserGroupRouter.POST("/commission_withdraw", publicUser.CommissionWithdrawHandler(serverCtx))
|
||||||
|
|
||||||
// Get Device List
|
// Get Device List
|
||||||
publicUserGroupRouter.GET("/devices", publicUser.GetDeviceListHandler(serverCtx))
|
publicUserGroupRouter.GET("/devices", publicUser.GetDeviceListHandler(serverCtx))
|
||||||
|
|
||||||
@ -857,6 +860,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
|
|
||||||
// Verify Email
|
// Verify Email
|
||||||
publicUserGroupRouter.POST("/verify_email", publicUser.VerifyEmailHandler(serverCtx))
|
publicUserGroupRouter.POST("/verify_email", publicUser.VerifyEmailHandler(serverCtx))
|
||||||
|
|
||||||
|
// Query Withdrawal Log
|
||||||
|
publicUserGroupRouter.GET("/withdrawal_log", publicUser.QueryWithdrawalLogHandler(serverCtx))
|
||||||
}
|
}
|
||||||
|
|
||||||
serverGroupRouter := router.Group("/v1/server")
|
serverGroupRouter := router.Group("/v1/server")
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/report"
|
||||||
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/payment"
|
"github.com/perfect-panel/server/internal/model/payment"
|
||||||
@ -43,15 +44,31 @@ func (l *GetPaymentMethodListLogic) GetPaymentMethodList(req *types.GetPaymentMe
|
|||||||
Total: total,
|
Total: total,
|
||||||
List: make([]types.PaymentMethodDetail, len(list)),
|
List: make([]types.PaymentMethodDetail, len(list)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gateway mod
|
||||||
|
|
||||||
|
isGatewayMod := report.IsGatewayMode()
|
||||||
|
|
||||||
for i, v := range list {
|
for i, v := range list {
|
||||||
config := make(map[string]interface{})
|
config := make(map[string]interface{})
|
||||||
_ = json.Unmarshal([]byte(v.Config), &config)
|
_ = json.Unmarshal([]byte(v.Config), &config)
|
||||||
notifyUrl := ""
|
notifyUrl := ""
|
||||||
|
|
||||||
if paymentPlatform.ParsePlatform(v.Platform) != paymentPlatform.Balance {
|
if paymentPlatform.ParsePlatform(v.Platform) != paymentPlatform.Balance {
|
||||||
|
notifyUrl = v.Domain
|
||||||
if v.Domain != "" {
|
if v.Domain != "" {
|
||||||
notifyUrl = v.Domain + "/v1/notify/" + v.Platform + "/" + v.Token
|
// if is gateway mod, use gateway domain
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api/"
|
||||||
|
}
|
||||||
|
notifyUrl += "/v1/notify/" + v.Platform + "/" + v.Token
|
||||||
} else {
|
} else {
|
||||||
notifyUrl = "https://" + l.svcCtx.Config.Host + "/v1/notify/" + v.Platform + "/" + v.Token
|
notifyUrl += "https://" + l.svcCtx.Config.Host
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api/v1/notify/" + v.Platform + "/" + v.Token
|
||||||
|
} else {
|
||||||
|
notifyUrl += "/v1/notify/" + v.Platform + "/" + v.Token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp.List[i] = types.PaymentMethodDetail{
|
resp.List[i] = types.PaymentMethodDetail{
|
||||||
|
|||||||
@ -120,10 +120,11 @@ func (l *GetStatLogic) GetStat() (resp *types.GetStatResponse, err error) {
|
|||||||
protocol = append(protocol, p)
|
protocol = append(protocol, p)
|
||||||
}
|
}
|
||||||
resp = &types.GetStatResponse{
|
resp = &types.GetStatResponse{
|
||||||
User: u,
|
User: u,
|
||||||
Node: n,
|
Node: n,
|
||||||
Country: int64(len(country)),
|
Country: int64(len(country)),
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
|
OnlineDevice: l.svcCtx.DeviceManager.GetOnlineDeviceCount(),
|
||||||
}
|
}
|
||||||
val, _ := json.Marshal(*resp)
|
val, _ := json.Marshal(*resp)
|
||||||
_ = l.svcCtx.Redis.Set(l.ctx, config.CommonStatCacheKey, string(val), time.Duration(3600)*time.Second).Err()
|
_ = l.svcCtx.Redis.Set(l.ctx, config.CommonStatCacheKey, string(val), time.Duration(3600)*time.Second).Err()
|
||||||
|
|||||||
@ -50,10 +50,26 @@ func (l *GetSubscriptionLogic) GetSubscription(req *types.GetSubscriptionRequest
|
|||||||
tool.DeepCopy(&sub, item)
|
tool.DeepCopy(&sub, item)
|
||||||
if item.Discount != "" {
|
if item.Discount != "" {
|
||||||
var discount []types.SubscribeDiscount
|
var discount []types.SubscribeDiscount
|
||||||
|
|
||||||
_ = json.Unmarshal([]byte(item.Discount), &discount)
|
_ = json.Unmarshal([]byte(item.Discount), &discount)
|
||||||
sub.Discount = discount
|
sub.Discount = discount
|
||||||
list[i] = sub
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算节点数量(通过服务组查询关联的实际节点数量)
|
||||||
|
if item.ServerGroup != "" {
|
||||||
|
// 获取服务组ID列表
|
||||||
|
groupIds := tool.StringToInt64Slice(item.ServerGroup)
|
||||||
|
|
||||||
|
// 通过服务组查询关联的节点数量
|
||||||
|
servers, err := l.svcCtx.ServerModel.FindServerListByGroupIds(l.ctx, groupIds)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[Site GetSubscription] FindServerListByGroupIds error", logger.Field("error", err.Error()))
|
||||||
|
sub.ServerCount = 0
|
||||||
|
} else {
|
||||||
|
sub.ServerCount = int64(len(servers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list[i] = sub
|
list[i] = sub
|
||||||
}
|
}
|
||||||
resp.List = list
|
resp.List = list
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/log"
|
"github.com/perfect-panel/server/internal/model/log"
|
||||||
|
"github.com/perfect-panel/server/internal/report"
|
||||||
"github.com/perfect-panel/server/pkg/constant"
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
|
|
||||||
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
||||||
@ -275,16 +276,29 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gateway mod
|
||||||
|
|
||||||
|
isGatewayMod := report.IsGatewayMode()
|
||||||
|
|
||||||
// Build notification URL for payment status callbacks
|
// Build notification URL for payment status callbacks
|
||||||
notifyUrl := ""
|
notifyUrl := ""
|
||||||
if config.Domain != "" {
|
if config.Domain != "" {
|
||||||
notifyUrl = config.Domain + "/v1/notify/" + config.Platform + "/" + config.Token
|
notifyUrl = config.Domain
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api/"
|
||||||
|
}
|
||||||
|
notifyUrl = notifyUrl + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||||
} else {
|
} else {
|
||||||
host, ok := l.ctx.Value(constant.CtxKeyRequestHost).(string)
|
host, ok := l.ctx.Value(constant.CtxKeyRequestHost).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
host = l.svcCtx.Config.Host
|
host = l.svcCtx.Config.Host
|
||||||
}
|
}
|
||||||
notifyUrl = "https://" + host + "/v1/notify/" + config.Platform + "/" + config.Token
|
|
||||||
|
notifyUrl = "https://" + host
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api"
|
||||||
|
}
|
||||||
|
notifyUrl = notifyUrl + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create payment URL for user redirection
|
// Create payment URL for user redirection
|
||||||
@ -317,18 +331,29 @@ func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gateway mod
|
||||||
|
isGatewayMod := report.IsGatewayMode()
|
||||||
|
|
||||||
// Build notification URL for payment status callbacks
|
// Build notification URL for payment status callbacks
|
||||||
notifyUrl := ""
|
notifyUrl := ""
|
||||||
if config.Domain != "" {
|
if config.Domain != "" {
|
||||||
notifyUrl = config.Domain + "/v1/notify/" + config.Platform + "/" + config.Token
|
notifyUrl = config.Domain
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api/"
|
||||||
|
}
|
||||||
|
notifyUrl = notifyUrl + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||||
} else {
|
} else {
|
||||||
host, ok := l.ctx.Value(constant.CtxKeyRequestHost).(string)
|
host, ok := l.ctx.Value(constant.CtxKeyRequestHost).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
host = l.svcCtx.Config.Host
|
host = l.svcCtx.Config.Host
|
||||||
}
|
}
|
||||||
notifyUrl = "https://" + host + "/v1/notify/" + config.Platform + "/" + config.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
|
notifyUrl = "https://" + host
|
||||||
|
if isGatewayMod {
|
||||||
|
notifyUrl += "/api"
|
||||||
|
}
|
||||||
|
notifyUrl = notifyUrl + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||||
|
}
|
||||||
// Create payment URL for user redirection
|
// Create payment URL for user redirection
|
||||||
url := client.CreatePayUrl(epay.Order{
|
url := client.CreatePayUrl(epay.Order{
|
||||||
Name: l.svcCtx.Config.Site.SiteName,
|
Name: l.svcCtx.Config.Site.SiteName,
|
||||||
|
|||||||
@ -48,6 +48,7 @@ func (l *QuerySubscribeListLogic) QuerySubscribeList(req *types.QuerySubscribeLi
|
|||||||
list := make([]types.Subscribe, len(data))
|
list := make([]types.Subscribe, len(data))
|
||||||
for i, item := range data {
|
for i, item := range data {
|
||||||
var sub types.Subscribe
|
var sub types.Subscribe
|
||||||
|
|
||||||
tool.DeepCopy(&sub, item)
|
tool.DeepCopy(&sub, item)
|
||||||
if item.Discount != "" {
|
if item.Discount != "" {
|
||||||
var discount []types.SubscribeDiscount
|
var discount []types.SubscribeDiscount
|
||||||
@ -56,6 +57,15 @@ func (l *QuerySubscribeListLogic) QuerySubscribeList(req *types.QuerySubscribeLi
|
|||||||
list[i] = sub
|
list[i] = sub
|
||||||
}
|
}
|
||||||
list[i] = sub
|
list[i] = sub
|
||||||
|
// 通过服务组查询关联的节点数量
|
||||||
|
servers, err := l.svcCtx.ServerModel.FindServerListByGroupIds(l.ctx, sub.ServerGroup)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[QuerySubscribeListLogic] FindServerListByGroupIds error", logger.Field("error", err.Error()))
|
||||||
|
sub.ServerCount = 0
|
||||||
|
} else {
|
||||||
|
sub.ServerCount = int64(len(servers))
|
||||||
|
}
|
||||||
|
list[i] = sub
|
||||||
}
|
}
|
||||||
resp.List = list
|
resp.List = list
|
||||||
return
|
return
|
||||||
|
|||||||
108
internal/logic/public/user/commissionWithdrawLogic.go
Normal file
108
internal/logic/public/user/commissionWithdrawLogic.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/model/log"
|
||||||
|
"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/constant"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommissionWithdrawLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commission Withdraw
|
||||||
|
func NewCommissionWithdrawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommissionWithdrawLogic {
|
||||||
|
return &CommissionWithdrawLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CommissionWithdrawLogic) CommissionWithdraw(req *types.CommissionWithdrawRequest) (resp *types.WithdrawalLog, err error) {
|
||||||
|
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
|
||||||
|
if !ok {
|
||||||
|
logger.Error("current user is not found in context")
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Commission < req.Amount {
|
||||||
|
logger.Errorf("User %d has insufficient commission balance: %.2f, requested: %.2f", u.Id, float64(u.Commission)/100, float64(req.Amount)/100)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserCommissionNotEnough), "User %d has insufficient commission balance", u.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := l.svcCtx.DB.WithContext(l.ctx).Begin()
|
||||||
|
|
||||||
|
// update user commission balance
|
||||||
|
u.Commission -= req.Amount
|
||||||
|
if err = l.svcCtx.UserModel.Update(l.ctx, u, tx); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
l.Errorf("Failed to update user %d commission balance: %v", u.Id, err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "Failed to update user %d commission balance: %v", u.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create withdrawal log
|
||||||
|
logInfo := log.Commission{
|
||||||
|
Type: log.CommissionTypeConvertBalance,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Timestamp: time.Now().UnixMilli(),
|
||||||
|
}
|
||||||
|
b, err := logInfo.Marshal()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
l.Errorf("Failed to marshal commission log for user %d: %v", u.Id, err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Failed to marshal commission log for user %d: %v", u.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Model(log.SystemLog{}).Create(&log.SystemLog{
|
||||||
|
Type: log.TypeCommission.Uint8(),
|
||||||
|
Date: time.Now().Format("2006-01-02"),
|
||||||
|
ObjectID: u.Id,
|
||||||
|
Content: string(b),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
l.Errorf("Failed to create commission log for user %d: %v", u.Id, err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Failed to create commission log for user %d: %v", u.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Model(&user.Withdrawal{}).Create(&user.Withdrawal{
|
||||||
|
UserId: u.Id,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Content: req.Content,
|
||||||
|
Status: 0,
|
||||||
|
Reason: "",
|
||||||
|
}).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
l.Errorf("Failed to create withdrawal log for user %d: %v", u.Id, err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "Failed to create withdrawal log for user %d: %v", u.Id, err)
|
||||||
|
}
|
||||||
|
if err = tx.Commit().Error; err != nil {
|
||||||
|
l.Errorf("Transaction commit failed for user %d withdrawal: %v", u.Id, err)
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Transaction commit failed for user %d withdrawal: %v", u.Id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.WithdrawalLog{
|
||||||
|
UserId: u.Id,
|
||||||
|
Amount: req.Amount,
|
||||||
|
Content: req.Content,
|
||||||
|
Status: 0,
|
||||||
|
Reason: "",
|
||||||
|
CreatedAt: time.Now().UnixMilli(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
30
internal/logic/public/user/queryWithdrawalLogLogic.go
Normal file
30
internal/logic/public/user/queryWithdrawalLogLogic.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryWithdrawalLogLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQueryWithdrawalLogLogic Query Withdrawal Log
|
||||||
|
func NewQueryWithdrawalLogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryWithdrawalLogLogic {
|
||||||
|
return &QueryWithdrawalLogLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryWithdrawalLogLogic) QueryWithdrawalLog(req *types.QueryWithdrawalLogListRequest) (resp *types.QueryWithdrawalLogListResponse, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -33,23 +33,24 @@ const (
|
|||||||
TypeTrafficStat Type = 42 // Daily traffic statistics log
|
TypeTrafficStat Type = 42 // Daily traffic statistics log
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
ResetSubscribeTypeAuto uint16 = 231 // Auto reset
|
ResetSubscribeTypeAuto uint16 = 231 // Auto reset
|
||||||
ResetSubscribeTypeAdvance uint16 = 232 // Advance reset
|
ResetSubscribeTypeAdvance uint16 = 232 // Advance reset
|
||||||
ResetSubscribeTypePaid uint16 = 233 // Paid reset
|
ResetSubscribeTypePaid uint16 = 233 // Paid reset
|
||||||
ResetSubscribeTypeQuota uint16 = 234 // Quota reset
|
ResetSubscribeTypeQuota uint16 = 234 // Quota reset
|
||||||
BalanceTypeRecharge uint16 = 321 // Recharge
|
BalanceTypeRecharge uint16 = 321 // Recharge
|
||||||
BalanceTypeWithdraw uint16 = 322 // Withdraw
|
BalanceTypeWithdraw uint16 = 322 // Withdraw
|
||||||
BalanceTypePayment uint16 = 323 // Payment
|
BalanceTypePayment uint16 = 323 // Payment
|
||||||
BalanceTypeRefund uint16 = 324 // Refund
|
BalanceTypeRefund uint16 = 324 // Refund
|
||||||
BalanceTypeAdjust uint16 = 326 // Admin Adjust
|
BalanceTypeAdjust uint16 = 326 // Admin Adjust
|
||||||
BalanceTypeReward uint16 = 325 // Reward
|
BalanceTypeReward uint16 = 325 // Reward
|
||||||
CommissionTypePurchase uint16 = 331 // Purchase
|
CommissionTypePurchase uint16 = 331 // Purchase
|
||||||
CommissionTypeRenewal uint16 = 332 // Renewal
|
CommissionTypeRenewal uint16 = 332 // Renewal
|
||||||
CommissionTypeRefund uint16 = 333 // Refund
|
CommissionTypeRefund uint16 = 333 // Refund
|
||||||
commissionTypeWithdraw uint16 = 334 // withdraw
|
CommissionTypeWithdraw uint16 = 334 // withdraw
|
||||||
CommissionTypeAdjust uint16 = 335 // Admin Adjust
|
CommissionTypeAdjust uint16 = 335 // Admin Adjust
|
||||||
GiftTypeIncrease uint16 = 341 // Increase
|
CommissionTypeConvertBalance uint16 = 336 // Convert to Balance
|
||||||
GiftTypeReduce uint16 = 342 // Reduce
|
GiftTypeIncrease uint16 = 341 // Increase
|
||||||
|
GiftTypeReduce uint16 = 342 // Reduce
|
||||||
)
|
)
|
||||||
|
|
||||||
// Uint8 converts Type to uint8.
|
// Uint8 converts Type to uint8.
|
||||||
|
|||||||
@ -88,6 +88,7 @@ func (m *defaultUserModel) QueryUserSubscribe(ctx context.Context, userId int64,
|
|||||||
// 订阅过期时间大于当前时间或者订阅结束时间大于当前时间
|
// 订阅过期时间大于当前时间或者订阅结束时间大于当前时间
|
||||||
return conn.Where("`expire_time` > ? OR `finished_at` >= ? OR `expire_time` = ?", now, sevenDaysAgo, time.UnixMilli(0)).
|
return conn.Where("`expire_time` > ? OR `finished_at` >= ? OR `expire_time` = ?", now, sevenDaysAgo, time.UnixMilli(0)).
|
||||||
Preload("Subscribe").
|
Preload("Subscribe").
|
||||||
|
Order("created_at DESC").
|
||||||
Find(&list).Error
|
Find(&list).Error
|
||||||
})
|
})
|
||||||
return list, err
|
return list, err
|
||||||
|
|||||||
@ -102,3 +102,18 @@ type DeviceOnlineRecord struct {
|
|||||||
func (DeviceOnlineRecord) TableName() string {
|
func (DeviceOnlineRecord) TableName() string {
|
||||||
return "user_device_online_record"
|
return "user_device_online_record"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Withdrawal struct {
|
||||||
|
Id int64 `gorm:"primaryKey"`
|
||||||
|
UserId int64 `gorm:"index:idx_user_id;not null;comment:User ID"`
|
||||||
|
Amount int64 `gorm:"not null;comment:Withdrawal Amount"`
|
||||||
|
Content string `gorm:"type:text;comment:Withdrawal Content"`
|
||||||
|
Status uint8 `gorm:"type:tinyint(1);default:0;comment:Withdrawal Status: 0: Pending 1: Approved 2: Rejected"`
|
||||||
|
Reason string `gorm:"type:varchar(500);default:'';comment:Rejection Reason"`
|
||||||
|
CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"`
|
||||||
|
UpdatedAt time.Time `gorm:"comment:Update Time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Withdrawal) TableName() string {
|
||||||
|
return "user_withdrawal"
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,10 @@ const (
|
|||||||
|
|
||||||
// RegisterResponse 模块注册响应参数
|
// RegisterResponse 模块注册响应参数
|
||||||
type RegisterResponse struct {
|
type RegisterResponse struct {
|
||||||
Success bool `json:"success"` // 注册是否成功
|
Code int `json:"code"` // 响应代码
|
||||||
Message string `json:"message"` // 返回信息
|
Message string `json:"message"` // 响应信息
|
||||||
|
Data struct {
|
||||||
|
Success bool `json:"success"` // 注册是否成功
|
||||||
|
Message string `json:"message"` // 返回信息
|
||||||
|
} `json:"data"` // 响应数据
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,7 @@ func RegisterModule(port int) error {
|
|||||||
// 从环境变量中读取网关模块端口
|
// 从环境变量中读取网关模块端口
|
||||||
gatewayPort, err := GatewayPort()
|
gatewayPort, err := GatewayPort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to determine GATEWAY_PORT: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +83,7 @@ func RegisterModule(port int) error {
|
|||||||
}).SetResult(&response).Post(RegisterAPI)
|
}).SetResult(&response).Post(RegisterAPI)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Errorf("Failed to register service: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +91,8 @@ func RegisterModule(port int) error {
|
|||||||
return errors.New("failed to register module: " + result.Status())
|
return errors.New("failed to register module: " + result.Status())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !response.Success {
|
if !response.Data.Success {
|
||||||
|
logger.Infof("Result: %v", result.String())
|
||||||
return errors.New("failed to register module: " + response.Message)
|
return errors.New("failed to register module: " + response.Message)
|
||||||
}
|
}
|
||||||
logger.Infof("Module registered successfully: %s", response.Message)
|
logger.Infof("Module registered successfully: %s", response.Message)
|
||||||
|
|||||||
@ -1,11 +1,5 @@
|
|||||||
package report
|
package report
|
||||||
|
|
||||||
// RegisterServiceResponse 模块注册请求参数
|
|
||||||
type RegisterServiceResponse struct {
|
|
||||||
Success bool `json:"success"` // 注册是否成功
|
|
||||||
Message string `json:"message"` // 返回信息
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegisterServiceRequest struct {
|
type RegisterServiceRequest struct {
|
||||||
Secret string `json:"secret"` // 通讯密钥
|
Secret string `json:"secret"` // 通讯密钥
|
||||||
ProxyPath string `json:"proxy_path"` // 代理路径
|
ProxyPath string `json:"proxy_path"` // 代理路径
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/report"
|
"github.com/perfect-panel/server/internal/report"
|
||||||
@ -86,7 +87,7 @@ func (m *Service) Start() {
|
|||||||
err = report.RegisterModule(port)
|
err = report.RegisterModule(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("register module error: %s", err.Error())
|
logger.Errorf("register module error: %s", err.Error())
|
||||||
panic(err)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
logger.Infof("module registered on port %d", port)
|
logger.Infof("module registered on port %d", port)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -239,6 +239,11 @@ type CommissionLog struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommissionWithdrawRequest struct {
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type Coupon struct {
|
type Coupon struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -1723,6 +1728,16 @@ type QueryUserSubscribeNodeListResponse struct {
|
|||||||
List []UserSubscribeInfo `json:"list"`
|
List []UserSubscribeInfo `json:"list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryWithdrawalLogListRequest struct {
|
||||||
|
Page int `form:"page"`
|
||||||
|
Size int `form:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryWithdrawalLogListResponse struct {
|
||||||
|
List []WithdrawalLog `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
type QuotaTask struct {
|
type QuotaTask struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Subscribers []int64 `json:"subscribers"`
|
Subscribers []int64 `json:"subscribers"`
|
||||||
@ -2766,3 +2781,14 @@ type VmessProtocol struct {
|
|||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
Transport string `json:"transport"`
|
Transport string `json:"transport"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WithdrawalLog struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId int64 `json:"user_id"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|||||||
@ -340,6 +340,11 @@ func (dm *DeviceManager) Broadcast(message string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOnlineDeviceCount returns the total number of online devices
|
||||||
|
func (dm *DeviceManager) GetOnlineDeviceCount() int64 {
|
||||||
|
return int64(atomic.LoadInt32(&dm.totalOnline))
|
||||||
|
}
|
||||||
|
|
||||||
// Gracefully shut down all WebSocket connections
|
// Gracefully shut down all WebSocket connections
|
||||||
func (dm *DeviceManager) Shutdown(ctx context.Context) {
|
func (dm *DeviceManager) Shutdown(ctx context.Context) {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|||||||
@ -18,15 +18,16 @@ const (
|
|||||||
|
|
||||||
// User error
|
// User error
|
||||||
const (
|
const (
|
||||||
UserExist uint32 = 20001
|
UserExist uint32 = 20001
|
||||||
UserNotExist uint32 = 20002
|
UserNotExist uint32 = 20002
|
||||||
UserPasswordError uint32 = 20003
|
UserPasswordError uint32 = 20003
|
||||||
UserDisabled uint32 = 20004
|
UserDisabled uint32 = 20004
|
||||||
InsufficientBalance uint32 = 20005
|
InsufficientBalance uint32 = 20005
|
||||||
StopRegister uint32 = 20006
|
StopRegister uint32 = 20006
|
||||||
TelegramNotBound uint32 = 20007
|
TelegramNotBound uint32 = 20007
|
||||||
UserNotBindOauth uint32 = 20008
|
UserNotBindOauth uint32 = 20008
|
||||||
InviteCodeError uint32 = 20009
|
InviteCodeError uint32 = 20009
|
||||||
|
UserCommissionNotEnough uint32 = 20010
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node error
|
// Node error
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user