feat(server/auth): Register 加 inviteCode + ReferralHook(注册后 best-effort 归因)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-13 07:33:42 +08:00
parent 2fb7391a17
commit 54d106ca8b
6 changed files with 83 additions and 26 deletions
+16 -3
View File
@@ -132,12 +132,22 @@ type Service struct {
now func() time.Time
devReg DeviceRegistrar // nil until wired; registration is best-effort
sessions SessionStore // nil until wired; session recording is best-effort
referral ReferralHook // nil until wired; referral attribution is best-effort
}
// SetDeviceRegistrar / SetSessionStore wire collaborators after construction
// (main keeps auth, devices and sessions decoupled). Call once during startup.
// ReferralHook is invoked after a successful registration to attribute the new
// account to the invite code it registered with (best-effort; implemented by
// reward.Service). Defined consumer-side to avoid an import cycle.
type ReferralHook interface {
OnRegister(ctx context.Context, inviteeID int64, inviteCode, deviceUUID string)
}
// SetDeviceRegistrar / SetSessionStore / SetReferralHook wire collaborators
// after construction (main keeps auth, devices, sessions and reward decoupled).
// Call once during startup.
func (s *Service) SetDeviceRegistrar(r DeviceRegistrar) { s.devReg = r }
func (s *Service) SetSessionStore(st SessionStore) { s.sessions = st }
func (s *Service) SetReferralHook(h ReferralHook) { s.referral = h }
// recordLogin registers the device and binds a session to the freshly-issued
// refresh JTI. Best-effort: a registrar/session error (device cap, transient DB)
@@ -267,7 +277,7 @@ func (s *Service) SendCode(ctx context.Context, rawEmail, ip string) (retryAfter
// Register verifies the code (one-time), creates the account plus a 7-day PRO
// trial in a single transaction, and returns a fresh token pair.
func (s *Service) Register(ctx context.Context, rawEmail, code, password, ip string, device DeviceMeta) (*TokenPair, *apierr.Error) {
func (s *Service) Register(ctx context.Context, rawEmail, code, password, ip string, device DeviceMeta, inviteCode string) (*TokenPair, *apierr.Error) {
email := NormalizeEmail(rawEmail)
if !ValidEmail(email) || len(password) < 8 || len(code) != 6 {
return nil, ErrInvalidRequest
@@ -301,6 +311,9 @@ func (s *Service) Register(ctx context.Context, rawEmail, code, password, ip str
return nil, ErrInternal
}
s.recordLogin(ctx, user.ID, jti, ip, device)
if s.referral != nil {
s.referral.OnRegister(ctx, user.ID, inviteCode, device.DeviceID) // best-effort
}
return pair, nil
}