Skip to content

插件概述

Fastdotnet 的插件系统是其核心特性之一,允许开发者在不修改主程序的情况下扩展功能。


🎯 什么是插件?

插件是一个独立的功能模块,可以:

  • 动态加载/卸载 - 无需重启应用
  • 独立开发 - 与主程序解耦
  • 按需启用 - 根据业务需求选择
  • 版本管理 - 支持多版本共存
  • 热更新 - 运行时更新插件

🏗️ 插件架构

整体架构

┌─────────────────────────────────────┐
│      Fastdotnet 主程序               │
│  (WebApi + Core + Service)          │
└──────────┬──────────────────────────┘
           │ 插件接口
    ┌──────┴──────┬──────────┐
    ↓             ↓          ↓
┌────────┐  ┌────────┐  ┌────────┐
│Plugin A│  │Plugin B│  │Plugin C│
└────────┘  └────────┘  └────────┘

插件项目结构

MyPlugin/
├── Backend/                    # 后端项目 (.NET 10)
│   ├── MyPlugin.csproj
│   ├── MyPluginPlugin.cs      # 插件入口类
│   ├── plugin.json            # 插件元数据
│   ├── Controllers/           # API 控制器
│   ├── Services/              # 业务服务
│   ├── Dto/                   # 数据传输对象
│   ├── Models/                # 数据模型
│   └── Initializers/          # 初始化器(菜单、配置等)

├── Frontend/                   # 前端项目 (Vue 3)
│   ├── MyPlugin.Admin/        # 管理端
│   │   ├── src/
│   │   ├── vite.config.ts
│   │   └── micro-index.html   # 微前端入口
│   │
│   └── MyPlugin.App/          # 应用端
│       ├── src/
│       ├── vite.config.ts
│       └── micro-index.html

└── publish/                    # 发布目录(自动生成)
    └── {PluginId}/
        ├── dependencies/      # 后端 DLL 文件
        ├── wwwroot/
        │   ├── admin/         # 管理端静态资源
        │   ├── app/           # 应用端静态资源
        │   └── publish/       # 其他资源
        └── plugin.json

🔧 技术栈

后端技术

技术说明
.NET 10最新版本的 .NET 运行时
SqlSugar ORM轻量级高性能 ORM
OIDC / OpenIddict身份认证和授权
Autofac依赖注入容器
Swagger/OpenAPIAPI 文档

前端技术

技术说明
Vue 3渐进式 JavaScript 框架
TypeScript类型安全的 JavaScript
Element PlusVue 3 UI 组件库
Vite下一代构建工具
qiankun微前端框架
Pinia状态管理

📦 插件元数据 (plugin.json)

每个插件都包含一个 plugin.json 文件,描述插件的基本信息:

json
{
  "PluginId": "12345678901234567",
  "Name": "MyPlugin",
  "DisplayName": "我的插件",
  "Version": "1.0.0",
  "Description": "这是一个示例插件",
  "Author": "Your Name",
  "Website": "https://example.com",
  "MinHostVersion": "1.0.0",
  "Enabled": true
}

字段说明:

  • PluginId: 从官网申请的唯一标识(必需)
  • Name: 插件名称(英文,用于命名空间)
  • DisplayName: 显示名称(中文)
  • Version: 版本号(语义化版本)
  • Description: 插件描述
  • Author: 作者
  • Website: 官方网站
  • MinHostVersion: 最低宿主版本要求
  • Enabled: 是否启用

🔄 插件生命周期

1. 创建阶段

使用 CLI 工具生成项目骨架:

bash
fd-plugin create --name MyPlugin --plugin-id 12345678901234567

2. 开发阶段

  • 后端:添加控制器、服务、数据模型
  • 前端:开发页面、组件、路由
  • 调试:本地测试功能

3. 打包阶段

bash
# 构建后端
dotnet publish -c Release

# 构建前端
pnpm build

