feat: complete administration and reporting
This commit is contained in:
135
app/Domain/Identity/Services/UserAdministrationService.php
Normal file
135
app/Domain/Identity/Services/UserAdministrationService.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Identity\Services;
|
||||
|
||||
use App\Domain\Audit\Models\AuditLog;
|
||||
use App\Domain\Identity\Enums\RoleName;
|
||||
use App\Domain\Identity\Models\Role;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
final class UserAdministrationService
|
||||
{
|
||||
/**
|
||||
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:string,active:bool,roles:list<string>} $data
|
||||
*/
|
||||
public function create(User $actor, array $data): User
|
||||
{
|
||||
return DB::transaction(function () use ($actor, $data): User {
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'email_verified_at' => now(),
|
||||
'department' => $data['department'],
|
||||
'snipe_user_id' => $data['snipe_user_id'],
|
||||
'password' => $data['password'],
|
||||
'active' => $data['active'],
|
||||
]);
|
||||
$user->roles()->sync($this->roleIds($data['roles']));
|
||||
|
||||
AuditLog::create([
|
||||
'actor_id' => $actor->id,
|
||||
'event' => 'user.created',
|
||||
'auditable_type' => $user->getMorphClass(),
|
||||
'auditable_id' => $user->id,
|
||||
'after' => $this->snapshot($user),
|
||||
]);
|
||||
|
||||
return $user->load('roles');
|
||||
}, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name:string,email:string,department:?string,snipe_user_id:?int,password:?string,active:bool,roles:list<string>} $data
|
||||
*/
|
||||
public function update(User $user, User $actor, array $data): User
|
||||
{
|
||||
return DB::transaction(function () use ($user, $actor, $data): User {
|
||||
$locked = User::query()->whereKey($user->id)->lockForUpdate()->firstOrFail();
|
||||
$before = $this->snapshot($locked);
|
||||
$removesAdministrator = ! in_array(RoleName::Administrator->value, $data['roles'], true);
|
||||
|
||||
if ($locked->id === $actor->id && (! $data['active'] || $removesAdministrator)) {
|
||||
throw ValidationException::withMessages([
|
||||
'roles' => 'Não pode desativar a própria conta nem remover o seu perfil de administrador.',
|
||||
]);
|
||||
}
|
||||
|
||||
if ((! $data['active'] || $removesAdministrator)
|
||||
&& $locked->hasRole(RoleName::Administrator)
|
||||
&& $this->activeAdministratorCount() <= 1) {
|
||||
throw ValidationException::withMessages([
|
||||
'roles' => 'O portal tem de manter pelo menos um administrador ativo.',
|
||||
]);
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'department' => $data['department'],
|
||||
'snipe_user_id' => $data['snipe_user_id'],
|
||||
'active' => $data['active'],
|
||||
];
|
||||
if (filled($data['password'])) {
|
||||
$attributes['password'] = $data['password'];
|
||||
}
|
||||
|
||||
$locked->update($attributes);
|
||||
$locked->roles()->sync($this->roleIds($data['roles']));
|
||||
$locked->load('roles');
|
||||
|
||||
AuditLog::create([
|
||||
'actor_id' => $actor->id,
|
||||
'event' => 'user.updated',
|
||||
'auditable_type' => $locked->getMorphClass(),
|
||||
'auditable_id' => $locked->id,
|
||||
'before' => $before,
|
||||
'after' => $this->snapshot($locked),
|
||||
'metadata' => ['password_changed' => filled($data['password'])],
|
||||
]);
|
||||
|
||||
return $locked;
|
||||
}, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $roles
|
||||
* @return list<int>
|
||||
*/
|
||||
private function roleIds(array $roles): array
|
||||
{
|
||||
$ids = array_values(Role::query()
|
||||
->whereIn('name', $roles)
|
||||
->pluck('id')
|
||||
->map(fn (int $id): int => $id)
|
||||
->all());
|
||||
|
||||
if (count($ids) !== count(array_unique($roles))) {
|
||||
throw ValidationException::withMessages(['roles' => 'Um ou mais perfis selecionados não existem.']);
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
private function activeAdministratorCount(): int
|
||||
{
|
||||
return User::query()
|
||||
->where('active', true)
|
||||
->whereHas('roles', fn ($query) => $query->where('name', RoleName::Administrator->value))
|
||||
->count();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function snapshot(User $user): array
|
||||
{
|
||||
return [
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'department' => $user->department,
|
||||
'snipe_user_id' => $user->snipe_user_id,
|
||||
'active' => $user->active,
|
||||
'roles' => $user->roles()->orderBy('name')->pluck('name')->all(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user