diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php index 39217f8..c35cae4 100644 --- a/app/Http/Controllers/Api/AuthController.php +++ b/app/Http/Controllers/Api/AuthController.php @@ -9,13 +9,14 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\Rules\Password; +use Spatie\Permission\Models\Role; #[Apidoc\Title('认证模块')] class AuthController extends Controller { public function __construct() { - $this->middleware('auth:api')->except('login'); + $this->middleware('auth:api')->except(['login', 'applyAccount']); $this->middleware(function (Request $request, \Closure $next) { /** @var User|null $user */ $user = Auth::guard('api')->user(); @@ -41,7 +42,7 @@ class AuthController extends Controller 'message' => '请先修改密码后再继续操作', 'data' => ['force_password_change' => true], ], 423); - })->except('login'); + })->except(['login', 'applyAccount']); } #[Apidoc\Title('登录'), Apidoc\Method('POST'), Apidoc\Url('/auth/login')] @@ -71,6 +72,19 @@ class AuthController extends Controller return response()->json(['code' => 401, 'message' => '账号或密码错误', 'data' => null], 401); } + /** @var User|null $user */ + $user = Auth::guard('api')->user(); + $permissionCount = (int) ($user?->getAllPermissions()->count() ?? 0); + if ($permissionCount <= 0) { + Auth::guard('api')->logout(); + + return response()->json([ + 'code' => 403, + 'message' => '账号申请已通过,但尚未开通任何权限,请联系管理员分配权限后再登录', + 'data' => null, + ], 403); + } + $this->auditLog($request, 'login', ['metadata' => ['account_type' => $accountType]]); return response()->json([ @@ -84,6 +98,39 @@ class AuthController extends Controller ]); } + #[Apidoc\Title('账号申请'), Apidoc\Method('POST'), Apidoc\Url('/auth/apply-account')] + public function applyAccount(Request $request): JsonResponse + { + $validated = $request->validate([ + 'nickname' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', 'unique:users,email'], + 'phone' => ['required', 'string', 'max:32', 'unique:users,phone'], + 'password' => ['required', 'confirmed', Password::min(6)], + ]); + + $user = User::query()->create([ + 'nickname' => $validated['nickname'], + 'email' => $validated['email'], + 'phone' => $validated['phone'], + 'password' => $validated['password'], + 'force_password_change' => false, + ]); + + $guestRole = Role::query()->firstOrCreate( + ['name' => 'guest', 'guard_name' => 'api'], + ['name' => 'guest', 'guard_name' => 'api'] + ); + $user->syncRoles([$guestRole->id]); + + $this->auditLog($request, 'account_apply', ['metadata' => ['target_user_id' => $user->id]]); + + return response()->json([ + 'code' => 0, + 'message' => '申请提交成功,请联系或等待管理员开通权限后登录控制台', + 'data' => null, + ], 201); + } + #[Apidoc\Title('当前用户信息'), Apidoc\Method('GET'), Apidoc\Url('/auth/me')] public function me(): JsonResponse { diff --git a/app/Http/Controllers/Api/BastionAccountController.php b/app/Http/Controllers/Api/BastionAccountController.php index 0f9ebe6..c6e9243 100644 --- a/app/Http/Controllers/Api/BastionAccountController.php +++ b/app/Http/Controllers/Api/BastionAccountController.php @@ -26,9 +26,14 @@ class BastionAccountController extends Controller } #[Apidoc\Title('账号列表'), Apidoc\Method('GET'), Apidoc\Url('/accounts')] - public function index(): JsonResponse + public function index(Request $request): JsonResponse { - return response()->json(['code' => 0, 'message' => 'ok', 'data' => BastionAccount::query()->latest()->paginate(20)]); + $validated = $request->validate([ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]); + $perPage = (int) ($validated['per_page'] ?? 20); + + return response()->json(['code' => 0, 'message' => 'ok', 'data' => BastionAccount::query()->latest()->paginate($perPage)]); } #[Apidoc\Title('创建账号'), Apidoc\Method('POST'), Apidoc\Url('/accounts')] diff --git a/app/Http/Controllers/Api/PermissionController.php b/app/Http/Controllers/Api/PermissionController.php index 7fef10e..0ad7b45 100644 --- a/app/Http/Controllers/Api/PermissionController.php +++ b/app/Http/Controllers/Api/PermissionController.php @@ -22,9 +22,13 @@ class PermissionController extends Controller } #[Apidoc\Title('权限列表'), Apidoc\Method('GET'), Apidoc\Url('/permissions')] - public function index(): JsonResponse + public function index(Request $request): JsonResponse { - $permissions = Permission::query()->latest()->paginate(200); + $validated = $request->validate([ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:200'], + ]); + $perPage = (int) ($validated['per_page'] ?? 20); + $permissions = Permission::query()->latest()->paginate($perPage); $permissions->getCollection()->transform(function (Permission $permission) { return $this->applyDefaultMeta($permission); diff --git a/app/Http/Controllers/Api/RoleController.php b/app/Http/Controllers/Api/RoleController.php index 3455cbe..5494da6 100644 --- a/app/Http/Controllers/Api/RoleController.php +++ b/app/Http/Controllers/Api/RoleController.php @@ -21,9 +21,13 @@ class RoleController extends Controller } #[Apidoc\Title('角色列表'), Apidoc\Method('GET'), Apidoc\Url('/roles')] - public function index(): JsonResponse + public function index(Request $request): JsonResponse { - $roles = Role::query()->with('permissions')->latest()->paginate(20); + $validated = $request->validate([ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]); + $perPage = (int) ($validated['per_page'] ?? 20); + $roles = Role::query()->with('permissions')->latest()->paginate($perPage); return response()->json(['code' => 0, 'message' => 'ok', 'data' => $roles]); } diff --git a/app/Http/Controllers/Api/UserController.php b/app/Http/Controllers/Api/UserController.php index 233fd47..5b4133d 100644 --- a/app/Http/Controllers/Api/UserController.php +++ b/app/Http/Controllers/Api/UserController.php @@ -24,7 +24,7 @@ class UserController extends Controller { $this->middleware('auth:api'); $this->middleware('permission:platform.users.view,api')->only(['index', 'show']); - $this->middleware('permission:platform.users.manage,api')->only(['store', 'update', 'destroy', 'syncPermissions', 'import', 'importTemplate']); + $this->middleware('permission:platform.users.manage,api')->only(['store', 'update', 'destroy', 'syncPermissions', 'syncBatchAssignments', 'import', 'importTemplate']); } #[Apidoc\Title('用户列表'), Apidoc\Method('GET'), Apidoc\Url('/users')] @@ -71,8 +71,12 @@ class UserController extends Controller } #[Apidoc\Title('更新用户'), Apidoc\Method('PUT'), Apidoc\Url('/users/{id}')] - public function update(UpdateUserRequest $request, int $id): JsonResponse + public function update(UpdateUserRequest $request, string $id): JsonResponse { + if ($id === 'batch-assignments') { + return $this->syncBatchAssignments($request); + } + $user = User::query()->findOrFail($id); $user->fill($request->safe()->except(['role_ids'])); @@ -108,6 +112,46 @@ class UserController extends Controller return response()->json(['code' => 0, 'message' => 'ok', 'data' => $user->load(['roles', 'permissions'])]); } + #[Apidoc\Title('批量设置用户组和用户权限'), Apidoc\Method('PUT'), Apidoc\Url('/users/batch-assignments')] + public function syncBatchAssignments(Request $request): JsonResponse + { + $validated = $request->validate([ + 'user_ids' => ['required', 'array', 'min:1'], + 'user_ids.*' => ['integer', 'exists:users,id'], + 'role_ids' => ['present', 'array'], + 'role_ids.*' => ['integer', 'exists:roles,id'], + 'permission_ids' => ['present', 'array'], + 'permission_ids.*' => ['integer', 'exists:permissions,id'], + ]); + + $userIds = collect($validated['user_ids'])->map(fn (int $id): int => (int) $id)->unique()->values()->all(); + $roleIds = collect($validated['role_ids'])->map(fn (int $id): int => (int) $id)->unique()->values()->all(); + $permissionIds = collect($validated['permission_ids'])->map(fn (int $id): int => (int) $id)->unique()->values()->all(); + + $users = User::query()->whereIn('id', $userIds)->get(); + foreach ($users as $user) { + $user->syncRoles($roleIds); + $user->syncPermissions($permissionIds); + $this->syncServerResourcePermissionsByDirectPermissions($user, $permissionIds); + } + + $this->auditLog($request, 'users_batch_assignments_update', [ + 'metadata' => [ + 'target_user_ids' => $userIds, + 'role_ids' => $roleIds, + 'permission_ids' => $permissionIds, + ], + ]); + + return response()->json([ + 'code' => 0, + 'message' => 'ok', + 'data' => [ + 'updated_count' => $users->count(), + ], + ]); + } + #[Apidoc\Title('删除用户'), Apidoc\Method('DELETE'), Apidoc\Url('/users/{id}')] public function destroy(Request $request, int $id): JsonResponse {