NextUI.Identity.Admin 真实管理功能实施方案

一、问题分析

当前状态

层次 状态 说明
Keycloak Admin API Client ✅ 完整 KeycloakAdminClient 已实现所有 REST API 调用
Admin Provider 抽象 ✅ 完整 IAdminProvider, KeycloakAdminProvider 已实现
服务器注册 ✅ 部分 ServerRegistry, AdminContextService 已实现
UI 组件 ⚠️ 只有皮 组件存在但需要外部传入 handlers,默认显示 mock
页面集成 ❌ 缺失 没有开箱即用的管理页面

核心问题

目前的 SxAdminUserList 组件:
┌─────────────────────────────────────────────────────┐
│  SxAdminUserList                                    │
│  ├── LoadUsersHandler = null  → 显示 mock 数据      │
│  ├── OnUserCreated = null     → 什么都不做          │
│  └── OnUserDeleted = null     → 什么都不做          │
└─────────────────────────────────────────────────────┘

期望的效果:
┌─────────────────────────────────────────────────────┐
│  SxAdminUserList (自动连接真实服务)                   │
│  ├── 自动从 AdminContext 获取当前服务器               │
│  ├── 自动调用 KeycloakAdminClient 获取用户列表        │
│  ├── 创建用户 → 真正调用 Keycloak API                │
│  └── 删除用户 → 真正调用 Keycloak API                │
└─────────────────────────────────────────────────────┘

二、解决方案

方案概述

创建 AdminPage 系列组件,它们:

  1. 自动注入 AdminContextService 获取当前管理上下文
  2. 自动使用 AdminProviderFactory 获取对应的 provider
  3. 内部调用现有的 UI 组件并自动传入真实的 handlers

架构设计

┌─────────────────────────────────────────────────────────────────────┐
│                        应用使用层                                     │
│                                                                     │
│  使用方式 1: 直接使用预组装页面                                        │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ <SxAdminUserManagementPage />                               │   │
│  │  - 自动获取当前服务器上下文                                    │   │
│  │  - 自动调用真实 API                                          │   │
│  │  - 包含完整 CRUD 功能                                        │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  使用方式 2: 使用智能组件                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ <SxAdminUserListConnected />                                │   │
│  │  - 自动连接到真实服务                                         │   │
│  │  - 可以自定义部分行为                                         │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  使用方式 3: 使用原始 UI 组件 + 手动 wiring (现有方式)                  │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ <SxAdminUserList LoadUsersHandler="@MyHandler" ... />       │   │
│  │  - 完全手动控制                                              │   │
│  │  - 最大灵活性                                                │   │
│  └─────────────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────┤
│                      服务/Provider 层                               │
│                                                                     │
│  ┌──────────────────────┐  ┌──────────────────────┐                │
│  │ AdminContextService  │  │ AdminProviderFactory │                │
│  │ - 当前选中的服务器     │  │ - 根据类型创建 provider│               │
│  │ - 当前 Realm/Client  │  │ - Keycloak/Auth0/...│                │
│  └──────────────────────┘  └──────────────────────┘                │
│                                                                     │
│  ┌──────────────────────┐  ┌──────────────────────┐                │
│  │ KeycloakAdminProvider│  │SharedProfileAdminProv│                │
│  │ - 用户/角色/组管理    │  │ - SharedProfile CRUD │                │
│  └──────────────────────┘  └──────────────────────┘                │
├─────────────────────────────────────────────────────────────────────┤
│                      API Client 层                                  │
│                                                                     │
│  ┌──────────────────────┐  ┌──────────────────────┐                │
│  │ KeycloakAdminClient  │  │ SharedProfileClient  │                │
│  │ - 真实 HTTP 调用      │  │ - 真实 HTTP 调用      │                │
│  │ - 完整 Admin REST API│  │ - 标准 Admin API     │                │
│  └──────────────────────┘  └──────────────────────┘                │
└─────────────────────────────────────────────────────────────────────┘

三、实施步骤

Phase 1: 完善 Admin Provider 层 (服务层)

目标: 确保 Provider 层能正确调用真实 API

1.1 完善 KeycloakAdminProvider

文件: src/NextUI.Identity/Admin/Providers/KeycloakAdminProvider.cs

public class KeycloakAdminProvider : IAdminProvider
{
    private readonly KeycloakAdminClient _client;
    private readonly string _realm;

    // 实现所有管理操作,调用 _client 的真实 API
    public async Task<PagedResult<AdminUser>> GetUsersAsync(UserQuery query)
    {
        var users = await _client.GetUsersAsync(_realm, query);
        var count = await _client.GetUsersCountAsync(_realm, query.Search);
        return new PagedResult<AdminUser>
        {
            Items = users.Select(MapToAdminUser).ToList(),
            TotalCount = count
        };
    }

    public async Task CreateUserAsync(AdminUser user)
    {
        var kcUser = MapToKeycloakUser(user);
        await _client.CreateUserAsync(_realm, kcUser);
    }

