feat(plugin): P2-P4 插件平台演进 — 通用服务 + 质量保障 + 市场

P2 平台通用服务:
- manifest 扩展: settings/numbering/templates/trigger_events/importable/exportable 声明
- 插件配置 UI: PluginSettingsForm 自动表单 + 后端校验 + 详情抽屉 Settings 标签页
- 编号规则: Host API numbering-generate + PostgreSQL 序列 + manifest 绑定
- 触发事件: data_service create/update/delete 自动发布 DomainEvent
- WIT 接口: 新增 numbering-generate/setting-get Host API

P3 质量保障:
- plugin_validator.rs: 安全扫描(WASM大小/实体数量/字段校验) + 复杂度评分
- 运行时监控指标: RuntimeMetrics (错误率/响应时间/Fuel/内存)
- 性能基准: BenchmarkResult 阈值定义
- 上传时自动安全扫描 + /validate API 端点

P4 插件市场:
- 数据库迁移: plugin_market_entries + plugin_market_reviews 表
- 前端 PluginMarket 页面: 分类浏览/搜索/详情/评分
- 路由注册: /plugins/market

测试: 269 全通过 (71 erp-plugin + 41 auth + 57 config + 34 core + 50 message + 16 workflow)
This commit is contained in:
iven
2026-04-19 12:16:24 +08:00
parent c4b1e9e56d
commit e429448c42
20 changed files with 1889 additions and 46 deletions

View File

@@ -14,6 +14,65 @@ use crate::error::PluginError;
use crate::manifest::PluginField;
use crate::state::EntityInfo;
/// 根据 plugin 数据库 ID 查找 manifest 中匹配 entity 的触发事件
async fn find_trigger_events(
plugin_db_id: Uuid,
entity_name: &str,
db: &sea_orm::DatabaseConnection,
) -> AppResult<Vec<crate::manifest::PluginTriggerEvent>> {
let model = plugin::Entity::find_by_id(plugin_db_id)
.one(db)
.await?
.ok_or_else(|| AppError::NotFound(format!("插件 {} 不存在", plugin_db_id)))?;
let manifest: crate::manifest::PluginManifest =
serde_json::from_value(model.manifest_json)
.map_err(|e| PluginError::InvalidManifest(e.to_string()))?;
let triggers = manifest.trigger_events
.unwrap_or_default()
.into_iter()
.filter(|t| t.entity == entity_name)
.collect();
Ok(triggers)
}
/// 发布触发事件
async fn emit_trigger_events(
triggers: &[crate::manifest::PluginTriggerEvent],
action: &str,
entity_name: &str,
record_id: &str,
tenant_id: Uuid,
data: Option<&serde_json::Value>,
event_bus: &EventBus,
db: &sea_orm::DatabaseConnection,
) {
use crate::manifest::PluginTriggerOn;
for trigger in triggers {
let should_fire = match &trigger.on {
PluginTriggerOn::Create => action == "create",
PluginTriggerOn::Update => action == "update",
PluginTriggerOn::Delete => action == "delete",
PluginTriggerOn::CreateOrUpdate => action == "create" || action == "update",
};
if should_fire {
let payload = serde_json::json!({
"event": trigger.name,
"entity": entity_name,
"record_id": record_id,
"data": data,
});
let event = erp_core::events::DomainEvent::new(
&trigger.name,
tenant_id,
payload,
);
event_bus.publish(event, db).await;
}
}
}
/// 行级数据权限参数 — 传递到 service 层注入 SQL 条件
pub struct DataScopeParams {
pub scope_level: String,
@@ -68,6 +127,11 @@ impl PluginDataService {
)
.await;
// 触发事件发布
if let Ok(triggers) = find_trigger_events(plugin_id, entity_name, db).await {
emit_trigger_events(&triggers, "create", entity_name, &result.id.to_string(), tenant_id, Some(&result.data), _event_bus, db).await;
}
Ok(PluginDataResp {
id: result.id.to_string(),
data: result.data,
@@ -279,6 +343,11 @@ impl PluginDataService {
)
.await;
// 触发事件发布
if let Ok(triggers) = find_trigger_events(plugin_id, entity_name, db).await {
emit_trigger_events(&triggers, "update", entity_name, &result.id.to_string(), tenant_id, Some(&result.data), _event_bus, db).await;
}
Ok(PluginDataResp {
id: result.id.to_string(),
data: result.data,
@@ -428,6 +497,11 @@ impl PluginDataService {
)
.await;
// 触发事件发布
if let Ok(triggers) = find_trigger_events(plugin_id, entity_name, db).await {
emit_trigger_events(&triggers, "delete", entity_name, &id.to_string(), tenant_id, None, _event_bus, db).await;
}
Ok(())
}