package user import ( "context" "time" "github.com/perfect-panel/server/internal/logic/auth" commonLogic "github.com/perfect-panel/server/internal/logic/common" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/tool" "github.com/perfect-panel/server/pkg/uuidx" ) func tryGrantTrialOnEmailBind(ctx context.Context, svcCtx *svc.ServiceContext, log logger.Logger, ownerUserId int64, email string) { rc := svcCtx.Config.Register commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_evaluating", "[SubscriptionFlow] evaluating email bind trial grant", logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), logger.Field("trial_subscribe_id", rc.TrialSubscribe), ) if !auth.ShouldAutoGrantTrialOnPublicEmailFlows(rc) { commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_skipped", "[SubscriptionFlow] auto trial on public email flow disabled", logger.Field("email", email), logger.Field("owner_user_id", ownerUserId), logger.Field("skip_reason", "public_email_trial_disabled"), ) return } if !auth.ShouldGrantTrialForEmail(rc, email) { if rc.EnableTrial && rc.EnableTrialEmailWhitelist { commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_skipped", "[SubscriptionFlow] email domain not in trial whitelist", logger.Field("email", email), logger.Field("owner_user_id", ownerUserId), logger.Field("skip_reason", "trial_whitelist_rejected"), ) } return } var count int64 if err := svcCtx.DB.WithContext(ctx). Model(&user.Subscribe{}). Where("user_id = ? AND subscribe_id = ?", ownerUserId, rc.TrialSubscribe). Count(&count).Error; err != nil { commonLogic.SubscriptionTraceError(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_error", "[SubscriptionFlow] failed to query existing trial subscription", logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), logger.Field("error", err.Error()), ) return } if count > 0 { commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_skipped", "[SubscriptionFlow] trial already exists for owner", logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), logger.Field("skip_reason", "trial_already_exists"), ) return } // Cross-user check: prevent the same real inbox (via dot trick / + alias) from // getting multiple trials across different accounts. if auth.NormalizedEmailHasTrial(ctx, svcCtx.DB, email, rc.TrialSubscribe) { commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_skipped", "[SubscriptionFlow] normalized email already received a trial elsewhere", logger.Field("email", email), logger.Field("owner_user_id", ownerUserId), logger.Field("skip_reason", "normalized_email_has_trial"), ) return } sub, err := svcCtx.SubscribeModel.FindOne(ctx, rc.TrialSubscribe) if err != nil { commonLogic.SubscriptionTraceError(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_error", "[SubscriptionFlow] failed to load trial subscription template", logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), logger.Field("trial_subscribe_id", rc.TrialSubscribe), logger.Field("error", err.Error()), ) return } userSub := &user.Subscribe{ UserId: ownerUserId, OrderId: 0, SubscribeId: sub.Id, StartTime: time.Now(), ExpireTime: tool.AddTime(rc.TrialTimeUnit, rc.TrialTime, time.Now()), Traffic: sub.Traffic, Download: 0, Upload: 0, Token: uuidx.NewUUID().String(), UUID: uuidx.NewUUID().String(), Status: 1, } if err = svcCtx.UserModel.InsertSubscribe(ctx, userSub); err != nil { commonLogic.SubscriptionTraceError(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_error", "[SubscriptionFlow] failed to create trial subscription for email bind", append(commonLogic.UserSubscribeTraceFields(userSub), logger.Field("error", err.Error()), logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), )..., ) return } if svcCtx.NodeModel != nil { if err = svcCtx.NodeModel.ClearServerAllCache(ctx); err != nil { log.Errorw("ClearServerAllCache error", logger.Field("error", err.Error())) } } commonLogic.SubscriptionTraceInfo(log, commonLogic.SubscriptionTraceFlowEmailBind, "trial_grant_succeeded", "[SubscriptionFlow] trial subscription granted after email bind", append(commonLogic.UserSubscribeTraceFields(userSub), logger.Field("owner_user_id", ownerUserId), logger.Field("email", email), logger.Field("trial_subscribe_id", sub.Id), )..., ) }