feat: Implement admin role and permission management, employee management APIs, and user profile extensions.

This commit is contained in:
pbao9
2026-03-10 11:43:19 +07:00
parent a70900f542
commit cf0f2cf686
14 changed files with 924 additions and 187 deletions
@@ -0,0 +1,170 @@
<?php
namespace App\Http\Controllers\Api\Admin\Employee;
use App\Http\Controllers\Controller;
use App\Http\Requests\EmployeeRequest;
use App\Repositories\Employee\EmployeeRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Role;
class EmployeeController extends Controller
{
public function __construct(
private readonly EmployeeRepositoryInterface $employeeRepository
) {}
public function index(Request $request): JsonResponse
{
$employees = $this->employeeRepository->filter($request->all());
return response()->json([
'error' => false,
'data' => $employees,
]);
}
public function store(EmployeeRequest $request): JsonResponse
{
$request->validated();
try {
DB::beginTransaction();
$employee = $this->employeeRepository->create([
'firstname' => $request->firstname,
'lastname' => $request->lastname,
'username' => $request->username,
'email' => $request->email,
'phone' => $request->phone,
'birthday' => $request->birthday,
'address' => $request->address,
'password' => Hash::make($request->password),
]);
$employee->syncRoles([$request->role]);
DB::commit();
return response()->json([
'error' => false,
'message' => 'Employee created successfully',
'data' => $employee->load('roles'),
], 201);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'error' => true,
'message' => $e->getMessage(),
], 500);
}
}
public function show(int $id): JsonResponse
{
$employee = $this->employeeRepository->find($id);
if (!$employee) {
return response()->json([
'error' => true,
'message' => "Can't find employee by ID #" . $id,
], 404);
}
return response()->json([
'error' => false,
'data' => $employee->load('roles'),
]);
}
public function update(int $id, EmployeeRequest $request): JsonResponse
{
$employee = $this->employeeRepository->find($id);
if (!$employee) {
return response()->json([
'error' => true,
'message' => "Can't find employee by ID #" . $id,
], 404);
}
$request->validated();
try {
DB::beginTransaction();
$data = [
'firstname' => $request->firstname,
'lastname' => $request->lastname,
'username' => $request->username,
'email' => $request->email,
'phone' => $request->phone,
'birthday' => $request->birthday,
'address' => $request->address,
];
if ($request->filled('password')) {
$data['password'] = Hash::make($request->password);
}
$this->employeeRepository->update($id, $data);
$employee->syncRoles([$request->role]);
DB::commit();
return response()->json([
'error' => false,
'message' => 'Employee updated successfully',
'data' => $this->employeeRepository->find($id)->load('roles'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'error' => true,
'message' => $e->getMessage(),
], 500);
}
}
public function destroy(int $id): JsonResponse
{
$employee = $this->employeeRepository->find($id);
if (!$employee) {
return response()->json([
'error' => true,
'message' => "Can't find employee by ID #" . $id,
], 404);
}
try {
DB::beginTransaction();
$this->employeeRepository->delete($id);
DB::commit();
return response()->json([
'error' => false,
'message' => 'Employee deleted successfully',
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'error' => true,
'message' => $e->getMessage(),
], 500);
}
}
public function roles(): JsonResponse
{
$roles = Role::where('guard_name', 'admin')->get(['id', 'name']);
return response()->json([
'error' => false,
'data' => $roles,
]);
}
}
@@ -0,0 +1,266 @@
<?php
namespace App\Http\Controllers\Api\Admin\Role;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RolePermissionController extends Controller
{
private function ensureFullAdmin(): bool
{
$user = auth('admin')->user();
return $user && $user->hasRole('Full Administrator');
}
/**
* GET /v1/administrator/role-permissions
* Danh sách tất cả roles (trừ Full Administrator) kèm permissions hiện tại
*/
public function index(): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$roles = Role::where('guard_name', 'admin')
->where('name', '!=', 'Full Administrator')
->with('permissions')
->get()
->map(fn ($role) => [
'id' => $role->id,
'name' => $role->name,
'permissions' => $role->permissions->pluck('name')->values(),
]);
return response()->json([
'error' => false,
'data' => $roles,
]);
}
/**
* GET /v1/administrator/role-permissions/all-permissions
* Tất cả permissions nhóm theo module
*/
public function allPermissions(): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$grouped = Permission::where('guard_name', 'admin')
->orderBy('id')
->get(['id', 'name'])
->groupBy(function ($p) {
// Format: module.action (e.g., product-service.view, customer.manager)
$parts = explode('.', $p->name);
return $parts[0] ?? $p->name;
})
->map(function ($group, $module) {
return [
'module' => $module,
'permissions' => $group->map(fn ($p) => [
'id' => $p->id,
'name' => $p->name,
'action' => explode('.', $p->name)[1] ?? $p->name,
])->values(),
];
})
->values();
return response()->json([
'error' => false,
'data' => $grouped,
]);
}
/**
* GET /v1/administrator/role-permissions/mine
* Trả về permissions hiệu lực của user đang đăng nhập (qua role của họ).
* Accessible bởi mọi admin đã xác thực — không yêu cầu Full Administrator.
*/
public function mine(): JsonResponse
{
$user = auth('admin')->user();
if (!$user) {
return response()->json(['error' => true, 'message' => 'Unauthorized'], 401);
}
$isFullAdmin = $user->hasRole('Full Administrator', 'admin');
return response()->json([
'error' => false,
'data' => [
'isFullAdmin' => $isFullAdmin,
'permissions' => $isFullAdmin
? ['*']
: $user->getAllPermissions()->pluck('name')->toArray(),
],
]);
}
/**
* POST /v1/administrator/role-permissions/permissions
* Tạo một nhóm permissions mới theo module (chỉ Full Administrator)
*/
public function createPermissionGroup(Request $request): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$module = trim($request->input('module', ''));
$actions = $request->input('actions', ['view', 'create', 'edit', 'destroy', 'manager']);
if (!$module) {
return response()->json(['error' => true, 'message' => 'Tên module không được trống'], 422);
}
// Kiểm tra module đã tồn tại chưa
$existing = Permission::where('guard_name', 'admin')
->where('name', 'like', $module . '.%')
->exists();
if ($existing) {
return response()->json(['error' => true, 'message' => "Module \"{$module}\" đã tồn tại"], 422);
}
$created = [];
foreach ($actions as $action) {
$name = $module . '.' . $action;
$perm = Permission::firstOrCreate([
'name' => $name,
'guard_name' => 'admin',
]);
$created[] = [
'id' => $perm->id,
'name' => $perm->name,
'action' => $action,
];
}
// Full Administrator tự động được cấp permissions mới
$fullAdmin = Role::where('name', 'Full Administrator')
->where('guard_name', 'admin')
->first();
if ($fullAdmin) {
$fullAdmin->givePermissionTo(array_column($created, 'name'));
}
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
return response()->json([
'error' => false,
'data' => [
'module' => $module,
'permissions' => $created,
],
], 201);
}
/**
* DELETE /v1/administrator/role-permissions/permissions/group/{module}
* Xóa toàn bộ permissions của một module (chỉ Full Administrator)
*/
public function deletePermissionGroup(string $module): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$deleted = Permission::where('guard_name', 'admin')
->where('name', 'like', $module . '.%')
->get();
if ($deleted->isEmpty()) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy nhóm quyền'], 404);
}
foreach ($deleted as $perm) {
$perm->delete();
}
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
return response()->json([
'error' => false,
'message' => "Đã xóa nhóm quyền \"{$module}\"",
'data' => ['module' => $module, 'deleted' => $deleted->count()],
]);
}
/**
* PATCH /v1/administrator/role-permissions/permissions/{id}
* Đổi tên một permission (chỉ Full Administrator)
*/
public function renamePermission(int $id, Request $request): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$permission = Permission::where('guard_name', 'admin')->find($id);
if (!$permission) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy permission'], 404);
}
$newName = trim($request->input('name', ''));
if (!$newName) {
return response()->json(['error' => true, 'message' => 'Tên permission không được trống'], 422);
}
if (Permission::where('guard_name', 'admin')->where('name', $newName)->where('id', '!=', $id)->exists()) {
return response()->json(['error' => true, 'message' => 'Tên permission đã tồn tại'], 422);
}
$permission->update(['name' => $newName]);
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
return response()->json([
'error' => false,
'data' => ['id' => $permission->id, 'name' => $permission->name],
]);
}
/**
* PUT /v1/administrator/role-permissions/{roleId}
* Sync permissions cho một role
*/
public function update(int $roleId, Request $request): JsonResponse
{
if (!$this->ensureFullAdmin()) {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập'], 403);
}
$role = Role::where('guard_name', 'admin')
->where('name', '!=', 'Full Administrator')
->find($roleId);
if (!$role) {
return response()->json([
'error' => true,
'message' => 'Không tìm thấy vai trò',
], 404);
}
$permissions = $request->input('permissions', []);
$role->syncPermissions($permissions);
// Xóa cache để /mine endpoint trả về permissions mới nhất
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
return response()->json([
'error' => false,
'message' => "Đã cập nhật quyền cho vai trò \"{$role->name}\"",
'data' => [
'id' => $role->id,
'name' => $role->name,
'permissions' => $role->permissions()->pluck('name')->values(),
],
]);
}
}