feat(subscription): implement resource quota management based on subscription plans

This commit is contained in:
hamed
2026-08-04 19:38:49 +03:30
parent 0dca245246
commit 3e7028d77a
16 changed files with 408 additions and 18 deletions
@@ -83,6 +83,7 @@ class SubscriptionController extends BaseController
'effective_plan' => $effectivePlan === null ? null : [
'features' => $effectivePlan->getFeatures(),
'max_secretaries' => $effectivePlan->getMaxSecretaries(),
'max_resources' => $effectivePlan->getMaxResources(),
],
]);
}
@@ -148,7 +149,8 @@ class SubscriptionController extends BaseController
$name,
(int) $data['level'],
(int) ($data['max_secretaries'] ?? 1),
$data['features'] ?? []
$data['features'] ?? [],
(int) ($data['max_resources'] ?? 1)
);
$this->planRepo->save($plan);
@@ -176,6 +178,7 @@ class SubscriptionController extends BaseController
}
if (isset($data['level'])) { $plan->setLevel((int) $data['level']); }
if (isset($data['max_secretaries'])) { $plan->setMaxSecretaries((int) $data['max_secretaries']); }
if (isset($data['max_resources'])) { $plan->setMaxResources((int) $data['max_resources']); }
if (isset($data['features'])) { $plan->setFeatures($data['features']); }
if (isset($data['active'])) { $plan->setActive((bool) $data['active']); }
+12 -1
View File
@@ -12,6 +12,9 @@ use Symfony\Component\Uid\Uuid;
#[ORM\Table(name: 'subscription_plans')]
class SubscriptionPlan
{
/** مقدارِ «بی‌نهایت» برای سقف‌های عددی — منفی است تا با هیچ شمارشِ واقعی اشتباه نشود. */
public const UNLIMITED = -1;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
@@ -29,6 +32,10 @@ class SubscriptionPlan
#[ORM\Column(name: 'max_secretaries', type: 'smallint')]
private int $maxSecretaries = 1;
/** سقف منابع محیط؛ `self::UNLIMITED` یعنی بی‌نهایت. */
#[ORM\Column(name: 'max_resources', type: 'smallint')]
private int $maxResources = 1;
#[ORM\Column(type: 'json')]
private array $features = [];
@@ -44,12 +51,13 @@ class SubscriptionPlan
#[ORM\OneToMany(targetEntity: SubscriptionPeriod::class, mappedBy: 'plan')]
private Collection $periods;
public function __construct(string $name, int $level, int $maxSecretaries, array $features)
public function __construct(string $name, int $level, int $maxSecretaries, array $features, int $maxResources = 1)
{
$this->uuid = Uuid::v4()->toRfc4122();
$this->name = $name;
$this->level = $level;
$this->maxSecretaries = $maxSecretaries;
$this->maxResources = $maxResources;
$this->features = $features;
$this->createdAt = time();
$this->updatedAt = time();
@@ -61,6 +69,7 @@ class SubscriptionPlan
public function getName(): string { return $this->name; }
public function getLevel(): int { return $this->level; }
public function getMaxSecretaries(): int { return $this->maxSecretaries; }
public function getMaxResources(): int { return $this->maxResources; }
public function getFeatures(): array { return $this->features; }
public function isActive(): bool { return $this->active; }
public function getCreatedAt(): int { return $this->createdAt; }
@@ -70,6 +79,7 @@ class SubscriptionPlan
public function setName(string $name): self { $this->name = $name; $this->updatedAt = time(); return $this; }
public function setLevel(int $level): self { $this->level = $level; $this->updatedAt = time(); return $this; }
public function setMaxSecretaries(int $v): self { $this->maxSecretaries = $v; $this->updatedAt = time(); return $this; }
public function setMaxResources(int $v): self { $this->maxResources = $v; $this->updatedAt = time(); return $this; }
public function setFeatures(array $features): self { $this->features = $features; $this->updatedAt = time(); return $this; }
public function setActive(bool $active): self { $this->active = $active; $this->updatedAt = time(); return $this; }
@@ -85,6 +95,7 @@ class SubscriptionPlan
'name' => $this->name,
'level' => $this->level,
'max_secretaries' => $this->maxSecretaries,
'max_resources' => $this->maxResources,
'features' => $this->features,
'active' => $this->active,
];
@@ -50,6 +50,19 @@ class SubscriptionService
return $plan?->getMaxSecretaries() ?? 1;
}
/**
* سقف منابعِ محیط — `SubscriptionPlan::UNLIMITED` یعنی بی‌نهایت.
*
* نبودِ اشتراک به پلن `free` می‌رسد (`getEffectivePlan`)، پس همان‌جا سقف ۱ گرفته
* می‌شود و لازم نیست «پرداختی دارد یا نه» جداگانه پرسیده شود.
*/
public function getResourceLimit(string $entityType, int $entityId): int
{
$plan = $this->getEffectivePlan($entityType, $entityId);
return $plan?->getMaxResources() ?? 1;
}
public function hasUsedTrial(string $entityType, int $entityId): bool
{
return $this->subscriptionRepo->hasUsedTrial($entityType, $entityId);