Группы пользователей
Модуль "Группы пользователей"
Версия: 1.0
Дата: 2025-11-19
Статус: Реализован
Содержание
- Общее описание
- Текущая реализация
- Права доступа
- Описание полей
- UI/UX
- Бизнес-логика
- Выявленные проблемы
- Рекомендации
1. Общее описание
1.1 Предназначение модуля
Модуль "Группы пользователей" предназначен для справочного просмотра групп пользователей системы. Это базовый справочник ролей, к которым привязываются пользователи.
Основные функции:
- Просмотр списка групп
- Отображение количества пользователей в каждой группе
- Базовые данные для системы прав доступа
1.2 Связь с другими модулями
| Модуль | Связь |
|---|---|
| Все пользователи | user.group_id → определяет роль пользователя |
| Политики доступа | group_policies → групповые права по умолчанию |
| Администраторы | group_id = 1 |
| Дилеры | group_id = 2 |
| Менеджеры | group_id = 3 |
| Производство | group_id = 4 |
1.3 Статистика
| ID | Группа | Пользователей |
|---|---|---|
| 1 | Администратор | 6 |
| 2 | Дилер | 13 |
| 3 | Менеджер | 10 |
| 4 | Производство | 1 |
Всего групповых политик: 124
2. Текущая реализация
2.1 Архитектура
┌─────────────────────────────────────────────────────────────────────────────┐
│ МОДУЛЬ "ГРУППЫ ПОЛЬЗОВАТЕЛЕЙ" │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ РОУТЫ (routes/web.php) │
│ └── GET /groups → GroupController@index → Список групп │
│ │
│ КОНТРОЛЛЕР (GroupController.php) │
│ └── БЕЗ ПРОВЕРКИ ПРАВ! Доступен всем авторизованным │
│ │
│ LIVEWIRE КОМПОНЕНТЫ │
│ └── Groups/Listing.php — Таблица групп (только просмотр) │
│ │
│ МОДЕЛИ │
│ ├── Group — группа пользователей │
│ └── GroupPolicy — связь группы с политикой │
│ │
└─────────────────────────────────────────────────────────────────────────────┘2.2 Структура БД
Таблица groups
| Поле | Тип | Описание |
|---|---|---|
id | bigint | PK |
name | string | Название группы |
manager | boolean | Флаг менеджера (не используется) |
⚠️ Примечание: Таблица
groupsсоздаётся в 2013 году (2013_03_06_193349), что раньше миграции users. Это legacy-дизайн.
Таблица group_policies
| Поле | Тип | Описание |
|---|---|---|
id | bigint | PK |
group_id | FK | Группа |
policy_id | FK | Политика |
value | boolean | Включена / выключена |
2.3 Файлы модуля
app/
├── Http/Controllers/
│ └── GroupController.php
├── Livewire/Groups/
│ └── Listing.php — Только просмотр списка
└── Models/
├── Group.php
└── GroupPolicy.php
database/migrations/
├── 2013_03_06_193349_create_groups_table.php
└── 2024_03_06_194633_create_group_policies_table.php3. Права доступа
3.1 Контроллер БЕЗ проверки прав
// GroupController.php — НЕТ ПРОВЕРКИ!
public function index()
{
return view('templates.groups.table');
}⚠️ Внимание! Модуль доступен всем авторизованным пользователям. Нет проверки прав доступа.
3.2 Видимость в Sidebar
// Sidebar.php — показывается только администраторам
if ($this->authUser->group_id == 1) {
// ... другие пункты
$this->items['groups'] = 'Группы пользователей';
}Фактически: Модуль виден только администраторам (group_id = 1), но URL /groups технически доступен всем.
3.3 Матрица доступа
| Роль | Просмотр URL | Видит в меню | Редактирование |
|---|---|---|---|
| Администратор | ✅ | ✅ | ❌ |
| Менеджер | ✅ | ❌ | ❌ |
| Производство | ✅ | ❌ | ❌ |
| Дилер | ✅ | ❌ | ❌ |
4. Описание полей
4.1 Колонки таблицы
| Колонка | Описание | Особенности |
|---|---|---|
id | ID группы | Поиск |
name | Название | Поиск |
users_count | Кол-во пользователей | Агрегация counts('users') |
4.2 Модель Group
class Group extends Model
{
public $timestamps = false; // Нет created_at/updated_at
protected $guarded = false;
public function users()
{
return $this->hasMany(User::class);
}
}4.3 Модель GroupPolicy
class GroupPolicy extends Model
{
public $table = "group_policies";
public $timestamps = false;
protected $guarded = false;
}5. UI/UX
5.1 Список групп
┌─────────────────────────────────────────────────────────────────────────────┐
│ Группы пользователей │
├─────────────────────────────────────────────────────────────────────────────┤
│ [Поиск...] │
├──────────────────┬────────────────────────┬─────────────────────────────────┤
│ Id │ Название │ Кол-во пользователей │
├──────────────────┼────────────────────────┼─────────────────────────────────┤
│ 1 │ Администратор │ 6 │
│ 2 │ Дилер │ 13 │
│ 3 │ Менеджер │ 10 │
│ 4 │ Производство │ 1 │
└──────────────────┴────────────────────────┴─────────────────────────────────┘
Особенности:
❌ Нет кнопки "Создать"
❌ Нет действий со строками
❌ Нет редактирования
❌ Нет ссылок на пользователей
✅ Только просмотр и поиск6. Бизнес-логика
6.1 Роль group_id в системе
Поле group_id в таблице users определяет роль пользователя:
| group_id | Роль | Описание |
|---|---|---|
| 1 | Администратор | Полный доступ |
| 2 | Дилер | Представитель дилерской компании |
| 3 | Менеджер | Куратор дилеров |
| 4 | Производство | Работник производства |
6.2 Групповые политики (GroupPolicy)
Для дилеров (group_id = 2) права берутся напрямую из group_policies:
// User.php — check_access()
if ($this->group_id == 2) {
$out = GroupPolicy::where('group_id', 2)
->where('policy_id', $policy->id)
->first();
} else {
$out = $this->user_policies()
->where('policy_id', $policy->id)
->first();
}Важно: Дилеры не имеют индивидуальных политик (user_policies). Все их права определяются групповыми политиками.
6.3 Копирование политик при создании менеджера
При создании менеджера (group_id = 3) групповые политики копируются в индивидуальные:
// User.php — boot()
static::created(function (User $user): void {
if ((int) $user->group_id === 3) {
$user->seedManagerPoliciesFromGroupDefaults();
}
});
// Метод копирования
public function seedManagerPoliciesFromGroupDefaults(): void
{
$groupPolicies = GroupPolicy::query()->where('group_id', 3)->get();
foreach ($groupPolicies as $gp) {
UserPolicy::firstOrCreate(
['user_id' => $this->id, 'policy_id' => $gp->policy_id],
['value' => $gp->value]
);
}
}6.4 Редактирование групповых политик
Групповые политики редактируются в модуле "Политики доступа" (/policies):
// Policies/Listing.php
Tables\Columns\ToggleColumn::make('admin.value') // group_id = 1
Tables\Columns\ToggleColumn::make('manager.value') // group_id = 3
Tables\Columns\ToggleColumn::make('dealer.value') // group_id = 2
Tables\Columns\ToggleColumn::make('production.value') // group_id = 46.5 Схема прав доступа
┌─────────────────────────────────────────────────────────────────────────────┐
│ СИСТЕМА ПРАВ ДОСТУПА │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ groups │ │ policies │ │
│ │ (4 группы) │ │ (57+ политик) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ └──────────┬─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ group_policies │ ← Групповые права по умолчанию │
│ │ (124 записи) │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │Дилеры │ │Менедж.│ │Админы │ │
│ │(gr=2) │ │(gr=3) │ │(gr=1) │ │
│ └───┬───┘ └───┬───┘ └───┬───┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌─────────────────────┐ │
│ │ │ user_policies │ ← Индивидуальные права │
│ │ │ (копия при создании)│ │
│ │ └─────────────────────┘ │
│ │ │
│ └──────► Права из group_policies напрямую (без user_policies) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘7. Выявленные проблемы
7.1 Критические
| # | Проблема | Файл | Влияние |
|---|---|---|---|
| — | Критических проблем не обнаружено | — | — |
7.2 Средние
| # | Проблема | Описание | Решение |
|---|---|---|---|
| 1 | Нет проверки прав в контроллере | URL доступен всем авторизованным | Добавить проверку group_can_view |
| 2 | Listing рендерит неправильный view | 'livewire.news.listing' вместо правильного | Заменить на 'livewire.base.listing' |
| 3 | Неиспользуемое поле manager | В таблице groups есть флаг manager | Удалить или использовать |
7.3 Низкие
| # | Проблема | Описание |
|---|---|---|
| 4 | Нет CRUD | Нельзя создать/редактировать группы |
| 5 | Нет ссылки на пользователей | Нельзя перейти к списку пользователей группы |
| 6 | Нет групповых политик в UI | Нужно идти в /policies |
8. Рекомендации
8.1 Приоритет: Высокий
Добавить проверку прав в контроллер
// GroupController.php — было:
public function index()
{
return view('templates.groups.table');
}
// Стало:
public function index(Request $request)
{
$user = $request->user();
if (!$user or $user->group_id != 1) // Только админы
abort(403);
return view('templates.groups.table');
}8.2 Приоритет: Средний
Исправить view в Listing
// Listing.php:43 — было:
return view('livewire.news.listing');
// Стало:
return view('livewire.base.listing');Добавить ссылку на пользователей группы
->columns([
Tables\Columns\TextColumn::make('id'),
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('users_count')
->counts('users')
->url(fn ($record) => route('users-by-group', $record->id)), // Если роут есть
])8.3 Приоритет: Низкий
Добавить колонку с групповыми политиками
Tables\Columns\TextColumn::make('policies_count')
->label('Политик')
->getStateUsing(fn ($record) => GroupPolicy::where('group_id', $record->id)->where('value', true)->count()),Приложение: SQL для аналитики
Группы с количеством пользователей
SELECT
g.id,
g.name,
COUNT(u.id) as users_count
FROM groups g
LEFT JOIN users u ON g.id = u.group_id
GROUP BY g.id, g.name
ORDER BY g.id;Включённые политики по группам
SELECT
g.name as group_name,
p.key as policy_key,
p.name as policy_name,
gp.value
FROM group_policies gp
JOIN groups g ON gp.group_id = g.id
JOIN policies p ON gp.policy_id = p.id
WHERE gp.value = 1
ORDER BY g.id, p.category, p.name;Количество политик по группам
SELECT
g.name,
COUNT(CASE WHEN gp.value = 1 THEN 1 END) as enabled,
COUNT(CASE WHEN gp.value = 0 THEN 1 END) as disabled,
COUNT(*) as total
FROM groups g
LEFT JOIN group_policies gp ON g.id = gp.group_id
GROUP BY g.id, g.name;Документ подготовлен на основе анализа исходного кода проекта