- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가 - PlanService, SubscriptionService, PaymentService 생성 - PlanController, SubscriptionController, PaymentController 생성 - FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개) - Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi) - API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개) - Pint 코드 스타일 정리
165 lines
4.5 KiB
PHP
165 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Tenants\Plan;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
class PlanService extends Service
|
|
{
|
|
// =========================================================================
|
|
// 요금제 목록/상세
|
|
// =========================================================================
|
|
|
|
/**
|
|
* 요금제 목록 (관리자용)
|
|
*/
|
|
public function index(array $params): LengthAwarePaginator
|
|
{
|
|
$query = Plan::query();
|
|
|
|
// 활성 상태 필터
|
|
if (isset($params['is_active'])) {
|
|
$query->where('is_active', (bool) $params['is_active']);
|
|
}
|
|
|
|
// 결제 주기 필터
|
|
if (! empty($params['billing_cycle'])) {
|
|
$query->ofCycle($params['billing_cycle']);
|
|
}
|
|
|
|
// 검색 (이름, 코드, 설명)
|
|
if (! empty($params['search'])) {
|
|
$search = $params['search'];
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%")
|
|
->orWhere('code', 'like', "%{$search}%")
|
|
->orWhere('description', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
// 정렬
|
|
$sortBy = $params['sort_by'] ?? 'price';
|
|
$sortDir = $params['sort_dir'] ?? 'asc';
|
|
$query->orderBy($sortBy, $sortDir);
|
|
|
|
$perPage = $params['per_page'] ?? 20;
|
|
|
|
return $query->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* 활성 요금제 목록 (공개용)
|
|
*/
|
|
public function active(): Collection
|
|
{
|
|
return Plan::active()
|
|
->orderBy('price', 'asc')
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* 요금제 상세
|
|
*/
|
|
public function show(int $id): Plan
|
|
{
|
|
return Plan::query()
|
|
->withCount(['subscriptions as active_subscriptions_count' => function ($q) {
|
|
$q->where('status', 'active');
|
|
}])
|
|
->findOrFail($id);
|
|
}
|
|
|
|
// =========================================================================
|
|
// 요금제 생성/수정/삭제
|
|
// =========================================================================
|
|
|
|
/**
|
|
* 요금제 생성
|
|
*/
|
|
public function store(array $data): Plan
|
|
{
|
|
$userId = $this->apiUserId();
|
|
|
|
return Plan::create([
|
|
'name' => $data['name'],
|
|
'code' => $data['code'],
|
|
'description' => $data['description'] ?? null,
|
|
'price' => $data['price'],
|
|
'billing_cycle' => $data['billing_cycle'] ?? Plan::BILLING_MONTHLY,
|
|
'features' => $data['features'] ?? null,
|
|
'is_active' => $data['is_active'] ?? true,
|
|
'created_by' => $userId,
|
|
'updated_by' => $userId,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 요금제 수정
|
|
*/
|
|
public function update(int $id, array $data): Plan
|
|
{
|
|
$userId = $this->apiUserId();
|
|
|
|
$plan = Plan::findOrFail($id);
|
|
|
|
$plan->fill([
|
|
'name' => $data['name'] ?? $plan->name,
|
|
'code' => $data['code'] ?? $plan->code,
|
|
'description' => $data['description'] ?? $plan->description,
|
|
'price' => $data['price'] ?? $plan->price,
|
|
'billing_cycle' => $data['billing_cycle'] ?? $plan->billing_cycle,
|
|
'features' => $data['features'] ?? $plan->features,
|
|
'is_active' => $data['is_active'] ?? $plan->is_active,
|
|
'updated_by' => $userId,
|
|
]);
|
|
|
|
$plan->save();
|
|
|
|
return $plan->fresh();
|
|
}
|
|
|
|
/**
|
|
* 요금제 삭제
|
|
*/
|
|
public function destroy(int $id): bool
|
|
{
|
|
$userId = $this->apiUserId();
|
|
|
|
$plan = Plan::findOrFail($id);
|
|
|
|
// 활성 구독이 있으면 삭제 불가
|
|
$activeCount = $plan->subscriptions()
|
|
->whereIn('status', ['active', 'pending'])
|
|
->count();
|
|
|
|
if ($activeCount > 0) {
|
|
throw new \Symfony\Component\HttpKernel\Exception\BadRequestHttpException(
|
|
__('error.plan.has_active_subscriptions')
|
|
);
|
|
}
|
|
|
|
$plan->deleted_by = $userId;
|
|
$plan->save();
|
|
$plan->delete();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 요금제 활성/비활성 토글
|
|
*/
|
|
public function toggle(int $id): Plan
|
|
{
|
|
$userId = $this->apiUserId();
|
|
|
|
$plan = Plan::findOrFail($id);
|
|
$plan->is_active = ! $plan->is_active;
|
|
$plan->updated_by = $userId;
|
|
$plan->save();
|
|
|
|
return $plan;
|
|
}
|
|
}
|