Files
star-cloud/app/Http/Controllers/Admin/PermissionController.php
sky121113 6588dcd7f7
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 54s
[REFACTOR] 將帳號管理之 Email 欄位改為選填
2026-03-19 17:22:12 +08:00

425 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class PermissionController extends Controller
{
// 權限角色設定
public function roles()
{
$per_page = request()->input('per_page', 10);
$user = auth()->user();
$query = \App\Models\System\Role::query()->with(['permissions', 'users']);
// 租戶隔離:租戶只能看到自己公司的角色 + 系統角色 (company_id is null)
if (!$user->isSystemAdmin()) {
$query->where('company_id', $user->company_id);
}
// 搜尋:角色名稱
if ($search = request()->input('search')) {
$query->where('name', 'like', "%{$search}%");
}
$roles = $query->latest()->paginate($per_page)->withQueryString();
$all_permissions = \Spatie\Permission\Models\Permission::all()
->filter(function($perm) {
// 排除子項目的權限,只顯示主選單權限
$excluded = [
'menu.basic.machines',
'menu.basic.payment-configs',
'menu.companies',
'menu.accounts',
'menu.roles',
];
return !in_array($perm->name, $excluded);
})
->groupBy(function($perm) {
if (str_starts_with($perm->name, 'menu.')) {
return 'menu';
}
return 'other';
});
// 根據路由決定標題
$title = request()->routeIs('*.sub-account-roles') ? __('Sub Account Roles') : __('Role Settings');
return view('admin.permission.roles', compact('roles', 'all_permissions', 'title'));
}
/**
* Store a newly created role in storage.
*/
public function storeRole(Request $request)
{
$is_system = auth()->user()->isSystemAdmin() && $request->boolean('is_system');
$company_id = $is_system ? null : auth()->user()->company_id;
$validated = $request->validate([
'name' => [
'required', 'string', 'max:255',
\Illuminate\Validation\Rule::unique('roles', 'name')->where(function ($query) use ($company_id) {
return $query->where('company_id', $company_id);
})
],
'permissions' => 'nullable|array',
'permissions.*' => 'string|exists:permissions,name',
]);
$role = \App\Models\System\Role::query()->create([
'name' => $validated['name'],
'guard_name' => 'web',
'company_id' => $is_system ? null : auth()->user()->company_id,
'is_system' => $is_system,
]);
if (!empty($validated['permissions'])) {
$perms = $validated['permissions'];
// 如果不是系統角色,排除主選單的系統權限
if (!$is_system) {
$perms = array_diff($perms, ['menu.basic-settings', 'menu.permissions']);
}
$role->syncPermissions($perms);
}
return redirect()->back()->with('success', __('Role created successfully.'));
}
/**
* Update the specified role in storage.
*/
public function updateRole(Request $request, $id)
{
$role = \App\Models\System\Role::findOrFail($id);
$is_system = $role->is_system;
$company_id = $role->company_id;
$validated = $request->validate([
'name' => [
'required', 'string', 'max:255',
\Illuminate\Validation\Rule::unique('roles', 'name')
->ignore($id)
->where(function ($query) use ($company_id) {
return $query->where('company_id', $company_id);
})
],
'permissions' => 'nullable|array',
'permissions.*' => 'string|exists:permissions,name',
]);
if ($role->name === 'super-admin') {
return redirect()->back()->with('error', __('The Super Admin role is immutable.'));
}
if (!auth()->user()->isSystemAdmin() && $role->is_system) {
return redirect()->back()->with('error', __('System roles cannot be modified by tenant administrators.'));
}
$is_system = auth()->user()->isSystemAdmin() ? $request->boolean('is_system') : $role->is_system;
$role->update([
'name' => $validated['name'],
'is_system' => $is_system,
'company_id' => $is_system ? null : $role->company_id,
]);
$perms = $validated['permissions'] ?? [];
// 如果不是系統角色,排除主選單的系統權限
if (!$is_system) {
$perms = array_diff($perms, ['menu.basic-settings', 'menu.permissions']);
}
$role->syncPermissions($perms);
return redirect()->back()->with('success', __('Role updated successfully.'));
}
/**
* Remove the specified role from storage.
*/
public function destroyRole($id)
{
$role = \App\Models\System\Role::findOrFail($id);
if ($role->name === 'super-admin') {
return redirect()->back()->with('error', __('The Super Admin role cannot be deleted.'));
}
if (!auth()->user()->isSystemAdmin() && $role->is_system) {
return redirect()->back()->with('error', __('System roles cannot be deleted by tenant administrators.'));
}
if ($role->users()->count() > 0) {
return redirect()->back()->with('error', __('Cannot delete role with active users.'));
}
$role->delete();
return redirect()->back()->with('success', __('Role deleted successfully.'));
}
// 帳號管理
public function accounts(Request $request)
{
$query = \App\Models\System\User::query()->with(['company', 'roles']);
// 租戶隔離:如果不是系統管理員,則只看自己公司的成員
if (!auth()->user()->isSystemAdmin()) {
$query->where('company_id', auth()->user()->company_id);
}
// 搜尋
if ($search = $request->input('search')) {
$query->where(function($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('username', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
// 公司篩選 (僅限 super-admin)
if (auth()->user()->isSystemAdmin() && $request->filled('company_id')) {
$query->where('company_id', $request->company_id);
}
$per_page = $request->input('per_page', 10);
$users = $query->latest()->paginate($per_page)->withQueryString();
$companies = auth()->user()->isSystemAdmin() ? \App\Models\System\Company::all() : collect();
$roles_query = \App\Models\System\Role::where('name', '!=', 'super-admin');
if (!auth()->user()->isSystemAdmin()) {
$roles_query->where('company_id', auth()->user()->company_id);
}
$roles = $roles_query->get();
// 根據路由決定標題
$title = request()->routeIs('*.sub-accounts') ? __('Sub Account Management') : __('Account Management');
return view('admin.data-config.accounts', compact('users', 'companies', 'roles', 'title'));
}
/**
* Store a newly created account in storage.
*/
public function storeAccount(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'username' => 'required|string|max:255|unique:users,username',
'email' => 'nullable|email|max:255|unique:users,email',
'password' => 'required|string|min:8',
'role' => 'required|string',
'status' => 'required|boolean',
'company_id' => 'nullable|exists:companies,id',
'phone' => 'nullable|string|max:20',
]);
$company_id = auth()->user()->isSystemAdmin() ? ($validated['company_id'] ?? null) : auth()->user()->company_id;
// 查找角色:優先尋找該公司的角色,若無則尋找全域範本
$role = \App\Models\System\Role::where('name', $validated['role'])
->where(function($q) use ($company_id) {
$q->where('company_id', $company_id)->orWhereNull('company_id');
})
->first();
if (!$role) {
return redirect()->back()->with('error', __('Role not found.'));
}
// 驗證角色與公司的匹配性 (RBAC Safeguard)
if ($company_id !== null) {
// 如果是租戶帳號,不能選各項系統角色 (is_system = 1)
if ($role->is_system) {
return redirect()->back()->with('error', __('System roles cannot be assigned to tenant accounts.'));
}
// 如果角色有特定的 company_id必須匹配
if ($role->company_id !== null && $role->company_id != $company_id) {
return redirect()->back()->with('error', __('This role belongs to another company and cannot be assigned.'));
}
} else {
// 如果是系統層級帳號,只能選系統角色 (is_system = 1)
if (!$role->is_system) {
return redirect()->back()->with('error', __('Only system roles can be assigned to platform administrative accounts.'));
}
}
// 角色初始化與克隆邏輯 (只有 super-admin 在幫空白公司開帳號時觸發)
$role_to_assign = $validated['role'];
$company_id = auth()->user()->isSystemAdmin() ? ($validated['company_id'] ?? null) : auth()->user()->company_id;
if ($company_id && $role && !$role->is_system && $role->company_id === null) {
// 檢查該公司是否已有名為「管理員」的角色
$existingRole = \App\Models\System\Role::where('company_id', $company_id)
->where('name', '管理員')
->first();
if (!$existingRole) {
// 克隆範本為該公司的「管理員」
$clonedRole = \App\Models\System\Role::query()->create([
'name' => '管理員',
'guard_name' => 'web',
'company_id' => $company_id,
'is_system' => false,
]);
$clonedRole->syncPermissions($role->permissions);
$role_to_assign = '管理員';
} else {
// 如果已存在名為「管理員」的角色,則直接使用它
$role_to_assign = '管理員';
}
}
$user = \App\Models\System\User::create([
'name' => $validated['name'],
'username' => $validated['username'],
'email' => $validated['email'],
'password' => \Illuminate\Support\Facades\Hash::make($validated['password']),
'status' => $validated['status'],
'company_id' => $company_id,
'phone' => $validated['phone'],
]);
$user->assignRole($role_to_assign);
return redirect()->back()->with('success', __('Account created successfully.'));
}
/**
* Update the specified account in storage.
*/
public function updateAccount(Request $request, $id)
{
$user = \App\Models\System\User::findOrFail($id);
if ($user->hasRole('super-admin')) {
return redirect()->back()->with('error', __('System super admin accounts cannot be modified via this interface.'));
}
$validated = $request->validate([
'name' => 'required|string|max:255',
'username' => 'required|string|max:255|unique:users,username,' . $id,
'email' => 'nullable|email|max:255|unique:users,email,' . $id,
'password' => 'nullable|string|min:8',
'role' => 'required|string',
'status' => 'required|boolean',
'company_id' => 'nullable|exists:companies,id',
'phone' => 'nullable|string|max:20',
]);
$target_company_id = auth()->user()->isSystemAdmin() ? ($validated['company_id'] ?? null) : auth()->user()->company_id;
// 查找角色:優先尋找該公司的角色,若無則尋找全域範本
$roleObj = \App\Models\System\Role::where('name', $validated['role'])
->where(function($q) use ($target_company_id) {
$q->where('company_id', $target_company_id)->orWhereNull('company_id');
})
->first();
if (!$roleObj) {
return redirect()->back()->with('error', __('Role not found.'));
}
// 驗證角色與公司的匹配性 (RBAC Safeguard)
if ($user->id !== auth()->id()) { // 排除編輯自己 (super-admin 有特殊邏輯)
if ($target_company_id !== null) {
if ($roleObj->is_system) {
return redirect()->back()->with('error', __('System roles cannot be assigned to tenant accounts.'));
}
if ($roleObj->company_id !== null && $roleObj->company_id != $target_company_id) {
return redirect()->back()->with('error', __('This role belongs to another company and cannot be assigned.'));
}
} else {
if (!$roleObj->is_system) {
return redirect()->back()->with('error', __('Only system roles can be assigned to platform administrative accounts.'));
}
}
}
$updateData = [
'name' => $validated['name'],
'username' => $validated['username'],
'email' => $validated['email'],
'status' => $validated['status'],
'phone' => $validated['phone'],
];
if (auth()->user()->isSystemAdmin()) {
// 防止超級管理員不小心把自己綁定到租客公司或降級
if ($user->id === auth()->id()) {
$updateData['company_id'] = null;
$validated['role'] = 'super-admin';
} else {
$updateData['company_id'] = $validated['company_id'];
}
}
if (!empty($validated['password'])) {
$updateData['password'] = \Illuminate\Support\Facades\Hash::make($validated['password']);
}
// 角色初始化與克隆邏輯
$role_to_assign = $validated['role'];
$target_company_id = auth()->user()->isSystemAdmin() ? ($validated['company_id'] ?? null) : auth()->user()->company_id;
if ($target_company_id && $roleObj && !$roleObj->is_system && $roleObj->company_id === null) {
// 檢查該公司是否已有名為「管理員」的角色
$existingRole = \App\Models\System\Role::where('company_id', $target_company_id)
->where('name', '管理員')
->first();
if (!$existingRole) {
$clonedRole = \App\Models\System\Role::query()->create([
'name' => '管理員',
'guard_name' => 'web',
'company_id' => $target_company_id,
'is_system' => false,
]);
$clonedRole->syncPermissions($roleObj->permissions);
$role_to_assign = '管理員';
} else {
$role_to_assign = '管理員';
}
}
$user->update($updateData);
// 如果是編輯自己且原本是超級管理員,強制保留 super-admin 角色
if ($user->id === auth()->id() && auth()->user()->isSystemAdmin()) {
$user->syncRoles(['super-admin']);
} else {
$user->syncRoles([$role_to_assign]);
}
return redirect()->back()->with('success', __('Account updated successfully.'));
}
/**
* Remove the specified account from storage.
*/
public function destroyAccount($id)
{
$user = \App\Models\System\User::findOrFail($id);
if ($user->hasRole('super-admin')) {
return redirect()->back()->with('error', __('System super admin accounts cannot be deleted.'));
}
if ($user->id === auth()->id()) {
return redirect()->back()->with('error', __('You cannot delete your own account.'));
}
// 為了解決軟刪除導致的唯一索引佔用問題,刪除前先重命名唯一欄位
$timestamp = now()->getTimestamp();
$user->username = $user->username . '.deleted.' . $timestamp;
$user->email = $user->email . '.deleted.' . $timestamp;
$user->save();
$user->delete();
return redirect()->back()->with('success', __('Account deleted successfully.'));
}
}