# 打包为 ZIP
zip -r MyPlugin.zip publish/

4. 发布阶段

上传到插件市场或手动部署到服务器。

5. 安装阶段

通过插件市场安装或手动复制到 Plugins/ 目录。

6. 运行阶段

插件被加载到内存中,提供服务。


🎨 前端双端架构

Fastdotnet 插件的前端分为两个独立的项目:

管理端 (Admin)

  • 目标用户: 系统管理员、运营人员
  • 功能: 配置管理、数据管理、权限控制
  • 路由前缀: /micro/{PluginId}/admin/
  • 端口: 开发时通常使用 8099

应用端 (App)

  • 目标用户: 最终用户
  • 功能: 业务操作、数据展示、工作流程
  • 路由前缀: /micro/{PluginId}/app/
  • 端口: 开发时通常使用 8100

为什么分两端?

  1. 职责分离: 管理和业务逻辑清晰分开
  2. 权限控制: 不同用户看到不同界面
  3. 性能优化: 可以独立加载和优化
  4. 用户体验: 针对不同场景定制 UI

🔌 插件通信机制

1. API 调用

前端通过 HTTP 请求调用后端 API:

typescript
// 前端调用
import axios from 'axios'

const response = await axios.get('/api/sample/data')

2. 事件总线

插件之间通过事件进行松耦合通信:

csharp
// 发布事件
await _eventBus.PublishAsync(new OrderCreatedEvent(orderId));

// 订阅事件
[EventHandler]
public async Task HandleOrderCreated(OrderCreatedEvent evt)
{
    // 处理订单创建事件
}

3. 依赖注入

插件可以注册自己的服务,也可以访问主程序的服务:

csharp
public override void ConfigureServices(IServiceCollection services)
{
    // 注册插件服务
    services.AddScoped<IMyService, MyService>();
    
    // 访问主程序服务
    var dbContext = services.BuildServiceProvider()
        .GetService<AppDbContext>();
}

⚡ 热插拔原理

AssemblyLoadContext (ALC)

Fastdotnet 使用 .NET 的 AssemblyLoadContext 技术实现插件隔离:

  1. 独立上下文: 每个插件在独立的 ALC 中加载
  2. 版本隔离: 不同插件可以使用同一库的不同版本
  3. 故障隔离: 插件崩溃不影响主程序
  4. 动态卸载: 可以卸载插件释放资源

加载流程

1. 扫描 Plugins 目录
2. 读取 plugin.json
3. 创建 AssemblyLoadContext
4. 加载插件 DLL
5. 调用 InitializeAsync()
6. 注册路由和服务
7. 插件就绪

📊 插件 vs 模块

特性插件模块
编译时机运行时加载编译时确定
独立性完全独立依赖主程序
更新方式热更新需要重新编译
适用场景可选功能核心功能
版本管理独立版本跟随主程序

🎯 最佳实践

1. 命名规范

  • 插件名称: PascalCase(例如:MyPlugin
  • Plugin ID: 小写字母+数字+连字符(例如:my-plugin-123
  • 命名空间: {PluginName}(例如:MyPlugin.Controllers

2. 版本管理

使用语义化版本:MAJOR.MINOR.PATCH

  • MAJOR: 不兼容的 API 变更
  • MINOR: 向后兼容的功能新增
  • PATCH: 向后兼容的问题修正

3. 错误处理

csharp
try
{
    // 业务逻辑
}
catch (Exception ex)
{
    _logger.LogError(ex, "操作失败");
    return StatusCode(500, new { error = "Internal server error" });
}

4. 日志记录

csharp
public class SampleController : ControllerBase
{
    private readonly ILogger<SampleController> _logger;
    
    public SampleController(ILogger<SampleController> logger)
    {
        _logger = logger;
    }
    
    public IActionResult GetData()
    {
        _logger.LogInformation("获取数据");
        return Ok(data);
    }
}

📚 下一步


🔗 相关链接

Released under the MIT License.