feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
|
|
|
|
use App\Models\Members\User;
|
|
|
|
|
use App\Models\Permissions\Role;
|
|
|
|
|
use App\Models\Tenants\Tenant;
|
2026-01-29 15:33:54 +09:00
|
|
|
use App\Traits\Auditable;
|
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
|
|
|
use App\Traits\BelongsToTenant;
|
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
|
|
|
|
|
|
class UserInvitation extends Model
|
|
|
|
|
{
|
2026-01-29 15:33:54 +09:00
|
|
|
use Auditable, BelongsToTenant;
|
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
|
|
|
|
|
|
|
|
// 상태 상수
|
|
|
|
|
public const STATUS_PENDING = 'pending';
|
|
|
|
|
|
|
|
|
|
public const STATUS_ACCEPTED = 'accepted';
|
|
|
|
|
|
|
|
|
|
public const STATUS_EXPIRED = 'expired';
|
|
|
|
|
|
|
|
|
|
public const STATUS_CANCELLED = 'cancelled';
|
|
|
|
|
|
|
|
|
|
// 기본 만료 기간 (일)
|
|
|
|
|
public const DEFAULT_EXPIRES_DAYS = 7;
|
|
|
|
|
|
|
|
|
|
protected $fillable = [
|
|
|
|
|
'tenant_id',
|
|
|
|
|
'email',
|
|
|
|
|
'role_id',
|
|
|
|
|
'message',
|
|
|
|
|
'token',
|
|
|
|
|
'status',
|
|
|
|
|
'invited_by',
|
|
|
|
|
'expires_at',
|
|
|
|
|
'accepted_at',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
protected $casts = [
|
|
|
|
|
'expires_at' => '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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|