'datetime', 'accepted_at' => 'datetime', ]; /** * 테넌트 관계 */ public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } /** * 역할 관계 */ public function role(): BelongsTo { return $this->belongsTo(Role::class); } /** * 초대한 사용자 관계 */ public function inviter(): BelongsTo { return $this->belongsTo(User::class, 'invited_by'); } /** * 토큰 생성 */ public static function generateToken(): string { return Str::random(64); } /** * 만료 일시 계산 */ public static function calculateExpiresAt(int $days = self::DEFAULT_EXPIRES_DAYS): \DateTime { return now()->addDays($days); } /** * 만료 여부 확인 */ public function isExpired(): bool { return $this->expires_at->isPast(); } /** * 수락 가능 여부 확인 */ public function canAccept(): bool { return $this->status === self::STATUS_PENDING && ! $this->isExpired(); } /** * 취소 가능 여부 확인 */ public function canCancel(): bool { return $this->status === self::STATUS_PENDING; } /** * 초대 수락 처리 */ public function markAsAccepted(): void { $this->status = self::STATUS_ACCEPTED; $this->accepted_at = now(); $this->save(); } /** * 초대 만료 처리 */ public function markAsExpired(): void { $this->status = self::STATUS_EXPIRED; $this->save(); } /** * 초대 취소 처리 */ public function markAsCancelled(): void { $this->status = self::STATUS_CANCELLED; $this->save(); } /** * Scope: 대기 중인 초대만 */ public function scopePending($query) { return $query->where('status', self::STATUS_PENDING); } /** * Scope: 만료된 초대 (상태 업데이트 대상) */ public function scopeExpiredPending($query) { return $query->where('status', self::STATUS_PENDING) ->where('expires_at', '<', now()); } /** * Scope: 특정 이메일 초대 */ public function scopeForEmail($query, string $email) { return $query->where('email', $email); } /** * 상태 라벨 반환 */ public function getStatusLabelAttribute(): string { return match ($this->status) { self::STATUS_PENDING => '대기중', self::STATUS_ACCEPTED => '수락됨', self::STATUS_EXPIRED => '만료됨', self::STATUS_CANCELLED => '취소됨', default => $this->status, }; } }