Files
star-erp/.agents/skills/permission-management/SKILL.md

6.7 KiB
Raw Blame History

name, description
name description
權限管理與實作規範 為新功能實作權限控制的完整流程規範,包含後端 Seeder 設定、Middleware 路由保護與前端權限判斷。

權限管理與實作規範

本文件說明如何在新增功能時,一併實作完整的權限控制機制。專案採用 spatie/laravel-permission 套件進行權限管理。


1. 定義權限 (Backend Seeder)

所有權限皆定義於 database/seeders/PermissionSeeder.php

步驟:

  1. 開啟 database/seeders/PermissionSeeder.php
  2. $permissions 關聯陣列中新增功能對應的權限。
    • 命名慣例{resource}.{action}(例如:system.view_logs, products.create
    • 格式'權限字串' => '中文動作名稱'
    • 常用動作:view, create, edit, delete, approve, cancel, export
  3. 在下方「角色分配」區段,將新權限分配給適合的角色。

範例:

// 1. 新增權限(注意:是 key => value 格式)
$permissions = [
    // ... 現有權限
    'utility_fees.view' => '檢視',
    'utility_fees.create' => '建立',
    'utility_fees.edit' => '編輯',
    'utility_fees.delete' => '刪除',
];

// 2. 分配給角色
$admin->givePermissionTo([
    // ... 現有權限
    'utility_fees.view', 'utility_fees.create', 'utility_fees.edit', 'utility_fees.delete',
]);

現有角色定義:

角色 說明 權限範圍
super-admin 系統管理員 自動擁有所有權限(Permission::all()
admin 一般管理員 大部分權限(除角色管理外)
warehouse-manager 倉庫管理員 庫存、盤點、調撥、進貨、門市叫貨
purchaser 採購人員 商品檢視、採購單、退貨、供應商、進貨
viewer 檢視人員 僅限各模組的 .view 權限

2. 套用資料庫變更 (Multi-tenancy)

修改 Seeder 後,必須在中央與所有租戶同步執行。

# 對所有租戶執行 Seeder
./vendor/bin/sail php artisan tenants:seed --class=PermissionSeeder

Warning

僅執行 db:seed 只會更新中央資料庫。務必使用 tenants:seed 確保所有租戶同步。


3. 路由保護 (Backend Middleware)

路由保護定義在各模組自己的 app/Modules/{ModuleName}/Routes/web.php 中。

Important

路由檔在各模組內(如 app/Modules/Finance/Routes/web.php不是全域的 routes/web.php

範例:

// 單一權限保護
Route::middleware('permission:utility_fees.view')->group(function () {
    Route::get('/utility-fees', [UtilityFeeController::class, 'index'])->name('utility-fees.index');
    Route::get('/utility-fees/{utilityFee}', [UtilityFeeController::class, 'show'])->name('utility-fees.show');
});

// 巢狀權限群組
Route::middleware('permission:utility_fees.create')->group(function () {
    Route::get('/utility-fees/create', [UtilityFeeController::class, 'create'])->name('utility-fees.create');
    Route::post('/utility-fees', [UtilityFeeController::class, 'store'])->name('utility-fees.store');
});

// 單行 middleware
Route::delete('/utility-fees/{utilityFee}', [UtilityFeeController::class, 'destroy'])
    ->middleware('permission:utility_fees.delete')
    ->name('utility-fees.destroy');

4. 配置權限群組名稱 (Backend UI Config)

為了讓新權限在「角色與權限」管理介面中正確分組並顯示中文標題,需修改 Controller。

位置: app/Modules/Core/Controllers/RoleController.phpgetGroupedPermissions()

$groupDefinitions = [
    'products' => '商品資料管理',
    'warehouses' => '倉庫管理',
    'inventory' => '庫存資料管理',
    // ...
    'utility_fees' => '公共事業費管理', // ✅ 新增此行
];

Note

未加入 $groupDefinitions 的權限群組仍會顯示,但標題會以原始 key英文呈現。


5. 前端權限判斷 (React)

5.1 方式一:usePermission Hook在邏輯中判斷

位置: resources/js/hooks/usePermission.ts

import { usePermission } from "@/hooks/usePermission";

export default function ProductIndex() {
    const { can, canAny, isSuperAdmin } = usePermission();

    return (
        <div>
            {can('products.create') && <Button>新增商品</Button>}
            {canAny(['products.edit', 'products.delete']) && <ManageDropdown />}
        </div>
    );
}

Hook 完整介面:

方法 說明
can(permission) 檢查是否擁有指定權限
canAny(permissions[]) 檢查是否擁有任一權限
canAll(permissions[]) 檢查是否擁有所有權限
hasRole(role) 檢查是否擁有指定角色
hasAnyRole(roles[]) 檢查是否擁有任一角色
hasAllRoles(roles[]) 檢查是否擁有所有角色
isSuperAdmin() 是否為超級管理員

所有方法對 super-admin 角色自動回傳 true

5.2 方式二:<Can> / <HasRole> / <CanAll> 元件(在 JSX 中包裹)

位置: resources/js/Components/Permission/Can.tsx

import { Can, HasRole, CanAll } from '@/Components/Permission/Can';

// 單一權限
<Can permission="products.create">
    <Button>新增商品</Button>
</Can>

// 任一權限OR 邏輯)
<Can permission={['products.edit', 'products.delete']}>
    <ManageDropdown />
</Can>

// 所有權限都必須有AND 邏輯)
<CanAll permissions={['products.edit', 'products.delete']}>
    <Button>完整管理</Button>
</CanAll>

// 角色判斷
<HasRole role="admin">
    <Link href="/admin">管理後台</Link>
</HasRole>

// Fallback 支援
<Can permission="products.delete" fallback={<span className="text-gray-400">無權限</span>}>
    <Button variant="destructive">刪除</Button>
</Can>

Important

UI 規範要求:所有可操作按鈕(新增、編輯、刪除)必須包裹 <Can> 元件或使用 can() 判斷。 詳見 UI 統一規範


6. 開發檢核清單 (Checklist)

後端

  • PermissionSeeder.php 已新增權限字串('key' => '中文動作名稱' 格式)。
  • PermissionSeeder.php 已將新權限分配給 admin 及其他適用角色。
  • 已執行 ./vendor/bin/sail php artisan tenants:seed --class=PermissionSeeder 同步所有租戶。
  • RoleController.php$groupDefinitions 已新增權限群組中文名稱。
  • 模組路由 (app/Modules/{ModuleName}/Routes/web.php) 已加上 middleware('permission:...') 保護。

前端

  • 頁面按鈕已使用 usePermission Hook 或 <Can> 元件進行權限控制。
  • 所有可操作按鈕都包裹於權限判斷中(符合 UI 統一規範)。