    // ... 其他操作
}

1.2 完善 SharedProfileAdminProvider

文件: src/NextUI.Identity/Admin/Providers/SharedProfileAdminProvider.cs

public class SharedProfileAdminProvider : ISharedProfileAdminService
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;

    public async Task<PagedResult<SharedProfile>> GetProfilesAsync(int skip, int take)
    {
        var response = await _httpClient.GetAsync($"{_baseUrl}?skip={skip}&take={take}");
        return await response.Content.ReadFromJsonAsync<PagedResult<SharedProfile>>();
    }

    public async Task UpdateProfileAsync(SharedProfile profile)
    {
        await _httpClient.PutAsJsonAsync($"{_baseUrl}/{profile.UserId}", profile);
    }
}

1.3 修复 AdminProviderFactory

确保能正确创建和配置 providers。


Phase 2: 创建 Connected 组件

目标: 创建自动连接真实服务的"智能"组件

2.1 SxAdminUserListConnected

文件: src/NextUI.Blazor/Components/Admin/SxAdminUserListConnected.razor

@inject AdminContextService AdminContext
@inject AdminProviderFactory ProviderFactory

@if (!_initialized)
{
    <SxProgressRing />
}
else if (_provider == null)
{
    <SxMessageBar Intent="MessageIntent.Warning">
        Please select a server to manage.
    </SxMessageBar>
}
else
{
    <SxAdminUserList
        LoadUsersHandler="LoadUsersAsync"
        OnUserCreated="CreateUserAsync"
        OnUserEdited="UpdateUserAsync"
        OnUserDeleted="DeleteUserAsync"
        OnUserStatusChanged="ToggleUserStatusAsync"
        OnPasswordReset="ResetPasswordAsync"
        LoadRolesHandler="LoadRolesAsync"
        OnRolesChanged="UpdateRolesAsync" />
}

@code {
    private IAdminProvider? _provider;
    private bool _initialized;

    protected override async Task OnInitializedAsync()
    {
        AdminContext.ContextChanged += OnContextChanged;
        await InitializeProvider();
    }

    private async Task InitializeProvider()
    {
        var server = AdminContext.CurrentServer;
        if (server != null)
        {
            _provider = await ProviderFactory.CreateProviderAsync(server);
        }
        _initialized = true;
    }

    private async Task<(List<AdminUser>, int)> LoadUsersAsync(UserSearchRequest request)
    {
        var result = await _provider!.GetUsersAsync(new UserQuery
        {
            Search = request.Search,
            First = request.First,
            Max = request.Max
        });
        return (result.Items, result.TotalCount);
    }

    private async Task CreateUserAsync(AdminUser user)
    {
        await _provider!.CreateUserAsync(user);
    }

    // ... 其他方法
}

2.2 类似创建其他 Connected 组件

  • SxAdminRoleListConnected
  • SxAdminClientListConnected
  • SxSharedProfileListConnected

Phase 3: 创建预组装管理页面

目标: 提供开箱即用的完整管理页面

3.1 SxAdminUserManagementPage

文件: src/NextUI.Blazor/Components/Admin/Pages/SxAdminUserManagementPage.razor

@namespace NextUI.Blazor.Components.Admin.Pages

<SxStack Orientation="StackOrientation.Vertical" Gap="ControlSize.Large">
    @* 页面标题 *@
    <SxStack Orientation="StackOrientation.Horizontal"
             VerticalAlignment="VerticalAlignment.Center"
             HorizontalAlignment="HorizontalAlignment.SpaceBetween">
        <SxTypography Variant="TypographyVariant.Headline" Level="HeadlineLevel.H1">
            User Management
        </SxTypography>
        <SxAdminServerSelector />
    </SxStack>

    @* 用户列表 (自动连接) *@
    <SxAdminUserListConnected />
</SxStack>

3.2 SxAdminRoleManagementPage

<SxStack Orientation="StackOrientation.Vertical" Gap="ControlSize.Large">
    <SxTypography Variant="TypographyVariant.Headline" Level="HeadlineLevel.H1">
        Role Management
    </SxTypography>
    <SxAdminServerSelector />

    <SxTabs>
        <SxTabList>
            <SxTab>Realm Roles</SxTab>
            <SxTab>Client Roles</SxTab>
        </SxTabList>
        <SxTabPanel>
            <SxAdminRoleListConnected RoleScope="RoleScope.Realm" />
        </SxTabPanel>
        <SxTabPanel>
            <SxAdminClientSelector />
            <SxAdminRoleListConnected RoleScope="RoleScope.Client" />
        </SxTabPanel>
    </SxTabs>
</SxStack>

3.3 SxAdminDashboardPage

首页仪表板,显示:

  • 服务器连接状态
  • 用户统计
  • 快捷操作

Phase 4: 服务器管理功能

目标: 实现真正的服务器配置和管理

