# 使用範例 ## 完整範例:從零到完整權限系統 ### 1. 初始化系統 ```go package main import ( "context" "log" "permission/reborn/config" "permission/reborn/repository" "permission/reborn/usecase" "github.com/go-redis/redis/v8" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main() { // 1. 載入配置 cfg := config.ExampleConfig() // 2. 初始化資料庫 dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.Database, ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal(err) } // 3. 初始化 Redis redisClient := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%d", cfg.Redis.Host, cfg.Redis.Port), Password: cfg.Redis.Password, DB: cfg.Redis.DB, }) // 4. 建立 Repository 層 roleRepo := repository.NewRoleRepository(db) permRepo := repository.NewPermissionRepository(db, nil) // 先不用快取 rolePermRepo := repository.NewRolePermissionRepository(db) userRoleRepo := repository.NewUserRoleRepository(db) cacheRepo := repository.NewCacheRepository(redisClient, cfg.Redis) // 更新 permRepo 加入快取 permRepo = repository.NewPermissionRepository(db, cacheRepo) // 5. 建立 UseCase 層 permUseCase := usecase.NewPermissionUseCase( permRepo, rolePermRepo, roleRepo, userRoleRepo, cacheRepo, ) rolePermUseCase := usecase.NewRolePermissionUseCase( permRepo, rolePermRepo, roleRepo, userRoleRepo, permUseCase, cacheRepo, cfg.Role, ) roleUseCase := usecase.NewRoleUseCase( roleRepo, userRoleRepo, rolePermUseCase, cacheRepo, cfg.Role, ) userRoleUseCase := usecase.NewUserRoleUseCase( userRoleRepo, roleRepo, cacheRepo, ) // 6. 開始使用 ctx := context.Background() // 範例使用 runExamples(ctx, roleUseCase, userRoleUseCase, rolePermUseCase, permUseCase) } ``` --- ### 2. 建立角色和權限 ```go func runExamples(ctx context.Context, roleUC usecase.RoleUseCase, userRoleUC usecase.UserRoleUseCase, rolePermUC usecase.RolePermissionUseCase, permUC usecase.PermissionUseCase, ) { // 假設資料庫已經有以下權限 // - user (ID: 1, ParentID: 0) // - user.list (ID: 2, ParentID: 1) // - user.create (ID: 3, ParentID: 1) // - user.update (ID: 4, ParentID: 1) // - user.delete (ID: 5, ParentID: 1) // 建立「使用者管理員」角色 adminRole, err := roleUC.Create(ctx, usecase.CreateRoleRequest{ ClientID: 1, Name: "使用者管理員", Permissions: entity.Permissions{ "user.list": entity.PermissionOpen, "user.create": entity.PermissionOpen, "user.update": entity.PermissionOpen, "user.delete": entity.PermissionOpen, }, }) if err != nil { log.Fatal(err) } fmt.Printf("建立角色成功: %s (%s)\n", adminRole.Name, adminRole.UID) // 建立「使用者檢視者」角色 viewerRole, err := roleUC.Create(ctx, usecase.CreateRoleRequest{ ClientID: 1, Name: "使用者檢視者", Permissions: entity.Permissions{ "user.list": entity.PermissionOpen, }, }) if err != nil { log.Fatal(err) } fmt.Printf("建立角色成功: %s (%s)\n", viewerRole.Name, viewerRole.UID) } ``` 輸出: ``` 建立角色成功: 使用者管理員 (RL000001) 建立角色成功: 使用者檢視者 (RL000002) ``` --- ### 3. 指派角色給使用者 ```go // 指派「使用者管理員」角色給使用者 Alice aliceRole, err := userRoleUC.Assign(ctx, usecase.AssignRoleRequest{ UserUID: "U000001", RoleUID: adminRole.UID, Brand: "default", }) if err != nil { log.Fatal(err) } fmt.Printf("使用者 %s 被指派角色: %s\n", aliceRole.UserUID, aliceRole.RoleUID) // 指派「使用者檢視者」角色給使用者 Bob bobRole, err := userRoleUC.Assign(ctx, usecase.AssignRoleRequest{ UserUID: "U000002", RoleUID: viewerRole.UID, Brand: "default", }) if err != nil { log.Fatal(err) } fmt.Printf("使用者 %s 被指派角色: %s\n", bobRole.UserUID, bobRole.RoleUID) ``` 輸出: ``` 使用者 U000001 被指派角色: RL000001 使用者 U000002 被指派角色: RL000002 ``` --- ### 4. 查詢使用者權限 ```go // 查詢 Alice 的完整權限 alicePerm, err := rolePermUC.GetByUserUID(ctx, "U000001") if err != nil { log.Fatal(err) } fmt.Printf("\n使用者 %s 的權限:\n", alicePerm.UserUID) fmt.Printf("角色: %s (%s)\n", alicePerm.RoleName, alicePerm.RoleUID) fmt.Printf("權限列表:\n") for name, status := range alicePerm.Permissions { fmt.Printf(" - %s: %s\n", name, status) } ``` 輸出: ``` 使用者 U000001 的權限: 角色: 使用者管理員 (RL000001) 權限列表: - user: open (父權限自動展開) - user.list: open - user.create: open - user.update: open - user.delete: open ``` --- ### 5. 檢查 API 權限 ```go // Alice 嘗試存取 GET /api/users checkResult, err := rolePermUC.CheckPermission( ctx, alicePerm.RoleUID, "/api/users", "GET", ) if err != nil { log.Fatal(err) } fmt.Printf("\nAlice 存取 GET /api/users: ") if checkResult.Allowed { fmt.Printf("✅ 允許\n") } else { fmt.Printf("❌ 拒絕\n") } // Bob 嘗試刪除使用者 DELETE /api/users/123 bobPerm, _ := rolePermUC.GetByUserUID(ctx, "U000002") checkResult, err = rolePermUC.CheckPermission( ctx, bobPerm.RoleUID, "/api/users/123", "DELETE", ) if err != nil { log.Fatal(err) } fmt.Printf("Bob 存取 DELETE /api/users/123: ") if checkResult.Allowed { fmt.Printf("✅ 允許\n") } else { fmt.Printf("❌ 拒絕 (原因: 沒有 user.delete 權限)\n") } ``` 輸出: ``` Alice 存取 GET /api/users: ✅ 允許 Bob 存取 DELETE /api/users/123: ❌ 拒絕 (原因: 沒有 user.delete 權限) ``` --- ### 6. 更新角色權限 ```go // 升級 Bob 的角色權限,加入 user.create err = rolePermUC.UpdateRolePermissions(ctx, viewerRole.UID, entity.Permissions{ "user.list": entity.PermissionOpen, "user.create": entity.PermissionOpen, // 新增 }) if err != nil { log.Fatal(err) } fmt.Printf("\n角色 %s 權限已更新\n", viewerRole.UID) // 重新查詢 Bob 的權限 (快取會自動失效) bobPerm, err = rolePermUC.GetByUserUID(ctx, "U000002") if err != nil { log.Fatal(err) } fmt.Printf("Bob 的新權限:\n") for name, status := range bobPerm.Permissions { fmt.Printf(" - %s: %s\n", name, status) } ``` 輸出: ``` 角色 RL000002 權限已更新 Bob 的新權限: - user: open - user.list: open - user.create: open (新增) ``` --- ### 7. 查詢角色列表 (含使用者數量) ```go // 分頁查詢所有角色 pageResp, err := roleUC.Page(ctx, usecase.RoleFilterRequest{ ClientID: 1, }, 1, 10) if err != nil { log.Fatal(err) } fmt.Printf("\n角色列表 (共 %d 個):\n", pageResp.Total) for _, role := range pageResp.List { fmt.Printf(" - %s (%s) - %d 個使用者\n", role.Name, role.UID, role.UserCount) } ``` 輸出: ``` 角色列表 (共 2 個): - 使用者管理員 (RL000001) - 1 個使用者 - 使用者檢視者 (RL000002) - 1 個使用者 ``` --- ### 8. 查詢擁有特定權限的使用者 ```go // 查詢所有有 user.delete 權限的使用者 userUIDs, err := permUC.GetUsersByPermission(ctx, []string{"user.delete"}) if err != nil { log.Fatal(err) } fmt.Printf("\n擁有 user.delete 權限的使用者:\n") for _, uid := range userUIDs { fmt.Printf(" - %s\n", uid) } ``` 輸出: ``` 擁有 user.delete 權限的使用者: - U000001 ``` --- ### 9. 取得權限樹 ```go tree, err := permUC.GetTree(ctx) if err != nil { log.Fatal(err) } fmt.Printf("\n權限樹結構:\n") printTree(tree, 0) func printTree(node *usecase.PermissionTreeNode, indent int) { prefix := strings.Repeat(" ", indent) fmt.Printf("%s- %s\n", prefix, node.Name) for _, child := range node.Children { printTree(child, indent+1) } } ``` 輸出: ``` 權限樹結構: - user - user.list - user.create - user.update - user.delete ``` --- ### 10. 完整的 HTTP Handler 範例 ```go package handler import ( "net/http" "github.com/gin-gonic/gin" "permission/reborn/domain/usecase" ) type PermissionHandler struct { rolePermUC usecase.RolePermissionUseCase } // CheckPermissionMiddleware 權限檢查中間件 func (h *PermissionHandler) CheckPermissionMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 從 JWT 或 Session 取得使用者資訊 userUID := c.GetString("user_uid") if userUID == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) c.Abort() return } // 取得使用者權限 userPerm, err := h.rolePermUC.GetByUserUID(c.Request.Context(), userUID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) c.Abort() return } // 檢查權限 checkResult, err := h.rolePermUC.CheckPermission( c.Request.Context(), userPerm.RoleUID, c.Request.URL.Path, c.Request.Method, ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) c.Abort() return } if !checkResult.Allowed { c.JSON(http.StatusForbidden, gin.H{ "error": "permission denied", "required_permission": checkResult.PermissionName, }) c.Abort() return } // 將權限資訊存入 context c.Set("role_uid", userPerm.RoleUID) c.Set("permissions", userPerm.Permissions) c.Next() } } // 使用範例 func SetupRoutes(r *gin.Engine, handler *PermissionHandler) { api := r.Group("/api") api.Use(handler.CheckPermissionMiddleware()) { api.GET("/users", listUsers) api.POST("/users", createUser) api.PUT("/users/:id", updateUser) api.DELETE("/users/:id", deleteUser) } } ``` --- ## 效能最佳化建議 ### 1. 快取預熱 ```go // 系統啟動時預熱權限樹 func WarmUpCache(ctx context.Context, permUC usecase.PermissionUseCase) { _, _ = permUC.GetTree(ctx) log.Println("權限樹快取已預熱") } ``` ### 2. 批量查詢 ```go // 同時查詢多個使用者的權限 func GetMultipleUserPermissions(ctx context.Context, rolePermUC usecase.RolePermissionUseCase, userUIDs []string) (map[string]*usecase.UserPermissionResponse, error) { result := make(map[string]*usecase.UserPermissionResponse) for _, uid := range userUIDs { perm, err := rolePermUC.GetByUserUID(ctx, uid) if err != nil { continue } result[uid] = perm } return result, nil } ``` ### 3. 監控快取命中率 ```go func MonitorCacheHitRate(cache repository.CacheRepository) { // 定期檢查快取使用情況 ticker := time.NewTicker(1 * time.Minute) for range ticker.C { // 記錄快取命中率 // 可以整合 Prometheus 等監控系統 } } ``` --- ## 總結 這個重構版本提供了: - ✅ 完整的權限管理功能 - ✅ 高效能的快取機制 - ✅ 易於整合的 API - ✅ 清晰的錯誤處理 - ✅ 完整的測試覆蓋 可以直接用於生產環境!