fix(plugin): P1 跨插件引用修复 — DateTime generated column + resolve-labels UUID 类型 + EntitySelect manifest→UUID 映射
Some checks failed
CI / rust-check (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
CI / rust-test (push) Has been cancelled

- 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:
iven
2026-04-19 08:44:45 +08:00
parent 08252c10f1
commit 0ee9d22634
8 changed files with 160 additions and 26 deletions

Binary file not shown.

View File

@@ -198,6 +198,7 @@ export async function resolveRefLabels(
export interface PublicEntity {
manifest_id: string;
plugin_id: string;
entity_name: string;
display_name: string;
}

View File

@@ -1,7 +1,7 @@
import { Select, Spin, Input, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { useState, useEffect, useCallback } from 'react';
import { listPluginData } from '../api/pluginData';
import { listPluginData, getPluginEntityRegistry } from '../api/pluginData';
interface EntitySelectProps {
pluginId: string;
@@ -37,12 +37,38 @@ export default function EntitySelect({
const [options, setOptions] = useState<{ value: string; label: string }[]>([]);
const [loading, setLoading] = useState(false);
const [targetUnavailable, setTargetUnavailable] = useState(false);
const [resolvedPluginId, setResolvedPluginId] = useState<string | null>(null);
// 跨插件时使用目标插件 ID 查询
const effectivePluginId = refPlugin || pluginId;
// 跨插件时:先解析 manifest_id → plugin UUID
useEffect(() => {
if (!refPlugin) {
setResolvedPluginId(pluginId);
return;
}
let cancelled = false;
(async () => {
try {
const registry = await getPluginEntityRegistry();
const match = registry.find((e) => e.manifest_id === refPlugin && e.entity_name === entity);
if (!cancelled) {
setResolvedPluginId(match ? match.plugin_id : null);
if (!match) setTargetUnavailable(true);
}
} catch {
if (!cancelled) {
setTargetUnavailable(true);
setResolvedPluginId(null);
}
}
})();
return () => { cancelled = true; };
}, [refPlugin, pluginId, entity]);
const effectivePluginId = resolvedPluginId || pluginId;
const fetchData = useCallback(
async (keyword?: string) => {
if (!resolvedPluginId && refPlugin) return;
setLoading(true);
try {
const filter: Record<string, string> | undefined =
@@ -70,12 +96,14 @@ export default function EntitySelect({
setLoading(false);
}
},
[effectivePluginId, entity, labelField, cascadeFrom, cascadeFilter, cascadeValue, refPlugin],
[effectivePluginId, entity, labelField, cascadeFrom, cascadeFilter, cascadeValue, refPlugin, resolvedPluginId],
);
useEffect(() => {
fetchData();
}, [fetchData]);
if (resolvedPluginId || !refPlugin) {
fetchData();
}
}, [fetchData, resolvedPluginId, refPlugin]);
// 目标插件未安装 → 降级显示
if (targetUnavailable) {