4.1 服务器配置持久化

文件: src/NextUI.Identity/Admin/Services/ServerConfigStorage.cs

public interface IServerConfigStorage
{
    Task<List<AdminServer>> LoadServersAsync();
    Task SaveServersAsync(List<AdminServer> servers);
    Task AddServerAsync(AdminServer server);
    Task UpdateServerAsync(AdminServer server);
    Task RemoveServerAsync(string serverId);
}

// 浏览器实现 (localStorage)
public class BrowserServerConfigStorage : IServerConfigStorage
{
    private readonly IJSRuntime _js;

    public async Task<List<AdminServer>> LoadServersAsync()
    {
        var json = await _js.InvokeAsync<string?>("localStorage.getItem", "nextui_admin_servers");
        return json == null ? [] : JsonSerializer.Deserialize<List<AdminServer>>(json)!;
    }

    public async Task SaveServersAsync(List<AdminServer> servers)
    {
        var json = JsonSerializer.Serialize(servers);
        await _js.InvokeVoidAsync("localStorage.setItem", "nextui_admin_servers", json);
    }
}

4.2 服务器连接测试

public class ServerConnectionTester
{
    public async Task<ConnectionTestResult> TestConnectionAsync(AdminServer server)
    {
        try
        {
            var provider = await _factory.CreateProviderAsync(server);

            // 尝试获取 realm 信息
            if (server.Type == AdminServerType.Keycloak)
            {
                var realms = await provider.GetRealmsAsync();
                return new ConnectionTestResult
                {
                    Success = true,
                    Message = $"Connected. Found {realms.Count} realms."
                };
            }

            return new ConnectionTestResult { Success = true };
        }
        catch (Exception ex)
        {
            return new ConnectionTestResult
            {
                Success = false,
                Message = ex.Message
            };
        }
    }
}

Phase 5: 完整管理门户模板

目标: 提供独立部署的管理门户项目模板

5.1 项目模板

templates/NextUI.Templates.AdminPortal/
├── Program.cs
├── appsettings.json
├── Pages/
│   ├── Index.razor           → 仪表板
│   ├── Users.razor           → 用户管理
│   ├── Roles.razor           → 角色管理
│   ├── Clients.razor         → 客户端管理
│   ├── SharedProfiles.razor  → 共享配置管理
│   └── Servers.razor         → 服务器管理
└── Shared/
    └── AdminLayout.razor     → 管理后台布局

5.2 CLI 创建命令

nx new my-admin-portal --template admin-portal

# 生成配置文件和初始代码
# 配置 Keycloak 连接
# 启动管理门户

四、实施顺序和优先级

阶段 优先级 预计工作量 内容
Phase 1 🔴 高 4h 完善 KeycloakAdminProvider 和 SharedProfileAdminProvider
Phase 2 🔴 高 4h 创建 Connected 组件系列
Phase 3 🟡 中 3h 创建预组装管理页面
Phase 4 🟡 中 3h 服务器管理功能
Phase 5 🟢 低 4h 管理门户模板

五、验证清单

Phase 1 验证

  • 启动 Keycloak (./nx identity demo start)
  • 调用 KeycloakAdminProvider.GetUsersAsync() 返回真实用户
  • 创建用户后在 Keycloak Admin Console 可见
  • 删除用户后在 Keycloak 中确实消失

Phase 2 验证

  • 放置 <SxAdminUserListConnected /> 显示真实用户列表
  • 点击 "Add User" 创建真实用户
  • 点击删除按钮真正删除用户
  • 重置密码功能正常工作

Phase 3 验证

  • 放置 <SxAdminUserManagementPage /> 显示完整管理页面
  • 切换服务器后数据自动刷新
  • 所有 CRUD 操作正常

Phase 4 验证

  • 添加新服务器并保存
  • 测试连接功能工作
  • 刷新页面后服务器配置仍然存在

六、技术决策

决策点 方案 理由
组件设计 Connected + Raw 双层 灵活性 + 开箱即用
状态管理 AdminContextService 单例 跨组件共享当前服务器上下文
服务器配置存储 localStorage 简单、不需要后端
认证 使用当前用户 Token 统一认证流程
错误处理 Toast 通知 用户友好

七、当前已有的基础设施

已实现 ✅

  • KeycloakAdminClient - 完整的 Keycloak Admin REST API 客户端
  • IAdminProvider 接口定义
  • KeycloakAdminProvider 部分实现
  • SharedProfileAdminProvider 部分实现
  • ServerRegistry 服务器注册
  • AdminContextService 管理上下文
  • UI 组件 (SxAdminUserList, SxAdminRoleList 等)

需要完善 ⚠️

  • Provider 的完整 CRUD 实现
  • Connected 组件
  • 预组装页面

需要新增 ❌

  • SxAdminUserListConnected 等 Connected 组件
  • SxAdminUserManagementPage 等预组装页面
  • 服务器配置持久化
  • 连接测试功能