добавляю все изменения проекта на текущий момент

This commit is contained in:
vasya
2026-02-27 18:49:27 +03:00
parent e927910fda
commit 9c35f4e35e
303 changed files with 79434 additions and 2558 deletions
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace App\Services;
use App\Dto\ApiResponseDto;
use Illuminate\Http\JsonResponse;
class ApiResponder{
public $dto;
public function __construct(
)
{}
public function success(): JsonResponse
{
return $this->buildResponse();
}
public function error()
{
return $this->buildResponse();
}
public function setDto(ApiResponseDto $dto)
{
$this->dto = $dto;
}
// public function fromDTO(ApiResponseDto $dto)
// {
// return $dto;
// }
#Гаврилов
//СБОР СТАТИСТИКИ ПО ВЫЗЫВАЕМЫМ API ЕНДПОИНТАМ. ВНЕСТИ В TODO ПРОЕКТА
private function setStatistics()
{
}
#Гаврилов
//ДОБАВЬ ЛОГИРОВАНИЕ ОШИБОК В TRY CATCH В .LOG ФАЙЛ
private function buildResponse(): JsonResponse
{
//echo '<pre>'; var_dump((array)$this->dto); echo'</pre>';
//return (array) $this->dto;
return response()->json((array) $this->dto);
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\Services;
use App\Models\MagicApps;
use App\Facades\UserContext;
class AuthorizationService
{
private $appWithRoles;
/**
* Получаем приложения вместе с ролями
*
* @return array
*/
public function getUserAppPermissions(): array
{
$this->appWithRoles = MagicApps::with('appRoles')->get()->toArray();
//Итоговый массив с доступами пользователя к приложению и ролью
$userAppAccess = [];
foreach ($this->appWithRoles as $appData) {
//Определяем по какому массиву проверять доступ к приложению - почтовые рассылки или группы AD
if (empty($appData['access_groups_email'])) {
$appAccess = explode(';', $appData['access_groups_ad']);
$userAccessGroups = UserContext::getUserADGroups();
} else {
$appAccess = explode(';', $appData['access_groups_email']);
$userAccessGroups = UserContext::getUserEmails();
}
//Если пересечения групп доступа приложения и групп доступа пользователя отсутствуют, считаем, что доступа нет
if (empty(array_intersect($appAccess, $userAccessGroups))) {
continue;
}
//Если ролевая модель отсутствует для приложения как таковая, прописывам значение null
if (empty($appData['app_roles'])) {
$userAppAccess[$appData['app_name']] = null;
} else {
//Если ролевая модель существует, ставим значение false, которое будет заменено ролью пользователя. Сохранение false на выходе, в свою очередь, сигнализирует, что роль пользователя не определилась
$userAppAccess[$appData['app_name']] = false;
foreach ($appData['app_roles'] as $roleData) {
if($this->checkRoleAccess($appData['role_driver'], explode(';', $roleData['role_access']))) {
$userAppAccess[$appData['app_name']] = $roleData['app_role'];
//Все роли идут по приоритету важности, поэтому останавливаемся на самой важной и возвращаем ее, остальные роли не перебираем. Я пока не уверен как лучше организовать отображение функционала ролей в приложениях: весь нужный функционал "класть" в одну роль и отображать строго тот функционал, который относится к роли. Или распределять функционал между ролями и, если пользователь входит в несколько ролей, отображать весь функционал всех ролей, куда он может входить. В случае последнего варианта, нужно будет хранить все доступные роли пользователя, а не только самую важную по приортету.
//Также пока непонятно, реализовывать ли возможность переключаться между ролями на продакшн среде. Если реализовывать, так же придется хранить все роли, куда входит пользователь. Но в этом случае есть риски запутаться в логировании, например, если пользователь под ролью админ переключится на пользователя юзер и совершит от него действие
break;
}
}
}
}
return $userAppAccess;
}
/**
* Возвращаем результат проверки доступности роли
*
* @param string $roleAccessDriver название драйвера доступа роли
* @param array $roleData массив с перечисленными рассылками/логинами/группами, которым доступна роль
* @return bool
*/
public function checkRoleAccess(string $roleAccessDriver, array $roleData): bool
{
switch ($roleAccessDriver) {
case 'login':
return in_array(UserContext::getUserLogin(), $roleData);
break;
case 'email':
return !empty(array_intersect(UserContext::getUserEmails(), $roleData));
break;
case 'ADgroup':
return !empty(array_intersect(UserContext::getUserADGroups(), $roleData));
break;
}
}
//УДАЛИТЬ
// public function getUserAppRoles(array $userGroups)
// {
// $userEmails = session()->get('_auth_groups');
// $userLogin = session()->get('_auth_login');
// $magicApps = $this->getAppRoles();
// }
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/**Сервис для определения модуля
@author dgavrilov
*/
namespace App\Services;
class ModuleService{
/**
* Получаем имя модуля из роута (так как в нем обязательно указывается префикс)
*
* @return string | null
*/
public function getModuleName(): string | null
{
$route = request()->route();
$routePrefix = null;
if ($route && $route->getPrefix()) {
$routePrefix = explode('/', $route->getPrefix())[1];
}
return $routePrefix;
}
/**
* Основываясь на имени модуля из роута получаем имя роута на русском (свойство name_ru), которое обязательное прописывается в конфиге модуля
*
* @return string | null
*/
public function getRuModuleName(): string | null
{
if ($module = $this->getModuleName()) {
return config("$module.name_ru", null);
} else {
return null;
}
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace App\Services;
/**
* Базовый класс для работы с Redis в контексте модульной структуры платформы
*/
abstract class RedisBaseService
{
/**
* @var string базовое подключение к Redis. Руками не прописывается, устанавливается в конструкторе дочернего сервиса через недоступный из фасада метод protected connection(). Сделано это, чтобы разработчик не указывал вручную подключение каждый раз b при этом любой дочерний сервис был обязан прописывать подключение
*/
protected string $connection;
/**
* @var int базовый ttl для данных с ограниченным сроком жизни
*/
public int $ttl = 600;
/**
* @var string имя модуля/приложения откуда отправляются данные. Передается в инициилизирующем методе module(). Значение '' означает, что данные отправляются из корня проекта, не из модуля
*/
public string $moduleName;
/**
* Метод установки подключения к Redis. В какую БД кладем данные
*
* @param string $connection имя подключения
* @return void
*/
abstract protected function connection(string $connection): void;
/**
* Инициилизирующий метод фасада, принимающий аргумент в виде названия модуля, откуда планируется отправить данные.
*
* @param string $moduleName имя модуля, откуда передаются данные. Аргумент можно не передавать, если данные передаются из корня ларавель
* @return self
*/
public function module(string $moduleName = ''): self
{
$this->moduleName = $moduleName;
return $this;
}
/**
* Меняем ttl данных
*
* @param int $ttl время жизни записи с данными
* @return self
*/
public function ttl(int $ttl): self
{
$this->ttl = $ttl;
return $this;
}
/**
* Возвращает часть префикса ключа redis для хранения информации, отправленной из модуля
*
* @return string
*/
protected function getModuleRedisPrefix(): string
{
return "_" . $this->moduleName . ":";
}
/**
* Реализация размещения данных в Redis
*
* @return void
*/
abstract public function put(): void;
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Redis;
use App\Facades\UserContext;
use Exception;
class RedisNotificationService extends RedisBaseService
{
protected string $connection = 'notifications';
/**
* @var array тексты уведомлений
*/
public array $notificationTexts = [];
/**
* @var array тексты нотификаций
*/
public array $notificationTypes = [];
public function __construct()
{
$this->connection('notifications');
}
protected function connection($connection): void
{
$this->connection = $connection;
//Предполагается, что время жизни должно быть небольшое, так уведомления размещаются перед самим редиректом для их демонстрации пользователю как можно скорее
$this->ttl = 180;
}
/**
* Метод возвращает id юзера, который является обязательной частью ключа в БД redis с нотификациями
*
* @return int
*/
private function identifyUser(): int
{
$userId = UserContext::getUserId();
return $userId;
}
/**
* Устанавливаем текст уведомлений для отправки в Redis
*
* @param array $notificationTexts массив текстов уведомлений
* @return self
*/
public function notifications(array $notificationTexts): self
{
$this->notificationTexts = $notificationTexts;
return $this;
}
/**
* Устанавливаем типы для передаваемых Redis уведомлений
*
* @param array $notificationTypes типы уведомлений
* @return self
*/
public function types(array $notificationTypes): self
{
//Если при передаче типов уведомлений не указаны тексты уведомлений, либо если количество текстов уведомлений не равно количеству типов уведомлений, возвращаем ошибку
if (empty($this->notificationTexts)) {
throw new Exception('Не переданы тексты уведомлений');
}
if (count($this->notificationTexts) !== count($notificationTypes)) {
throw new Exception('Количество переданных типов должно совпадать с количеством текстов уведомлений');
}
if ($incorrectTypes = array_diff($notificationTypes, config('app.FRONT_notification_types'))) {
throw new Exception('Переданные типы уведомлений ' . implode(', ', $incorrectTypes) . ' не существуют');
}
$this->notificationTypes = $notificationTypes;
return $this;
}
public function put(): void
{
$notificationsRedis = [];
if (empty($this->notificationTexts) || empty($this->notificationTypes)) {
throw new Exception('Отсутствуют обязательные параметры для отправки в Redis');
} else {
foreach ($this->notificationTexts as $key => $text) {
$notificationsRedis[] = ['text' => $text, 'type' => $this->notificationTypes[$key]];
}
}
Redis::connection($this->connection)->setex($this->getModuleRedisPrefix() . "notifications:user:" . $this->identifyUser(), $this->ttl, json_encode($notificationsRedis));
}
}
+198
View File
@@ -0,0 +1,198 @@
<?php
namespace App\Services;
use Exception;
/**
* Сервис работы с данными юзера (установка/получение логина, групп, подмена значений в случае работы на тестовой среде)
*/
class UserService
{
/**
* @var string пользовательский логин
*/
public string $userLogin;
/**
* @var array пользовательские группы AD
*/
public array $userADGroups;
/**
* @var array почтовые рассылки куда входит пользователь
*/
public array $userEmails;
/**
* @var boolean является ли пользователь админом приложения Magic
*/
public bool $isAdmin;
/**
* @var array доступы пользователя к приложениям
*/
public array $userAppPermissions;
/**
* @var int идентификатор пользователя из таблицы users
*/
public int $userId;
/**
* Установка пользовательского логина
*
* @param string $login логин для подмены превоначального значения, взятого из сессии
* @return void
*/
public function setUserLogin(string $login): void
{
$this->userLogin = $login;
}
/**
* Установка пользовательского логина
*
* @param array $appRoles доступы пользователя к приложениям Magic
* @return void
*/
public function setUserAppPermissions(array $appRoles): void
{
$this->userAppPermissions = $appRoles;
}
/**
* Установка пользовательского идентификатора из таблицы users
*
* @param int $userId идентификатор пользователя
* @return void
*/
public function setUserId(int $userId): void
{
$this->userId = $userId;
}
/**
* Установка групп AD пользователя
*
* @param array $userGroups все группы из AD где состоит пользователь (emails, AD и т.д.)
* @return void
*/
public function setUserADGroups(array $userGroups): void
{
$this->userADGroups = array_filter($userGroups, function($el){return substr($el, 0, 1) !== '#';});
}
/**
* Установка почтовых рассылок куда входит пользователь
*
* @param array $userGroups все групы пользователя AD, в которых он состоит (почтовые ящики, AD и т.д.)
* @return void
*/
public function setUserEmails(array $userGroups): void
{
$this->userEmails = array_filter($userGroups, function($el){return substr($el, 0, 1) === '#';});
}
/**
* Устанавливаем флаг является ли пользователь админом приложения Magic
*
* @param boolean $flag
* @return void
*/
public function setIsAdminFlag(bool $flag): void
{
$this->isAdmin = $flag;
}
/**
* Добавление группы AD в массив групп пользователя
*
* @param string $group группа для добавления в массив установленных при аутентификации групп AD пользователя
* @return void
*/
public function addUserADGroup(string $group): void
{
$this->userADGroups[] = $group;
}
/**
* Добавление email в массив емейлов пользователя
*
* @param string $email почтовая рассылка для добавления в массив установленных при аутентификации почтовых ящиков пользователя
* @return void
*/
public function addUserEmail(string $email): void
{
$this->userEmails[] = $email;
}
/**
* Возвращаем доступы пользователя к приложениям Magic
*
* @return array
*/
public function getUserAppPermissions(): array
{
return $this->userAppPermissions;
}
/**
* Получаем пользовательский логин
*
* @return string
*/
public function getUserLogin(): string
{
return $this->userLogin;
}
/**
* Получаем пользовательский логин
*
* @return int
*/
public function getUserId(): int
{
return $this->userId;
}
/**
* Получаем пользовательский логин
*
* @return array
*/
public function getUserADGroups(): array
{
return $this->userADGroups;
}
/**
* Получаем email рассылки куда он входит
*
* @return array
*/
public function getUserEmails(): array
{
return $this->userEmails;
}
/**
* Получаем значение флага является ли пользователь админом приложения Magic
*
* @return boolean
*/
public function getIsAdminFlag(): bool
{
return $this->isAdmin;
}
}