diff --git a/internal/handler/admin/user/getAdminUserInviteListHandler.go b/internal/handler/admin/user/getAdminUserInviteListHandler.go new file mode 100644 index 0000000..a20f0ed --- /dev/null +++ b/internal/handler/admin/user/getAdminUserInviteListHandler.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" +) + +// Get admin user invite list +func GetAdminUserInviteListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.GetAdminUserInviteListRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewGetAdminUserInviteListLogic(c.Request.Context(), svcCtx) + resp, err := l.GetAdminUserInviteList(&req) + result.HttpResult(c, resp, err) + } +} diff --git a/internal/handler/admin/user/getAdminUserInviteStatsHandler.go b/internal/handler/admin/user/getAdminUserInviteStatsHandler.go new file mode 100644 index 0000000..20cb810 --- /dev/null +++ b/internal/handler/admin/user/getAdminUserInviteStatsHandler.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" +) + +// Get admin user invite stats +func GetAdminUserInviteStatsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.GetAdminUserInviteStatsRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewGetAdminUserInviteStatsLogic(c.Request.Context(), svcCtx) + resp, err := l.GetAdminUserInviteStats(&req) + result.HttpResult(c, resp, err) + } +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 28adc6b..842fe14 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -644,6 +644,12 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { // Get user subcribe traffic logs adminUserGroupRouter.GET("/subscribe/traffic_logs", adminUser.GetUserSubscribeTrafficLogsHandler(serverCtx)) + + // Get admin user invite stats + adminUserGroupRouter.GET("/invite/stats", adminUser.GetAdminUserInviteStatsHandler(serverCtx)) + + // Get admin user invite list + adminUserGroupRouter.GET("/invite/list", adminUser.GetAdminUserInviteListHandler(serverCtx)) } authGroupRouter := router.Group("/v1/auth") diff --git a/internal/logic/admin/user/getAdminUserInviteListLogic.go b/internal/logic/admin/user/getAdminUserInviteListLogic.go new file mode 100644 index 0000000..6ed8df1 --- /dev/null +++ b/internal/logic/admin/user/getAdminUserInviteListLogic.go @@ -0,0 +1,83 @@ +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 GetAdminUserInviteListLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetAdminUserInviteListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAdminUserInviteListLogic { + return &GetAdminUserInviteListLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetAdminUserInviteListLogic) GetAdminUserInviteList(req *types.GetAdminUserInviteListRequest) (resp *types.GetAdminUserInviteListResponse, err error) { + if req.Page < 1 { + req.Page = 1 + } + if req.Size < 1 { + req.Size = 10 + } + if req.Size > 100 { + req.Size = 100 + } + + type InvitedUser struct { + Id int64 `gorm:"column:id"` + Avatar string `gorm:"column:avatar"` + Identifier string `gorm:"column:identifier"` + Enable bool `gorm:"column:enable"` + CreatedAt int64 `gorm:"column:created_at"` + } + + var total int64 + baseQuery := l.svcCtx.DB.WithContext(l.ctx). + Table("user u"). + Where("u.referer_id = ? AND u.deleted_at IS NULL", req.UserId) + + if err = baseQuery.Count(&total).Error; err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count invited users failed: %v", err) + } + + var rows []InvitedUser + err = l.svcCtx.DB.WithContext(l.ctx). + Table("user u"). + Select("u.id, u.avatar, u.enable, UNIX_TIMESTAMP(u.created_at) as created_at, COALESCE((SELECT uam.auth_identifier FROM user_auth_methods uam WHERE uam.user_id = u.id AND uam.deleted_at IS NULL ORDER BY uam.id ASC LIMIT 1), '') as identifier"). + Where("u.referer_id = ? AND u.deleted_at IS NULL", req.UserId). + Order("u.created_at DESC"). + Limit(req.Size). + Offset((req.Page - 1) * req.Size). + Scan(&rows).Error + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invited users failed: %v", err) + } + + list := make([]types.AdminInvitedUser, 0, len(rows)) + for _, r := range rows { + list = append(list, types.AdminInvitedUser{ + Id: r.Id, + Avatar: r.Avatar, + Identifier: r.Identifier, + Enable: r.Enable, + CreatedAt: r.CreatedAt, + }) + } + + return &types.GetAdminUserInviteListResponse{ + Total: total, + List: list, + }, nil +} diff --git a/internal/logic/admin/user/getAdminUserInviteStatsLogic.go b/internal/logic/admin/user/getAdminUserInviteStatsLogic.go new file mode 100644 index 0000000..3e6bd24 --- /dev/null +++ b/internal/logic/admin/user/getAdminUserInviteStatsLogic.go @@ -0,0 +1,72 @@ +package user + +import ( + "context" + "database/sql" + + "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 GetAdminUserInviteStatsLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetAdminUserInviteStatsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAdminUserInviteStatsLogic { + return &GetAdminUserInviteStatsLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetAdminUserInviteStatsLogic) GetAdminUserInviteStats(req *types.GetAdminUserInviteStatsRequest) (resp *types.GetAdminUserInviteStatsResponse, err error) { + userId := req.UserId + + // 邀请总人数 + var inviteCount int64 + if err = l.svcCtx.DB.WithContext(l.ctx). + Table("user"). + Where("referer_id = ? AND deleted_at IS NULL", userId). + Count(&inviteCount).Error; err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count invite users failed: %v", err) + } + + // 历史累计佣金(来自 system_logs type=33 的佣金记录) + var totalCommission sql.NullInt64 + if err = l.svcCtx.DB.WithContext(l.ctx). + Table("system_logs"). + Select("COALESCE(SUM(JSON_EXTRACT(content, '$.amount')), 0) as total"). + Where("type = ? AND object_id = ? AND JSON_EXTRACT(content, '$.type') IN (?, ?)", 33, userId, 331, 332). + Scan(&totalCommission).Error; err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "sum commission failed: %v", err) + } + + // 当前佣金余额 + type UserCommission struct { + Commission int64 `gorm:"column:commission"` + ReferralPercentage uint8 `gorm:"column:referral_percentage"` + OnlyFirstPurchase bool `gorm:"column:only_first_purchase"` + } + var userInfo UserCommission + if err = l.svcCtx.DB.WithContext(l.ctx). + Table("user"). + Select("commission, referral_percentage, only_first_purchase"). + Where("id = ?", userId). + Scan(&userInfo).Error; err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get user commission failed: %v", err) + } + + return &types.GetAdminUserInviteStatsResponse{ + InviteCount: inviteCount, + TotalCommission: totalCommission.Int64, + CurrentCommission: userInfo.Commission, + ReferralPercentage: userInfo.ReferralPercentage, + OnlyFirstPurchase: userInfo.OnlyFirstPurchase, + }, nil +} diff --git a/internal/types/types.go b/internal/types/types.go index 07761fe..a4f015e 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -3199,3 +3199,34 @@ type WithdrawalLog struct { CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } + +type GetAdminUserInviteStatsRequest struct { + UserId int64 `form:"user_id" validate:"required"` +} + +type GetAdminUserInviteStatsResponse struct { + InviteCount int64 `json:"invite_count"` + TotalCommission int64 `json:"total_commission"` + CurrentCommission int64 `json:"current_commission"` + ReferralPercentage uint8 `json:"referral_percentage"` + OnlyFirstPurchase bool `json:"only_first_purchase"` +} + +type GetAdminUserInviteListRequest struct { + UserId int64 `form:"user_id" validate:"required"` + Page int `form:"page"` + Size int `form:"size"` +} + +type AdminInvitedUser struct { + Id int64 `json:"id"` + Avatar string `json:"avatar"` + Identifier string `json:"identifier"` + Enable bool `json:"enable"` + CreatedAt int64 `json:"created_at"` +} + +type GetAdminUserInviteListResponse struct { + Total int64 `json:"total"` + List []AdminInvitedUser `json:"list"` +}