diff --git a/apis/public/user.api b/apis/public/user.api index 45ebdef..f37eef3 100644 --- a/apis/public/user.api +++ b/apis/public/user.api @@ -111,6 +111,28 @@ type ( UpdateUserRulesRequest { 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 ( @@ -222,5 +244,13 @@ service ppanel { @doc "Update User Rules" @handler UpdateUserRules 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) } diff --git a/initialize/migrate/database/02121_user_withdrawal.down.sql b/initialize/migrate/database/02121_user_withdrawal.down.sql new file mode 100644 index 0000000..4de8bc5 --- /dev/null +++ b/initialize/migrate/database/02121_user_withdrawal.down.sql @@ -0,0 +1,5 @@ +DROP TABLE IF EXISTS `withdrawals`; + +DELETE FROM `system` +WHERE `category` = 'invite' + AND `key` = 'WithdrawalMethod'; diff --git a/initialize/migrate/database/02121_user_withdrawal.up.sql b/initialize/migrate/database/02121_user_withdrawal.up.sql new file mode 100644 index 0000000..4f39e1e --- /dev/null +++ b/initialize/migrate/database/02121_user_withdrawal.up.sql @@ -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'); \ No newline at end of file diff --git a/internal/handler/public/user/commissionWithdrawHandler.go b/internal/handler/public/user/commissionWithdrawHandler.go new file mode 100644 index 0000000..f4f244c --- /dev/null +++ b/internal/handler/public/user/commissionWithdrawHandler.go @@ -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) + } +} diff --git a/internal/handler/public/user/queryWithdrawalLogHandler.go b/internal/handler/public/user/queryWithdrawalLogHandler.go new file mode 100644 index 0000000..9f0bddc --- /dev/null +++ b/internal/handler/public/user/queryWithdrawalLogHandler.go @@ -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) + } +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 7faf037..e3ad493 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -807,6 +807,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { // Query User Commission Log publicUserGroupRouter.GET("/commission_log", publicUser.QueryUserCommissionLogHandler(serverCtx)) + // Commission Withdraw + publicUserGroupRouter.POST("/commission_withdraw", publicUser.CommissionWithdrawHandler(serverCtx)) + // Get Device List publicUserGroupRouter.GET("/devices", publicUser.GetDeviceListHandler(serverCtx)) @@ -857,6 +860,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { // Verify Email publicUserGroupRouter.POST("/verify_email", publicUser.VerifyEmailHandler(serverCtx)) + + // Query Withdrawal Log + publicUserGroupRouter.GET("/withdrawal_log", publicUser.QueryWithdrawalLogHandler(serverCtx)) } serverGroupRouter := router.Group("/v1/server") diff --git a/internal/logic/public/user/commissionWithdrawLogic.go b/internal/logic/public/user/commissionWithdrawLogic.go new file mode 100644 index 0000000..0fe7e90 --- /dev/null +++ b/internal/logic/public/user/commissionWithdrawLogic.go @@ -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 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) { + // todo: add your logic here and delete this line + + return +} diff --git a/internal/logic/public/user/queryWithdrawalLogLogic.go b/internal/logic/public/user/queryWithdrawalLogLogic.go new file mode 100644 index 0000000..1b1a583 --- /dev/null +++ b/internal/logic/public/user/queryWithdrawalLogLogic.go @@ -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 +} diff --git a/internal/model/user/user.go b/internal/model/user/user.go index ee9e6f9..923594e 100644 --- a/internal/model/user/user.go +++ b/internal/model/user/user.go @@ -102,3 +102,18 @@ type DeviceOnlineRecord struct { func (DeviceOnlineRecord) TableName() string { 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" +} diff --git a/internal/types/types.go b/internal/types/types.go index f039294..565c4ce 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -239,6 +239,11 @@ type CommissionLog struct { Timestamp int64 `json:"timestamp"` } +type CommissionWithdrawRequest struct { + Amount int64 `json:"amount"` + Content string `json:"content"` +} + type Coupon struct { Id int64 `json:"id"` Name string `json:"name"` @@ -1723,6 +1728,16 @@ type QueryUserSubscribeNodeListResponse struct { 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 { Id int64 `json:"id"` Subscribers []int64 `json:"subscribers"` @@ -2766,3 +2781,14 @@ type VmessProtocol struct { Network string `json:"network"` 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"` +}