fix(plugin): P1 跨插件引用修复 — DateTime generated column + resolve-labels UUID 类型 + EntitySelect manifest→UUID 映射
- manifest.rs: DateTime 类型 generated column 改为 TEXT 存储(PostgreSQL TIMESTAMPTZ cast 非 immutable) - data_handler.rs: resolve-labels 查询参数从 String 改为 UUID 类型避免类型不匹配 - data_dto.rs: PublicEntityResp 新增 plugin_id 字段 - EntitySelect.tsx: 跨插件查询先通过 registry 解析 manifest_id→plugin UUID - pluginData.ts: PublicEntity 接口增加 plugin_id - plugin_tests.rs: 适配 PluginField/PluginEntity 新增字段
This commit is contained in:
@@ -117,6 +117,8 @@ impl PluginService {
|
||||
entity_name: Set(entity_def.name.clone()),
|
||||
table_name: Set(table_name.clone()),
|
||||
schema_json: Set(serde_json::to_value(entity_def).unwrap_or_default()),
|
||||
manifest_id: Set(manifest.metadata.id.clone()),
|
||||
is_public: Set(entity_def.is_public.unwrap_or(false)),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
created_by: Set(Some(operator_id)),
|
||||
@@ -166,6 +168,10 @@ impl PluginService {
|
||||
})?;
|
||||
}
|
||||
|
||||
// 将插件权限自动分配给 admin 角色
|
||||
tracing::info!("Granting plugin permissions to admin role");
|
||||
grant_permissions_to_admin(db, tenant_id, &manifest.metadata.id).await?;
|
||||
|
||||
// 加载到内存
|
||||
tracing::info!(manifest_id = %manifest.metadata.id, "Loading plugin into engine");
|
||||
engine
|
||||
@@ -204,6 +210,9 @@ impl PluginService {
|
||||
|
||||
let plugin_manifest_id = &manifest.metadata.id;
|
||||
|
||||
// 确保插件权限已分配给 admin 角色(幂等操作)
|
||||
grant_permissions_to_admin(db, tenant_id, plugin_manifest_id).await?;
|
||||
|
||||
// 如果之前是 disabled 状态,需要先卸载再重新加载到内存
|
||||
// (disable 只改内存状态但不从 DashMap 移除)
|
||||
if model.status == "disabled" {
|
||||
@@ -551,32 +560,51 @@ impl PluginService {
|
||||
|
||||
let plugin_manifest_id = &new_manifest.metadata.id;
|
||||
|
||||
// 对比 schema — 为新增实体创建动态表
|
||||
// 对比 schema — 为新增实体创建动态表 + 已有实体字段演进
|
||||
if let Some(new_schema) = &new_manifest.schema {
|
||||
let old_entities: Vec<&str> = old_manifest
|
||||
.schema
|
||||
.as_ref()
|
||||
.map(|s| s.entities.iter().map(|e| e.name.as_str()).collect())
|
||||
.unwrap_or_default();
|
||||
let old_schema = old_manifest.schema.as_ref();
|
||||
|
||||
for entity in &new_schema.entities {
|
||||
if !old_entities.contains(&entity.name.as_str()) {
|
||||
tracing::info!(entity = %entity.name, "创建新增实体表");
|
||||
DynamicTableManager::create_table(db, plugin_manifest_id, entity).await?;
|
||||
let old_entity = old_schema
|
||||
.and_then(|s| s.entities.iter().find(|e| e.name == entity.name));
|
||||
|
||||
match old_entity {
|
||||
None => {
|
||||
tracing::info!(entity = %entity.name, "创建新增实体表");
|
||||
DynamicTableManager::create_table(db, plugin_manifest_id, entity).await?;
|
||||
}
|
||||
Some(old) => {
|
||||
let diff = DynamicTableManager::diff_entity_fields(old, entity);
|
||||
if !diff.new_filterable.is_empty() || !diff.new_searchable.is_empty() {
|
||||
tracing::info!(
|
||||
entity = %entity.name,
|
||||
new_cols = diff.new_filterable.len(),
|
||||
new_search = diff.new_searchable.len(),
|
||||
"Schema 演进:新增 Generated Column"
|
||||
);
|
||||
DynamicTableManager::alter_add_generated_columns(
|
||||
db, plugin_manifest_id, entity, &diff
|
||||
).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卸载旧 WASM 并加载新 WASM
|
||||
engine.unload(plugin_manifest_id).await.ok();
|
||||
// 先加载新版本到临时 key,确保成功后再替换旧版本(原子回滚)
|
||||
let temp_id = format!("{}__upgrade_{}", plugin_manifest_id, Uuid::now_v7());
|
||||
engine
|
||||
.load(plugin_manifest_id, &new_wasm, new_manifest.clone())
|
||||
.load(&temp_id, &new_wasm, new_manifest.clone())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, "新版本 WASM 加载失败");
|
||||
tracing::error!(error = %e, "新版本 WASM 加载失败,旧版本仍在运行");
|
||||
e
|
||||
})?;
|
||||
|
||||
// 新版本加载成功,卸载旧版本并重命名新版本为正式 key
|
||||
engine.unload(plugin_manifest_id).await.ok();
|
||||
engine.rename_plugin(&temp_id, plugin_manifest_id).await?;
|
||||
|
||||
// 更新数据库记录
|
||||
let wasm_hash = {
|
||||
let mut hasher = Sha256::new();
|
||||
@@ -822,6 +850,76 @@ async fn register_plugin_permissions(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 将插件的所有权限分配给 admin 角色。
|
||||
///
|
||||
/// 使用 raw SQL 按 manifest_id 前缀匹配权限,INSERT 到 role_permissions。
|
||||
/// ON CONFLICT DO NOTHING 保证幂等。
|
||||
pub async fn grant_permissions_to_admin(
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
tenant_id: Uuid,
|
||||
plugin_manifest_id: &str,
|
||||
) -> AppResult<()> {
|
||||
let prefix = format!("{}.%", plugin_manifest_id);
|
||||
|
||||
let sql = r#"
|
||||
INSERT INTO role_permissions (tenant_id, role_id, permission_id, data_scope, created_at, updated_at, created_by, updated_by, deleted_at, version)
|
||||
SELECT
|
||||
p.tenant_id,
|
||||
r.id,
|
||||
p.id,
|
||||
'all',
|
||||
NOW(), NOW(),
|
||||
r.id, r.id,
|
||||
NULL, 1
|
||||
FROM permissions p
|
||||
CROSS JOIN roles r
|
||||
WHERE p.tenant_id = $1
|
||||
AND r.tenant_id = $1
|
||||
AND r.code = 'admin'
|
||||
AND r.deleted_at IS NULL
|
||||
AND p.code LIKE $2
|
||||
AND p.deleted_at IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM role_permissions rp
|
||||
WHERE rp.permission_id = p.id
|
||||
AND rp.role_id = r.id
|
||||
AND rp.deleted_at IS NULL
|
||||
)
|
||||
ON CONFLICT (role_id, permission_id) DO NOTHING
|
||||
"#;
|
||||
|
||||
let result = db
|
||||
.execute(sea_orm::Statement::from_sql_and_values(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
sql,
|
||||
vec![
|
||||
sea_orm::Value::from(tenant_id),
|
||||
sea_orm::Value::from(prefix.clone()),
|
||||
],
|
||||
))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(
|
||||
plugin = plugin_manifest_id,
|
||||
error = %e,
|
||||
"分配插件权限给 admin 角色失败"
|
||||
);
|
||||
PluginError::DatabaseError(format!(
|
||||
"分配插件权限给 admin 角色失败: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let rows = result.rows_affected();
|
||||
tracing::info!(
|
||||
plugin = plugin_manifest_id,
|
||||
rows_affected = rows,
|
||||
tenant_id = %tenant_id,
|
||||
"插件权限已分配给 admin 角色"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 清理插件注册的权限(软删除)。
|
||||
///
|
||||
/// 使用 raw SQL 按前缀匹配清理:`{plugin_manifest_id}.%`。
|
||||
|
||||
Reference in New Issue
Block a user