流程编排界面节点元数据和Schema配置改为通过api获取
This commit is contained in:
parent
1d00939152
commit
24eb9f6199
@ -9,7 +9,7 @@ import { useFlowStore } from './store/flowStore.js';
|
|||||||
import { flowExecutor } from './executors/flowExecutor.js';
|
import { flowExecutor } from './executors/flowExecutor.js';
|
||||||
import NodePalette from './components/Sidebar/NodePalette.vue';
|
import NodePalette from './components/Sidebar/NodePalette.vue';
|
||||||
import ExecutionResults from './components/ExecutionResults.vue';
|
import ExecutionResults from './components/ExecutionResults.vue';
|
||||||
import { getNodeComponents, getNodeMetadataByType } from './utils/nodeMetadata.js';
|
import { getNodeComponents, getNodeMetadataByType, preloadNodeMetadata } from './utils/nodeMetadata.js';
|
||||||
import { t } from '@/shared/i18n'
|
import { t } from '@/shared/i18n'
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
@ -136,8 +136,24 @@ const onPaneReady = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 自动化获取所有节点组件,使用markRaw避免响应式
|
// 节点组件映射(响应式)
|
||||||
const nodeTypes = markRaw(getNodeComponents());
|
const nodeTypes = ref({});
|
||||||
|
const nodeTypesLoaded = ref(false);
|
||||||
|
|
||||||
|
// 预加载节点元数据
|
||||||
|
onMounted(async () => {
|
||||||
|
await preloadNodeMetadata();
|
||||||
|
// 更新节点组件映射
|
||||||
|
const components = getNodeComponents();
|
||||||
|
nodeTypes.value = markRaw(components);
|
||||||
|
nodeTypesLoaded.value = true;
|
||||||
|
|
||||||
|
// 节点元数据加载完成后,再初始化流程数据
|
||||||
|
nextTick(async () => {
|
||||||
|
initFlowData();
|
||||||
|
store.saveToHistory();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 获取节点颜色函数
|
// 获取节点颜色函数
|
||||||
const getNodeColor = (nodeType) => {
|
const getNodeColor = (nodeType) => {
|
||||||
@ -325,11 +341,6 @@ const onKeyDown = (event) => {
|
|||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(async () => {
|
|
||||||
// 初始化流程数据
|
|
||||||
initFlowData();
|
|
||||||
store.saveToHistory();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 暴露 teleport 目标到全局,供节点组件使用
|
// 暴露 teleport 目标到全局,供节点组件使用
|
||||||
window.nodePropertyTeleportTarget = {
|
window.nodePropertyTeleportTarget = {
|
||||||
@ -617,6 +628,7 @@ const showBubbleTip = (msg, type = '') => {
|
|||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<VueFlow
|
<VueFlow
|
||||||
|
v-if="nodeTypesLoaded"
|
||||||
ref="vueFlowInstance"
|
ref="vueFlowInstance"
|
||||||
:nodes="nodes"
|
:nodes="nodes"
|
||||||
:edges="edges"
|
:edges="edges"
|
||||||
@ -677,6 +689,14 @@ const showBubbleTip = (msg, type = '') => {
|
|||||||
<!-- 顶部面板 -->
|
<!-- 顶部面板 -->
|
||||||
<!-- Panel position="top-center" class="status-panel" 移除 -->
|
<!-- Panel position="top-center" class="status-panel" 移除 -->
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
|
|
||||||
|
<!-- 加载提示 -->
|
||||||
|
<div v-else class="loading-container">
|
||||||
|
<div class="loading-spinner">
|
||||||
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
|
<p>正在加载节点类型...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 调试:显示 edges 和 nodes 的 JSON -->
|
<!-- 调试:显示 edges 和 nodes 的 JSON -->
|
||||||
<!-- 模板数据弹窗 -->
|
<!-- 模板数据弹窗 -->
|
||||||
@ -1219,4 +1239,29 @@ const showBubbleTip = (msg, type = '') => {
|
|||||||
.ai-agent-flow-builder.fullscreen .bubble-tip-global {
|
.ai-agent-flow-builder.fullscreen .bubble-tip-global {
|
||||||
z-index: 100000;
|
z-index: 100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 加载提示样式 */
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
text-align: center;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner i {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -16,29 +16,27 @@ function resolveComponent(componentType) {
|
|||||||
return COMPONENT_MAP[componentType] || GenericNode
|
return COMPONENT_MAP[componentType] || GenericNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发现所有节点的元数据(从本地JSON文件)
|
// 发现所有节点的元数据(从API获取)
|
||||||
function discoverNodeMetadata() {
|
async function discoverNodeMetadata() {
|
||||||
// 使用 import.meta.glob 获取所有节点JSON文件
|
try {
|
||||||
// 注意:Vite 要求以 './' 或 '/' 开头,别名在 glob 中不可用
|
const response = await fetch('/jingrow/node-definitions/metadata');
|
||||||
// 从当前文件路径 ../../../../../../../ 指向到项目根,再进入 apps/jingrow/nodes 目录(core 比原 features 深一层)
|
|
||||||
const modulesNew = import.meta.glob('../../../../../../../../apps/jingrow/jingrow/ai/pagetype/local_ai_agent/nodes/**/*.json', { eager: true })
|
|
||||||
|
|
||||||
const metadataMap = {}
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
Object.keys(modulesNew).forEach(path => {
|
|
||||||
try {
|
|
||||||
const module = modulesNew[path]
|
|
||||||
const data = module.default || module
|
|
||||||
|
|
||||||
// 检查是否有 metadata 字段
|
|
||||||
if (data && typeof data === 'object' && data.metadata && data.metadata.type) {
|
|
||||||
metadataMap[data.metadata.type] = data.metadata
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// 静默失败,避免污染控制台
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
return metadataMap
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
return result.data;
|
||||||
|
} else {
|
||||||
|
console.warn('获取节点元数据失败:', result);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取节点元数据失败:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动生成节点分组
|
// 自动生成节点分组
|
||||||
@ -60,36 +58,65 @@ function generateNodeGroups(metadataMap) {
|
|||||||
return Object.values(groupMap)
|
return Object.values(groupMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 节点元数据映射(同步初始化)
|
// 节点元数据映射(异步初始化)
|
||||||
const NODE_METADATA_MAP = {}
|
let NODE_METADATA_MAP = {}
|
||||||
const LOCAL_METADATA = discoverNodeMetadata()
|
let NODE_GROUPS = []
|
||||||
|
let isInitialized = false
|
||||||
|
|
||||||
// 构建最终的元数据映射
|
// 初始化节点元数据
|
||||||
Object.keys(LOCAL_METADATA).forEach(type => {
|
async function initializeNodeMetadata() {
|
||||||
const metadata = LOCAL_METADATA[type]
|
if (isInitialized) return
|
||||||
NODE_METADATA_MAP[type] = {
|
|
||||||
...metadata,
|
try {
|
||||||
component: resolveComponent(metadata.component_type)
|
const LOCAL_METADATA = await discoverNodeMetadata()
|
||||||
|
|
||||||
|
// 构建最终的元数据映射
|
||||||
|
NODE_METADATA_MAP = {}
|
||||||
|
Object.keys(LOCAL_METADATA).forEach(type => {
|
||||||
|
const metadata = LOCAL_METADATA[type]
|
||||||
|
NODE_METADATA_MAP[type] = {
|
||||||
|
...metadata,
|
||||||
|
component: resolveComponent(metadata.component)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动生成节点分组
|
||||||
|
NODE_GROUPS = generateNodeGroups(NODE_METADATA_MAP)
|
||||||
|
isInitialized = true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化节点元数据失败:', error)
|
||||||
|
NODE_METADATA_MAP = {}
|
||||||
|
NODE_GROUPS = []
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
// 自动生成节点分组
|
// 导出函数(保持同步接口)
|
||||||
const NODE_GROUPS = generateNodeGroups(NODE_METADATA_MAP)
|
|
||||||
|
|
||||||
// 导出函数
|
|
||||||
export function getNodeMetadataByType(type) {
|
export function getNodeMetadataByType(type) {
|
||||||
|
if (!isInitialized) {
|
||||||
|
// 如果还没初始化,返回null,组件会处理这种情况
|
||||||
|
return null
|
||||||
|
}
|
||||||
return NODE_METADATA_MAP[type]
|
return NODE_METADATA_MAP[type]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllNodeTypes() {
|
export function getAllNodeTypes() {
|
||||||
|
if (!isInitialized) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
return Object.values(NODE_METADATA_MAP)
|
return Object.values(NODE_METADATA_MAP)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNodeGroups() {
|
export function getNodeGroups() {
|
||||||
|
if (!isInitialized) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
return NODE_GROUPS
|
return NODE_GROUPS
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNodeComponents() {
|
export function getNodeComponents() {
|
||||||
|
if (!isInitialized) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
const components = {}
|
const components = {}
|
||||||
Object.keys(NODE_METADATA_MAP).forEach(type => {
|
Object.keys(NODE_METADATA_MAP).forEach(type => {
|
||||||
if (NODE_METADATA_MAP[type].component) {
|
if (NODE_METADATA_MAP[type].component) {
|
||||||
@ -99,5 +126,10 @@ export function getNodeComponents() {
|
|||||||
return components
|
return components
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 预加载函数
|
||||||
|
export async function preloadNodeMetadata() {
|
||||||
|
await initializeNodeMetadata()
|
||||||
|
}
|
||||||
|
|
||||||
// 导出常量
|
// 导出常量
|
||||||
export { NODE_METADATA_MAP, NODE_GROUPS }
|
export { NODE_METADATA_MAP, NODE_GROUPS }
|
||||||
@ -4,29 +4,20 @@
|
|||||||
* @returns {Promise<Object>} Schema配置对象
|
* @returns {Promise<Object>} Schema配置对象
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const LOCAL_NODE_SCHEMAS_NEW = import.meta.glob('../../../../../../../../apps/jingrow/jingrow/ai/pagetype/local_ai_agent/nodes/*/*.json', { eager: true });
|
|
||||||
|
|
||||||
function loadLocalSchemaByConvention(nodeType) {
|
|
||||||
try {
|
|
||||||
const suffix = `/${nodeType}/${nodeType}.json`;
|
|
||||||
for (const path of Object.keys(LOCAL_NODE_SCHEMAS_NEW)) {
|
|
||||||
if (path.endsWith(suffix)) {
|
|
||||||
const mod = LOCAL_NODE_SCHEMAS_NEW[path];
|
|
||||||
const data = mod?.default || mod;
|
|
||||||
if (data && data.properties) return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export async function loadNodeSchema(nodeType) {
|
export async function loadNodeSchema(nodeType) {
|
||||||
try {
|
try {
|
||||||
const localByPath = loadLocalSchemaByConvention(nodeType);
|
const response = await fetch(`/jingrow/node-definitions/schema/${nodeType}`);
|
||||||
return localByPath || {};
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success && result.data) {
|
||||||
|
return result.data;
|
||||||
|
} else {
|
||||||
|
console.warn(`获取节点Schema失败: ${nodeType}`, result);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
console.error(`Schema加载失败: ${nodeType}`, error);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ async def export_node_definition(payload: Dict[str, Any]):
|
|||||||
export_data = {"metadata": metadata, **(schema or {})}
|
export_data = {"metadata": metadata, **(schema or {})}
|
||||||
|
|
||||||
current_file = Path(__file__).resolve()
|
current_file = Path(__file__).resolve()
|
||||||
jingrow_root = current_file.parents[2]
|
jingrow_root = current_file.parents[1] # 修正路径层级
|
||||||
new_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
new_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
||||||
target = new_root / node_type / f"{node_type}.json"
|
target = new_root / node_type / f"{node_type}.json"
|
||||||
atomic_write_json(target, export_data)
|
atomic_write_json(target, export_data)
|
||||||
@ -40,7 +40,7 @@ async def import_local_node_definitions():
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
current_file = Path(__file__).resolve()
|
current_file = Path(__file__).resolve()
|
||||||
jingrow_root = current_file.parents[2]
|
jingrow_root = current_file.parents[1] # 修正路径层级
|
||||||
nodes_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
nodes_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
||||||
if not nodes_root.exists():
|
if not nodes_root.exists():
|
||||||
return {"success": True, "matched": 0, "imported": 0, "skipped_existing": 0}
|
return {"success": True, "matched": 0, "imported": 0, "skipped_existing": 0}
|
||||||
@ -110,3 +110,80 @@ async def import_local_node_definitions():
|
|||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/jingrow/node-definitions/metadata")
|
||||||
|
async def get_all_node_metadata():
|
||||||
|
"""
|
||||||
|
获取所有节点的元数据,用于流程编排界面
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_file = Path(__file__).resolve()
|
||||||
|
jingrow_root = current_file.parents[1] # 修正路径层级
|
||||||
|
nodes_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
||||||
|
|
||||||
|
if not nodes_root.exists():
|
||||||
|
return {"success": True, "data": {}}
|
||||||
|
|
||||||
|
metadata_map = {}
|
||||||
|
|
||||||
|
for node_dir in nodes_root.iterdir():
|
||||||
|
if not node_dir.is_dir():
|
||||||
|
continue
|
||||||
|
json_file = node_dir / f"{node_dir.name}.json"
|
||||||
|
if not json_file.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(json_file, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
metadata = data.get("metadata") or {}
|
||||||
|
node_type = metadata.get("type")
|
||||||
|
if not node_type:
|
||||||
|
continue
|
||||||
|
|
||||||
|
metadata_map[node_type] = {
|
||||||
|
"type": node_type,
|
||||||
|
"label": metadata.get("label") or node_type,
|
||||||
|
"icon": metadata.get("icon") or "fa-cube",
|
||||||
|
"color": metadata.get("color") or "#6b7280",
|
||||||
|
"description": metadata.get("description") or "",
|
||||||
|
"group": metadata.get("group") or "其他",
|
||||||
|
"component": metadata.get("component_type") or "GenericNode"
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return {"success": True, "data": metadata_map}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/jingrow/node-definitions/schema/{node_type}")
|
||||||
|
async def get_node_schema(node_type: str):
|
||||||
|
"""
|
||||||
|
获取指定节点类型的Schema配置
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_file = Path(__file__).resolve()
|
||||||
|
jingrow_root = current_file.parents[1]
|
||||||
|
nodes_root = jingrow_root / "ai" / "pagetype" / "local_ai_agent" / "nodes"
|
||||||
|
json_file = nodes_root / node_type / f"{node_type}.json"
|
||||||
|
|
||||||
|
if not json_file.exists():
|
||||||
|
raise HTTPException(status_code=404, detail=f"节点类型 {node_type} 不存在")
|
||||||
|
|
||||||
|
with open(json_file, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
schema = dict(data)
|
||||||
|
schema.pop("metadata", None)
|
||||||
|
|
||||||
|
return {"success": True, "data": schema}
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise HTTPException(status_code=404, detail=f"节点类型 {node_type} 不存在")
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|||||||
221
test_api.py
221
test_api.py
@ -1,221 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
测试本地版REST API和钩子功能
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
# 配置
|
|
||||||
BASE_URL = "http://localhost:9001"
|
|
||||||
TEST_PAGETYPE = "Test Page"
|
|
||||||
TEST_NAME = "TEST-001"
|
|
||||||
|
|
||||||
def test_create_record():
|
|
||||||
"""测试创建记录"""
|
|
||||||
print("=== 测试创建记录 ===")
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"name": TEST_NAME,
|
|
||||||
"title": "测试记录",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.post(
|
|
||||||
f"{BASE_URL}/api/data/{TEST_PAGETYPE}",
|
|
||||||
json=data,
|
|
||||||
headers={"Content-Type": "application/json"}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 创建记录成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 创建记录失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 创建记录异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_update_record():
|
|
||||||
"""测试更新记录"""
|
|
||||||
print("\n=== 测试更新记录 ===")
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"title": "更新后的标题",
|
|
||||||
"status": "Inactive"
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.put(
|
|
||||||
f"{BASE_URL}/api/data/{TEST_PAGETYPE}/{TEST_NAME}",
|
|
||||||
json=data,
|
|
||||||
headers={"Content-Type": "application/json"}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 更新记录成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 更新记录失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 更新记录异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_get_record():
|
|
||||||
"""测试获取记录"""
|
|
||||||
print("\n=== 测试获取记录 ===")
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.get(
|
|
||||||
f"{BASE_URL}/api/data/{TEST_PAGETYPE}/{TEST_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 获取记录成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 获取记录失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 获取记录异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_get_records():
|
|
||||||
"""测试获取记录列表"""
|
|
||||||
print("\n=== 测试获取记录列表 ===")
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.get(
|
|
||||||
f"{BASE_URL}/api/data/{TEST_PAGETYPE}",
|
|
||||||
params={
|
|
||||||
"limit_page_length": 10
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 获取记录列表成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 获取记录列表失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 获取记录列表异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_delete_record():
|
|
||||||
"""测试删除记录"""
|
|
||||||
print("\n=== 测试删除记录 ===")
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.delete(
|
|
||||||
f"{BASE_URL}/api/data/{TEST_PAGETYPE}/{TEST_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 删除记录成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 删除记录失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 删除记录异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_hook_execution():
|
|
||||||
"""测试钩子执行"""
|
|
||||||
print("\n=== 测试钩子执行 ===")
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"pagetype": TEST_PAGETYPE,
|
|
||||||
"name": TEST_NAME,
|
|
||||||
"hook_name": "on_update",
|
|
||||||
"data": {"test": "hook data"}
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.post(
|
|
||||||
f"{BASE_URL}/api/hooks/execute",
|
|
||||||
json=data,
|
|
||||||
headers={"Content-Type": "application/json"}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"状态码: {response.status_code}")
|
|
||||||
print(f"响应: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
print("✅ 钩子执行成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ 钩子执行失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 钩子执行异常: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主测试函数"""
|
|
||||||
print("开始测试本地版REST API和钩子功能...")
|
|
||||||
print(f"测试目标: {BASE_URL}")
|
|
||||||
print(f"测试PageType: {TEST_PAGETYPE}")
|
|
||||||
print(f"测试记录名: {TEST_NAME}")
|
|
||||||
|
|
||||||
# 等待服务启动
|
|
||||||
print("\n等待服务启动...")
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
# 执行测试
|
|
||||||
tests = [
|
|
||||||
test_create_record,
|
|
||||||
test_update_record,
|
|
||||||
test_get_record,
|
|
||||||
test_get_records,
|
|
||||||
test_hook_execution,
|
|
||||||
test_delete_record
|
|
||||||
]
|
|
||||||
|
|
||||||
passed = 0
|
|
||||||
total = len(tests)
|
|
||||||
|
|
||||||
for test in tests:
|
|
||||||
try:
|
|
||||||
if test():
|
|
||||||
passed += 1
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 测试异常: {e}")
|
|
||||||
|
|
||||||
print(f"\n=== 测试结果 ===")
|
|
||||||
print(f"通过: {passed}/{total}")
|
|
||||||
print(f"成功率: {passed/total*100:.1f}%")
|
|
||||||
|
|
||||||
if passed == total:
|
|
||||||
print("🎉 所有测试通过!")
|
|
||||||
else:
|
|
||||||
print("⚠️ 部分测试失败,请检查日志")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user