feat: [demo] 데모 테넌트 관리 API 및 만료 알림 (Phase 3)
- DemoTenantController: 목록/상세/생성/리셋/연장/전환/통계 API - DemoTenantStoreRequest: 고객 체험 테넌트 생성 검증 - DemoTenantService: API용 메서드 추가 (index/show/reset/extend/convert/stats) - CheckDemoExpiredCommand: 만료 임박(7일) 알림 + 만료 테넌트 비활성 처리 - 라우트 등록 (api/v1/demo-tenants, 7개 엔드포인트) - 스케줄러 등록 (04:20 demo:check-expired) - i18n 메시지 추가 (message.demo_tenant.*, error.demo_tenant.*)
This commit is contained in:
92
app/Console/Commands/CheckDemoExpiredCommand.php
Normal file
92
app/Console/Commands/CheckDemoExpiredCommand.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Tenants\Tenant;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* 데모 테넌트 만료 체크 및 알림 커맨드
|
||||
*
|
||||
* - 만료 임박 (7일 이내): 파트너에게 알림 로그
|
||||
* - 만료된 테넌트: 비활성 상태로 전환
|
||||
*
|
||||
* 기존 코드 영향 없음: DEMO_TRIAL 테넌트만 대상
|
||||
*
|
||||
* @see docs/features/sales/demo-tenant-policy.md
|
||||
*/
|
||||
class CheckDemoExpiredCommand extends Command
|
||||
{
|
||||
protected $signature = 'demo:check-expired
|
||||
{--dry-run : 실제 변경 없이 대상만 표시}';
|
||||
|
||||
protected $description = '데모 체험 테넌트 만료 체크 및 비활성 처리';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
// 1. 만료 임박 테넌트 (7일 이내)
|
||||
$expiringSoon = Tenant::withoutGlobalScopes()
|
||||
->where('tenant_type', Tenant::TYPE_DEMO_TRIAL)
|
||||
->where('tenant_st_code', '!=', 'expired')
|
||||
->whereNotNull('demo_expires_at')
|
||||
->where('demo_expires_at', '>', now())
|
||||
->where('demo_expires_at', '<=', now()->addDays(7))
|
||||
->get();
|
||||
|
||||
if ($expiringSoon->isNotEmpty()) {
|
||||
$this->info("만료 임박 테넌트: {$expiringSoon->count()}건");
|
||||
foreach ($expiringSoon as $tenant) {
|
||||
$daysLeft = (int) now()->diffInDays($tenant->demo_expires_at, false);
|
||||
$this->line(" - [{$tenant->id}] {$tenant->company_name} (D-{$daysLeft})");
|
||||
|
||||
Log::info('데모 체험 만료 임박', [
|
||||
'tenant_id' => $tenant->id,
|
||||
'company_name' => $tenant->company_name,
|
||||
'expires_at' => $tenant->demo_expires_at->toDateString(),
|
||||
'days_left' => $daysLeft,
|
||||
'partner_id' => $tenant->demo_source_partner_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 이미 만료된 테넌트 → 상태 변경
|
||||
$expired = Tenant::withoutGlobalScopes()
|
||||
->where('tenant_type', Tenant::TYPE_DEMO_TRIAL)
|
||||
->where('tenant_st_code', '!=', 'expired')
|
||||
->whereNotNull('demo_expires_at')
|
||||
->where('demo_expires_at', '<', now())
|
||||
->get();
|
||||
|
||||
if ($expired->isEmpty()) {
|
||||
$this->info('만료 처리 대상 없음');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info("만료 처리 대상: {$expired->count()}건");
|
||||
|
||||
foreach ($expired as $tenant) {
|
||||
$this->line(" - [{$tenant->id}] {$tenant->company_name} (만료: {$tenant->demo_expires_at->toDateString()})");
|
||||
|
||||
if (! $this->option('dry-run')) {
|
||||
$tenant->forceFill(['tenant_st_code' => 'expired']);
|
||||
$tenant->save();
|
||||
|
||||
Log::info('데모 체험 만료 처리', [
|
||||
'tenant_id' => $tenant->id,
|
||||
'company_name' => $tenant->company_name,
|
||||
'partner_id' => $tenant->demo_source_partner_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('dry-run')) {
|
||||
$this->warn('(dry-run 모드 — 실제 변경 없음)');
|
||||
} else {
|
||||
$this->info(" {$expired->count()}건 만료 처리 완료");
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user