Files
sam-api/app/Services/Demo/DemoTenantService.php
김보곤 1eb8d2cb01 feat: [demo] 데모 테넌트 운영 자동화 (Phase 2)
- DemoLimitMiddleware: 쇼케이스 읽기전용, 만료 체크, 외부연동 차단
- DemoTenantService: 파트너/체험 테넌트 생성, 기간 연장, 정식 전환
- ResetDemoShowcaseCommand: 매일 자정 데이터 리셋 + 샘플 재시드
- ManufacturingPresetSeeder: 부서/거래처/품목/견적/수주 샘플 데이터
- 스케줄러 등록 (00:00 demo:reset-showcase --seed)
- 미들웨어 별칭 등록 (demo.limit)
2026-03-14 14:41:55 +09:00

168 lines
4.7 KiB
PHP

<?php
namespace App\Services\Demo;
use App\Models\Tenants\Tenant;
use App\Services\Service;
use Illuminate\Support\Facades\Log;
/**
* 데모 테넌트 생성/관리 서비스
*
* 기존 코드 영향 없음: 데모 전용 로직만 포함
*
* @see docs/features/sales/demo-tenant-policy.md
*/
class DemoTenantService extends Service
{
/**
* 기본 데모 수량 제한
*/
private const DEFAULT_DEMO_LIMITS = [
'max_items' => 100,
'max_orders' => 50,
'max_productions' => 30,
'max_users' => 5,
'max_storage_gb' => 1,
'max_ai_tokens' => 100000,
];
/**
* 사용 가능한 프리셋 목록
*/
private const AVAILABLE_PRESETS = [
'manufacturing', // 제조업 기본
// 향후 추가 예정:
// 'blinds', // 블라인드/스크린
// 'construction', // 시공/건설
// 'distribution', // 유통/도소매
];
/**
* 파트너 데모 테넌트 생성 (Tier 2)
* 파트너 승인 시 호출
*/
public function createPartnerDemo(int $partnerId, string $preset = 'manufacturing'): Tenant
{
$tenant = new Tenant;
$tenant->forceFill([
'company_name' => '파트너데모_'.$partnerId,
'code' => 'DEMO_P_'.$partnerId,
'email' => 'demo-partner-'.$partnerId.'@codebridge-x.com',
'tenant_st_code' => 'active',
'tenant_type' => Tenant::TYPE_DEMO_PARTNER,
'demo_source_partner_id' => $partnerId,
'options' => [
Tenant::OPTION_DEMO_PRESET => $preset,
Tenant::OPTION_DEMO_LIMITS => self::DEFAULT_DEMO_LIMITS,
],
]);
$tenant->save();
Log::info('파트너 데모 테넌트 생성', [
'tenant_id' => $tenant->id,
'partner_id' => $partnerId,
'preset' => $preset,
]);
return $tenant;
}
/**
* 고객 체험 테넌트 생성 (Tier 3)
* 파트너가 요청 → 본사 승인 후 호출
*/
public function createTrialDemo(
int $partnerId,
string $companyName,
string $email,
int $durationDays = 30,
string $preset = 'manufacturing'
): Tenant {
$tenant = new Tenant;
$tenant->forceFill([
'company_name' => $companyName,
'code' => 'DEMO_T_'.strtoupper(substr(md5(uniqid()), 0, 8)),
'email' => $email,
'tenant_st_code' => 'trial',
'tenant_type' => Tenant::TYPE_DEMO_TRIAL,
'demo_expires_at' => now()->addDays($durationDays),
'demo_source_partner_id' => $partnerId,
'options' => [
Tenant::OPTION_DEMO_PRESET => $preset,
Tenant::OPTION_DEMO_LIMITS => self::DEFAULT_DEMO_LIMITS,
],
]);
$tenant->save();
Log::info('고객 체험 테넌트 생성', [
'tenant_id' => $tenant->id,
'partner_id' => $partnerId,
'company_name' => $companyName,
'expires_at' => $tenant->demo_expires_at->toDateString(),
]);
return $tenant;
}
/**
* 체험 기간 연장 (최대 1회, 30일)
*/
public function extendTrial(Tenant $tenant, int $days = 30): bool
{
if (! $tenant->isDemoTrial()) {
return false;
}
// 이미 연장한 이력이 있는지 체크 (options에 기록)
if ($tenant->getOption('demo_extended', false)) {
return false;
}
$newExpiry = ($tenant->demo_expires_at ?? now())->addDays($days);
$tenant->forceFill(['demo_expires_at' => $newExpiry]);
$tenant->setOption('demo_extended', true);
$tenant->save();
Log::info('고객 체험 기간 연장', [
'tenant_id' => $tenant->id,
'new_expires_at' => $newExpiry->toDateString(),
]);
return true;
}
/**
* 데모 → 정식 전환
*/
public function convertToProduction(Tenant $tenant): void
{
$tenant->convertToProduction();
Log::info('데모 → 정식 테넌트 전환', [
'tenant_id' => $tenant->id,
'company_name' => $tenant->company_name,
]);
}
/**
* 사용 가능한 프리셋 목록
*/
public function getAvailablePresets(): array
{
return self::AVAILABLE_PRESETS;
}
/**
* 만료된 체험 테넌트 조회
*/
public function getExpiredTrials(): \Illuminate\Database\Eloquent\Collection
{
return Tenant::withoutGlobalScopes()
->where('tenant_type', Tenant::TYPE_DEMO_TRIAL)
->whereNotNull('demo_expires_at')
->where('demo_expires_at', '<', now())
->get();
}
}