bp/app/Services/ModuleSubscriptionService.php

379 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use CodeIgniter\Config\BaseConfig;
/**
* Сервис для работы с подписками организаций на модули
*
* Предоставляет API для проверки доступности модулей,
* управления подписками и получения информации о модулях.
*/
class ModuleSubscriptionService
{
/**
* Конфигурация модулей (базовые значения)
*/
protected array $modulesConfig = [
'base' => [
'name' => 'Базовый модуль',
'description' => 'Основные функции',
'price_monthly' => 0,
'price_yearly' => 0,
'trial_days' => 0,
],
'crm' => [
'name' => 'CRM',
'description' => 'Управление клиентами и сделками',
'price_monthly' => 990,
'price_yearly' => 9900,
'trial_days' => 14,
],
'booking' => [
'name' => 'Бронирования',
'description' => 'Управление бронированиями',
'price_monthly' => 1490,
'price_yearly' => 14900,
'trial_days' => 14,
],
'tasks' => [
'name' => 'Задачи',
'description' => 'Управление задачами',
'price_monthly' => 790,
'price_yearly' => 7900,
'trial_days' => 14,
],
'proof' => [
'name' => 'Proof',
'description' => 'Согласование документов',
'price_monthly' => 590,
'price_yearly' => 5900,
'trial_days' => 14,
],
];
protected $db;
protected string $moduleSettingsTable = 'module_settings';
protected string $subscriptionsTable = 'organization_subscriptions';
public function __construct()
{
$this->db = \Config\Database::connect();
}
/**
* Получение базовой конфигурации модуля
*/
public function getModuleConfig(string $moduleCode): ?array
{
return $this->modulesConfig[$moduleCode] ?? null;
}
/**
* Получение всех модулей (с учётом переопределений из БД)
*/
public function getAllModules(): array
{
$modules = $this->modulesConfig;
$builder = $this->db->table($this->moduleSettingsTable);
$settings = $builder->get()->getResultArray();
foreach ($settings as $setting) {
$code = $setting['module_code'];
if (isset($modules[$code])) {
if (!empty($setting['name'])) {
$modules[$code]['name'] = $setting['name'];
}
if (isset($setting['description']) && $setting['description'] !== '') {
$modules[$code]['description'] = $setting['description'];
}
if (isset($setting['price_monthly'])) {
$modules[$code]['price_monthly'] = (int) $setting['price_monthly'];
}
if (isset($setting['price_yearly'])) {
$modules[$code]['price_yearly'] = (int) $setting['price_yearly'];
}
if (isset($setting['trial_days'])) {
$modules[$code]['trial_days'] = (int) $setting['trial_days'];
}
}
}
return $modules;
}
/**
* Проверка активности модуля для организации
*/
public function isModuleActive(string $moduleCode, ?int $organizationId = null): bool
{
if ($moduleCode === 'base') {
return true;
}
$orgId = $organizationId ?? session()->get('active_org_id');
if (!$orgId) {
return false;
}
$subscription = $this->getSubscription($orgId, $moduleCode);
if (!$subscription) {
return false;
}
return $subscription['status'] === 'active';
}
/**
* Проверка доступности модуля (активен или в триале)
*/
public function isModuleAvailable(string $moduleCode, ?int $organizationId = null): bool
{
if ($moduleCode === 'base') {
return true;
}
$orgId = $organizationId ?? session()->get('active_org_id');
if (!$orgId) {
return false;
}
$subscription = $this->getSubscription($orgId, $moduleCode);
if (!$subscription) {
return false;
}
return in_array($subscription['status'], ['active', 'trial'], true);
}
/**
* Получение подписки организации на модуль
*/
public function getSubscription(int $organizationId, string $moduleCode): ?array
{
$builder = $this->db->table($this->subscriptionsTable);
return $builder->where('organization_id', $organizationId)
->where('module_code', $moduleCode)
->get()
->getRowArray();
}
/**
* Получение всех подписок организации
*/
public function getOrganizationSubscriptions(int $organizationId): array
{
$builder = $this->db->table($this->subscriptionsTable);
return $builder->where('organization_id', $organizationId)
->orderBy('created_at', 'DESC')
->get()
->getResultArray();
}
/**
* Получение всех активных модулей организации
*/
public function getActiveModules(int $organizationId): array
{
$builder = $this->db->table($this->subscriptionsTable);
$subscriptions = $builder->where('organization_id', $organizationId)
->whereIn('status', ['active', 'trial'])
->get()
->getResultArray();
$modules = array_column($subscriptions, 'module_code');
$modules[] = 'base';
return array_unique($modules);
}
/**
* Создание/обновление подписки
*/
public function upsertSubscription(
int $organizationId,
string $moduleCode,
string $status = 'active',
?int $days = null
): bool {
$existing = $this->getSubscription($organizationId, $moduleCode);
$data = [
'organization_id' => $organizationId,
'module_code' => $moduleCode,
'status' => $status,
'expires_at' => $days > 0 ? date('Y-m-d H:i:s', strtotime("+{$days} days")) : null,
'updated_at' => date('Y-m-d H:i:s'),
];
if ($existing) {
return $this->db->table($this->subscriptionsTable)
->where('id', $existing['id'])
->update($data);
}
$data['created_at'] = date('Y-m-d H:i:s');
return $this->db->table($this->subscriptionsTable)->insert($data);
}
/**
* Удаление подписки
*/
public function deleteSubscription(int $subscriptionId): bool
{
return $this->db->table($this->subscriptionsTable)
->where('id', $subscriptionId)
->delete();
}
/**
* Запуск триала для модуля
*/
public function startTrial(int $organizationId, string $moduleCode, int $trialDays = 14): bool
{
$config = $this->getModuleConfig($moduleCode);
if (!$config || $config['trial_days'] <= 0) {
return false;
}
return $this->upsertSubscription(
$organizationId,
$moduleCode,
'trial',
$trialDays
);
}
/**
* Активация подписки
*/
public function activate(int $organizationId, string $moduleCode, int $months = 1): bool
{
return $this->upsertSubscription(
$organizationId,
$moduleCode,
'active',
$months * 30
);
}
/**
* Отмена подписки
*/
public function cancel(int $organizationId, string $moduleCode): bool
{
$existing = $this->getSubscription($organizationId, $moduleCode);
if (!$existing) {
return false;
}
return $this->db->table($this->subscriptionsTable)
->where('id', $existing['id'])
->update(['status' => 'cancelled', 'updated_at' => date('Y-m-d H:i:s')]);
}
/**
* Получение всех подписок (для суперадмина)
*/
public function getAllSubscriptions(): array
{
$builder = $this->db->table($this->subscriptionsTable);
return $builder
->select('organization_subscriptions.*, organizations.name as organization_name')
->join('organizations', 'organizations.id = organization_subscriptions.organization_id')
->orderBy('organization_subscriptions.created_at', 'DESC')
->get()
->getResultArray();
}
/**
* Статистика по модулям (для суперадмина)
*/
public function getModuleStats(): array
{
$stats = [];
$modules = $this->getAllModules();
foreach ($modules as $code => $module) {
$activeCount = $this->db->table($this->subscriptionsTable)
->where('module_code', $code)
->where('status', 'active')
->countAllResults();
$trialCount = $this->db->table($this->subscriptionsTable)
->where('module_code', $code)
->where('status', 'trial')
->countAllResults();
$stats[$code] = [
'name' => $module['name'],
'active' => $activeCount,
'trial' => $trialCount,
];
}
return $stats;
}
/**
* Сохранение настроек модуля
*/
public function saveModuleSettings(
string $moduleCode,
?string $name = null,
?string $description = null,
?int $priceMonthly = null,
?int $priceYearly = null,
?int $trialDays = null
): bool {
$existing = $this->db->table($this->moduleSettingsTable)
->where('module_code', $moduleCode)
->get()
->getRowArray();
$data = [
'module_code' => $moduleCode,
'updated_at' => date('Y-m-d H:i:s'),
];
if ($name !== null) {
$data['name'] = $name;
}
if ($description !== null) {
$data['description'] = $description;
}
if ($priceMonthly !== null) {
$data['price_monthly'] = $priceMonthly;
}
if ($priceYearly !== null) {
$data['price_yearly'] = $priceYearly;
}
if ($trialDays !== null) {
$data['trial_days'] = $trialDays;
}
if ($existing) {
return $this->db->table($this->moduleSettingsTable)
->where('id', $existing['id'])
->update($data);
}
$data['created_at'] = date('Y-m-d H:i:s');
$data['is_active'] = 1;
return $this->db->table($this->moduleSettingsTable)->insert($data);
}
/**
* Получение настроек модуля
*/
public function getModuleSettings(string $moduleCode): ?array
{
return $this->db->table($this->moduleSettingsTable)
->where('module_code', $moduleCode)
->get()
->getRowArray();
}
}