From 31e634ba662080a78bbd695729279bf8e0b68c9f Mon Sep 17 00:00:00 2001 From: Tension Date: Sun, 28 Dec 2025 16:49:28 +0800 Subject: [PATCH] feat(user): add handlers and logic for resetting user subscribe token and traffic --- apis/admin/user.api | 18 ++++++ apis/public/user.api | 4 +- apis/types.api | 4 ++ .../user/resetUserSubscribeTokenHandler.go | 26 +++++++++ .../user/resetUserSubscribeTrafficHandler.go | 26 +++++++++ .../admin/user/stopUserSubscribeHandler.go | 26 +++++++++ internal/handler/routes.go | 9 +++ .../user/resetUserSubscribeTokenLogic.go | 55 +++++++++++++++++++ .../user/resetUserSubscribeTrafficLogic.go | 53 ++++++++++++++++++ .../admin/user/stopUserSubscribeLogic.go | 53 ++++++++++++++++++ internal/model/user/user.go | 2 +- internal/types/types.go | 8 +++ 12 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 internal/handler/admin/user/resetUserSubscribeTokenHandler.go create mode 100644 internal/handler/admin/user/resetUserSubscribeTrafficHandler.go create mode 100644 internal/handler/admin/user/stopUserSubscribeHandler.go create mode 100644 internal/logic/admin/user/resetUserSubscribeTokenLogic.go create mode 100644 internal/logic/admin/user/resetUserSubscribeTrafficLogic.go create mode 100644 internal/logic/admin/user/stopUserSubscribeLogic.go diff --git a/apis/admin/user.api b/apis/admin/user.api index 699fe6d..1f01a6f 100644 --- a/apis/admin/user.api +++ b/apis/admin/user.api @@ -184,6 +184,12 @@ type ( GetUserSubscribeByIdRequest { Id int64 `form:"id" validate:"required"` } + StopUserSubscribeRequest { + UserSubscribeId int64 `json:"user_subscribe_id"` + } + ResetUserSubscribeTrafficRequest { + UserSubscribeId int64 `json:"user_subscribe_id"` + } ) @server ( @@ -292,5 +298,17 @@ service ppanel { @doc "Get user login logs" @handler GetUserLoginLogs get /login/logs (GetUserLoginLogsRequest) returns (GetUserLoginLogsResponse) + + @doc "Reset user subscribe token" + @handler ResetUserSubscribeToken + post /subscribe/reset/token (ResetUserSubscribeTokenRequest) + + @doc "Stop user subscribe" + @handler StopUserSubscribe + post /subscribe/stop (StopUserSubscribeRequest) + + @doc "Reset user subscribe traffic" + @handler ResetUserSubscribeTraffic + post /subscribe/reset/traffic (ResetUserSubscribeTrafficRequest) } diff --git a/apis/public/user.api b/apis/public/user.api index f37eef3..3612328 100644 --- a/apis/public/user.api +++ b/apis/public/user.api @@ -66,9 +66,7 @@ type ( UnbindOAuthRequest { Method string `json:"method"` } - ResetUserSubscribeTokenRequest { - UserSubscribeId int64 `json:"user_subscribe_id"` - } + GetLoginLogRequest { Page int `form:"page"` Size int `form:"size"` diff --git a/apis/types.api b/apis/types.api index 85ecf63..b212734 100644 --- a/apis/types.api +++ b/apis/types.api @@ -844,5 +844,9 @@ type ( CertDNSProvider string `json:"cert_dns_provider,omitempty"` // DNS provider for certificate CertDNSEnv string `json:"cert_dns_env,omitempty"` // Environment for DNS provider } + // reset user subscribe token + ResetUserSubscribeTokenRequest { + UserSubscribeId int64 `json:"user_subscribe_id"` + } ) diff --git a/internal/handler/admin/user/resetUserSubscribeTokenHandler.go b/internal/handler/admin/user/resetUserSubscribeTokenHandler.go new file mode 100644 index 0000000..0ccff0c --- /dev/null +++ b/internal/handler/admin/user/resetUserSubscribeTokenHandler.go @@ -0,0 +1,26 @@ +package user + +import ( + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/logic/admin/user" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/result" +) + +// Reset user subscribe token +func ResetUserSubscribeTokenHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.ResetUserSubscribeTokenRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewResetUserSubscribeTokenLogic(c.Request.Context(), svcCtx) + err := l.ResetUserSubscribeToken(&req) + result.HttpResult(c, nil, err) + } +} diff --git a/internal/handler/admin/user/resetUserSubscribeTrafficHandler.go b/internal/handler/admin/user/resetUserSubscribeTrafficHandler.go new file mode 100644 index 0000000..80dccfc --- /dev/null +++ b/internal/handler/admin/user/resetUserSubscribeTrafficHandler.go @@ -0,0 +1,26 @@ +package user + +import ( + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/logic/admin/user" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/result" +) + +// Reset user subscribe traffic +func ResetUserSubscribeTrafficHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.ResetUserSubscribeTrafficRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewResetUserSubscribeTrafficLogic(c.Request.Context(), svcCtx) + err := l.ResetUserSubscribeTraffic(&req) + result.HttpResult(c, nil, err) + } +} diff --git a/internal/handler/admin/user/stopUserSubscribeHandler.go b/internal/handler/admin/user/stopUserSubscribeHandler.go new file mode 100644 index 0000000..d61f04a --- /dev/null +++ b/internal/handler/admin/user/stopUserSubscribeHandler.go @@ -0,0 +1,26 @@ +package user + +import ( + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/logic/admin/user" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/result" +) + +// Stop user subscribe +func StopUserSubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.StopUserSubscribeRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewStopUserSubscribeLogic(c.Request.Context(), svcCtx) + err := l.StopUserSubscribe(&req) + result.HttpResult(c, nil, err) + } +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 2ac18a8..2d41010 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -576,6 +576,15 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { // Get user subcribe reset traffic logs adminUserGroupRouter.GET("/subscribe/reset/logs", adminUser.GetUserSubscribeResetTrafficLogsHandler(serverCtx)) + // Reset user subscribe token + adminUserGroupRouter.POST("/subscribe/reset/token", adminUser.ResetUserSubscribeTokenHandler(serverCtx)) + + // Reset user subscribe traffic + adminUserGroupRouter.POST("/subscribe/reset/traffic", adminUser.ResetUserSubscribeTrafficHandler(serverCtx)) + + // Stop user subscribe + adminUserGroupRouter.POST("/subscribe/stop", adminUser.StopUserSubscribeHandler(serverCtx)) + // Get user subcribe traffic logs adminUserGroupRouter.GET("/subscribe/traffic_logs", adminUser.GetUserSubscribeTrafficLogsHandler(serverCtx)) } diff --git a/internal/logic/admin/user/resetUserSubscribeTokenLogic.go b/internal/logic/admin/user/resetUserSubscribeTokenLogic.go new file mode 100644 index 0000000..02f46ca --- /dev/null +++ b/internal/logic/admin/user/resetUserSubscribeTokenLogic.go @@ -0,0 +1,55 @@ +package user + +import ( + "context" + "fmt" + "time" + + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/logger" + "github.com/perfect-panel/server/pkg/uuidx" + "github.com/perfect-panel/server/pkg/xerr" + "github.com/pkg/errors" +) + +type ResetUserSubscribeTokenLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// NewResetUserSubscribeTokenLogic Reset user subscribe token +func NewResetUserSubscribeTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetUserSubscribeTokenLogic { + return &ResetUserSubscribeTokenLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ResetUserSubscribeTokenLogic) ResetUserSubscribeToken(req *types.ResetUserSubscribeTokenRequest) error { + userSub, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId) + if err != nil { + logger.Errorf("[ResetUserSubscribeToken] FindOneSubscribe error: %v", err.Error()) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneSubscribe error: %v", err.Error()) + } + userSub.Token = uuidx.SubscribeToken(fmt.Sprintf("AdminUpdate:%d", time.Now().UnixMilli())) + + err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, userSub) + if err != nil { + logger.Errorf("[ResetUserSubscribeToken] UpdateSubscribe error: %v", err.Error()) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateSubscribe error: %v", err.Error()) + } + // Clear user subscribe cache + if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, userSub); err != nil { + l.Errorw("ClearSubscribeCache failed:", logger.Field("error", err.Error()), logger.Field("userSubscribeId", userSub.Id)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error()) + } + // Clear subscribe cache + if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId); err != nil { + l.Errorw("failed to clear subscribe cache", logger.Field("error", err.Error()), logger.Field("subscribeId", userSub.SubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear subscribe cache: %v", err.Error()) + } + return nil +} diff --git a/internal/logic/admin/user/resetUserSubscribeTrafficLogic.go b/internal/logic/admin/user/resetUserSubscribeTrafficLogic.go new file mode 100644 index 0000000..5b90147 --- /dev/null +++ b/internal/logic/admin/user/resetUserSubscribeTrafficLogic.go @@ -0,0 +1,53 @@ +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" + "github.com/perfect-panel/server/pkg/xerr" + "github.com/pkg/errors" +) + +type ResetUserSubscribeTrafficLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// NewResetUserSubscribeTrafficLogic Reset user subscribe traffic +func NewResetUserSubscribeTrafficLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResetUserSubscribeTrafficLogic { + return &ResetUserSubscribeTrafficLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ResetUserSubscribeTrafficLogic) ResetUserSubscribeTraffic(req *types.ResetUserSubscribeTrafficRequest) error { + userSub, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId) + if err != nil { + l.Errorw("FindOneSubscribe error", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " FindOneSubscribe error: %v", err.Error()) + } + userSub.Download = 0 + userSub.Upload = 0 + + err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, userSub) + if err != nil { + l.Errorw("UpdateSubscribe error", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), " UpdateSubscribe error: %v", err.Error()) + } + // Clear user subscribe cache + if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, userSub); err != nil { + l.Errorw("ClearSubscribeCache failed:", logger.Field("error", err.Error()), logger.Field("userSubscribeId", userSub.Id)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error()) + } + // Clear subscribe cache + if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId); err != nil { + l.Errorw("failed to clear subscribe cache", logger.Field("error", err.Error()), logger.Field("subscribeId", userSub.SubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear subscribe cache: %v", err.Error()) + } + return nil +} diff --git a/internal/logic/admin/user/stopUserSubscribeLogic.go b/internal/logic/admin/user/stopUserSubscribeLogic.go new file mode 100644 index 0000000..658e4cf --- /dev/null +++ b/internal/logic/admin/user/stopUserSubscribeLogic.go @@ -0,0 +1,53 @@ +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" + "github.com/perfect-panel/server/pkg/xerr" + "github.com/pkg/errors" +) + +type StopUserSubscribeLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// NewStopUserSubscribeLogic Stop user subscribe +func NewStopUserSubscribeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StopUserSubscribeLogic { + return &StopUserSubscribeLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *StopUserSubscribeLogic) StopUserSubscribe(req *types.StopUserSubscribeRequest) error { + userSub, err := l.svcCtx.UserModel.FindOneSubscribe(l.ctx, req.UserSubscribeId) + if err != nil { + l.Errorw("FindOneSubscribe error", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), " FindOneSubscribe error: %v", err.Error()) + } + userSub.Status = 5 // set status to stopped + err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, userSub) + if err != nil { + l.Errorw("UpdateSubscribe error", logger.Field("error", err.Error()), logger.Field("userSubscribeId", req.UserSubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), " UpdateSubscribe error: %v", err.Error()) + } + + // Clear user subscribe cache + if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, userSub); err != nil { + l.Errorw("ClearSubscribeCache failed:", logger.Field("error", err.Error()), logger.Field("userSubscribeId", userSub.Id)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error()) + } + // Clear subscribe cache + if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId); err != nil { + l.Errorw("failed to clear subscribe cache", logger.Field("error", err.Error()), logger.Field("subscribeId", userSub.SubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "failed to clear subscribe cache: %v", err.Error()) + } + + return nil +} diff --git a/internal/model/user/user.go b/internal/model/user/user.go index 80db348..30fa804 100644 --- a/internal/model/user/user.go +++ b/internal/model/user/user.go @@ -51,7 +51,7 @@ type Subscribe struct { Upload int64 `gorm:"default:0;comment:Upload Traffic"` Token string `gorm:"index:idx_token;unique;type:varchar(255);default:'';comment:Token"` UUID string `gorm:"type:varchar(255);unique;index:idx_uuid;default:'';comment:UUID"` - Status uint8 `gorm:"type:tinyint(1);default:0;comment:Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired 4: Deducted"` + Status uint8 `gorm:"type:tinyint(1);default:0;comment:Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired 4: Deducted 5: stopped"` Note string `gorm:"type:varchar(500);default:'';comment:User note for subscription"` CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` diff --git a/internal/types/types.go b/internal/types/types.go index d44d465..44180f4 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1846,6 +1846,10 @@ type ResetUserSubscribeTokenRequest struct { UserSubscribeId int64 `json:"user_subscribe_id"` } +type ResetUserSubscribeTrafficRequest struct { + UserSubscribeId int64 `json:"user_subscribe_id"` +} + type RevenueStatisticsResponse struct { Today OrdersStatistics `json:"today"` Monthly OrdersStatistics `json:"monthly"` @@ -2039,6 +2043,10 @@ type StopBatchSendEmailTaskRequest struct { Id int64 `json:"id"` } +type StopUserSubscribeRequest struct { + UserSubscribeId int64 `json:"user_subscribe_id"` +} + type StripePayment struct { Method string `json:"method"` ClientSecret string `json:"client_secret"`