Pārlūkot izejas kodu

代码优化。 开发数据管理智能体。

maxiaolong 2 mēneši atpakaļ
vecāks
revīzija
3d408c5634
82 mainītis faili ar 1760 papildinājumiem un 22075 dzēšanām
  1. 0 24
      .cursor/mcp.json
  2. 0 106
      .cursor/rules/auto_task_execution.mdc
  3. 0 85
      .cursor/task_execute_instructions.md
  4. 30 0
      app/api/business_domain/routes.py
  5. 12 5
      app/api/meta_data/routes.py
  6. 82 0
      app/core/business_domain/business_domain.py
  7. 156 32
      app/core/data_service/data_product_service.py
  8. 8 6
      app/core/meta_data/meta_data.py
  9. 0 29
      app/scripts/create_user_table.sql
  10. 327 0
      datafactory/datameta-agent-workflow.json
  11. 0 462
      datafactory/scripts/task_43_产品库存表的原始数据导入.py
  12. 0 415
      datafactory/scripts/task_44_DF_DO202601210001.py
  13. 0 232
      docs/API_get_BD_list接口说明.md
  14. 0 258
      docs/BUSINESS_RULES.md
  15. 0 219
      docs/CHANGELOG_auto_deploy.md
  16. 0 253
      docs/DEPLOYMENT_GUIDE.md
  17. 1044 0
      docs/DataMeta管理智能体技术文档.md
  18. 0 154
      docs/PYTHON38_COMPATIBILITY.md
  19. 0 123
      docs/QUICK_FIX.md
  20. 0 166
      docs/QUICK_START_N8N_TOOLS.md
  21. 0 131
      docs/README.md
  22. 0 239
      docs/RELEASE_NOTES.md
  23. 0 248
      docs/TASK_EXECUTION_QUICK_START.md
  24. 0 522
      docs/Task_Manager_MCP_说明.md
  25. 0 729
      docs/api_bd_compose_guide.md
  26. 0 664
      docs/api_get_script.md
  27. 0 107
      docs/api_interface_labellist.md
  28. 0 521
      docs/api_review_create_guide.md
  29. 0 840
      docs/api_review_resolve_guide.md
  30. 0 309
      docs/archive/AUTO_TASK_EXECUTION_FIX.md
  31. 0 311
      docs/archive/CHECK_API_DIAGNOSTIC_REPORT.md
  32. 0 184
      docs/archive/CORS_FIX_README.md
  33. 0 269
      docs/archive/CURSOR_AUTO_EXECUTION_FIX.md
  34. 0 310
      docs/archive/CURSOR_AUTO_TASK_EXECUTION.md
  35. 0 318
      docs/archive/CURSOR_AUTO_TASK_TRIGGER.md
  36. 0 199
      docs/archive/CURSOR_FIX_README.md
  37. 0 319
      docs/archive/CURSOR_TASK_AUTOMATION_SUMMARY.md
  38. 0 163
      docs/archive/DDL_PARSER_TIMEOUT_FIX.md
  39. 0 333
      docs/archive/DDL_PARSE_FIX_SUMMARY.md
  40. 0 363
      docs/archive/DDL_Parse_API修复说明.md
  41. 0 159
      docs/archive/DDL_Parse_数组格式示例.json
  42. 0 137
      docs/archive/DDL_Parse_格式对比.json
  43. 0 148
      docs/archive/DDLparse格式.txt
  44. 0 380
      docs/archive/DataFlow_get_dataflow_by_id优化说明.md
  45. 0 298
      docs/archive/DataFlow_rule提取优化说明.md
  46. 0 266
      docs/archive/DataFlow_script_requirement优化说明.md
  47. 0 501
      docs/archive/DataFlow_task_list优化说明.md
  48. 0 300
      docs/archive/DataFlow_实施步骤优化说明.md
  49. 0 454
      docs/archive/N8N_WORKFLOW_SUMMARY.md
  50. 0 261
      docs/archive/REMOVAL_SUMMARY_CLEAN_LIST.md
  51. 0 303
      docs/archive/TEST_REPORT_218.md
  52. 0 280
      docs/archive/TRIGGER_OPTIMIZATION_SUMMARY.md
  53. 0 245
      docs/archive/WORKFLOW_UPDATE_SUMMARY.md
  54. 0 1053
      docs/archive/add_webpage_talent_api_docs.md
  55. 0 430
      docs/archive/get-parsed-talents-api-documentation.md
  56. 0 25
      docs/archive/get_calendar_info.txt
  57. 0 380
      docs/archive/n8n_chat_trigger_error_diagnosis.md
  58. 0 381
      docs/archive/n8n_internal_error_fix.md
  59. 0 314
      docs/archive/n8n_tools_added_status.md
  60. 0 493
      docs/archive/n8n_workflow_enhancement_summary.md
  61. 0 461
      docs/archive/n8n_workflow_test_report.md
  62. 0 326
      docs/archive/n8n_workflow_test_success.md
  63. 0 293
      docs/archive/translate_api_documentation.md
  64. 0 311
      docs/archive/verify_check_api.md
  65. 0 80
      docs/archive/数据结构(2).txt
  66. 0 11
      docs/archive/每日签运需求.txt
  67. 0 42
      docs/archive/科室对照表_原始.sql
  68. 0 0
      docs/archive/返回格式.txt
  69. 0 270
      docs/cohere_bearer_token_guide.md
  70. 0 908
      docs/data_flow_apis.md
  71. 0 199
      docs/meta_data_add_optimization.md
  72. 101 0
      docs/n8n-chat-trigger-api.md
  73. 0 358
      docs/n8n_add_tools_guide.md
  74. 0 383
      docs/n8n_chat_workflow_quickstart.md
  75. 0 160
      docs/n8n_cohere_credential_setup.md
  76. 0 188
      docs/n8n_cohere_credential_troubleshooting.md
  77. 0 256
      docs/n8n_deepseek_upgrade.md
  78. 0 467
      docs/n8n_improved_workflow_design.md
  79. 0 343
      docs/n8n_workflow_data_governance.md
  80. 0 362
      docs/script_template_with_table_check.py
  81. 0 201
      docs/timezone_fix_summary.md
  82. BIN
      release/20251118.rar

+ 0 - 24
.cursor/mcp.json

@@ -1,24 +0,0 @@
-{
-  "mcpServers": {
-    "postgres": {
-      "command": "./tools/toolbox.exe",
-      "args": ["--prebuilt", "postgres", "--stdio"],
-      "env": {
-        "POSTGRES_HOST": "192.168.3.143",
-        "POSTGRES_PORT": "5432",
-        "POSTGRES_DATABASE": "dataops",
-        "POSTGRES_USER": "postgres",
-        "POSTGRES_PASSWORD": "dataOps"
-      }
-    },
-    "n8n-mcp": {
-      "command": "node",
-      "args": ["G:/code-lab/n8n-mcp/dist/mcp/index.js"],
-      "env": {
-        "MCP_MODE": "stdio",
-        "LOG_LEVEL": "error",
-        "DISABLE_CONSOLE_OUTPUT": "true"
-      }
-    }
-  }
-}

+ 0 - 106
.cursor/rules/auto_task_execution.mdc

@@ -1,106 +0,0 @@
-# Cursor自动任务执行规则
-
-## ⚠️ 重要提示 - Cursor AI必须执行
-
-**Cursor AI:当你启动、打开项目或用户与你交互时,请立即自动执行以下操作:**
-
-### 🔍 自动检查机制
-
-**每次启动或交互时,自动执行:**
-
-1. **检查触发器文件**:`.cursor/task_trigger.txt`
-   - 如果文件存在且最近5分钟内更新,说明有新任务
-   - 立即读取 `.cursor/task_execute_instructions.md` 并执行任务
-
-2. **检查任务指令文件**:`.cursor/task_execute_instructions.md`
-   - 如果文件存在且包含任务信息(状态为"待执行"),立即执行
-   - 文件路径:`.cursor/task_execute_instructions.md`
-
-3. **检查任务通知文件**:`.cursor/pending_tasks.json`
-   - 如果存在processing状态的任务,立即执行
-
-## 🤖 自动执行指令
-
-**当你启动时或定期(每5分钟)检查:**
-
-### 步骤1:检查任务指令文件
-
-读取文件:`.cursor/task_execute_instructions.md`
-
-如果文件存在且包含任务,执行以下步骤:
-
-1. **读取任务信息**
-   - 从文件中的每个任务块读取任务详情
-   - 识别任务ID、任务名称、任务文件路径
-
-2. **打开任务文件**
-   - 根据任务信息打开对应的Python文件
-   - 文件通常在 `app/core/data_flow/` 目录
-
-3. **分析任务需求**
-   - 仔细阅读任务描述中的所有内容
-   - 理解功能要求、数据源配置、目标表结构等
-
-4. **实现或完善功能**
-   - 根据任务描述编写或完善代码
-   - 确保代码符合项目规范(PEP 8)
-   - 添加必要的错误处理和日志记录
-   - 如果任务文件已存在但只是模板,需要完善实现
-
-5. **更新任务状态**
-   - 代码完成后,立即调用MCP工具 `update_task_status`
-   - 使用任务ID、代码文件名和路径
-   - 更新状态为 `completed`
-
-### 🚀 立即执行指令
-
-**当检测到任务指令文件或触发器文件更新时:**
-
-1. **立即读取** `.cursor/task_execute_instructions.md` 文件
-2. **依次处理**文件中的每个任务
-3. **执行任务**:打开文件 → 实现功能 → 更新状态
-4. **完成后**继续处理下一个任务
-
-### 📝 手动触发命令
-
-如果自动检测未触发,用户可以在Cursor Chat中输入:
-```
-请检查并执行所有待处理任务
-```
-
-或者:
-```
-请读取 .cursor/task_execute_instructions.md 并执行其中的任务
-```
-
-或者运行脚本:
-```bash
-python scripts/trigger_cursor_execution.py --once
-```
-
-### 定期执行模式
-
-如果需要持续监控,可以在后台运行:
-```bash
-python scripts/trigger_cursor_execution.py --interval 300
-```
-
-这个脚本会:
-- 每5分钟检查一次processing任务
-- 自动更新 `task_execute_instructions.md` 文件
-- Cursor会自动检测文件变化并执行任务
-
-### 注意事项
-
-1. **自动执行模式**:
-   - Cursor会自动检测任务指令文件的变化
-   - 文件更新时间在5分钟内,会触发自动执行
-
-2. **任务状态**:
-   - 只处理 `processing` 状态的任务
-   - 任务完成后会自动更新为 `completed`
-
-3. **代码规范**:
-   - 确保生成的代码符合项目规范
-   - 添加适当的错误处理和日志记录
-   - 遵循项目的代码风格指南

+ 0 - 85
.cursor/task_execute_instructions.md

@@ -1,85 +0,0 @@
-# 🤖 Cursor 自动任务执行指令
-
-**⚠️ 重要:请立即执行以下任务!**
-
-**生成时间**: 2026-01-06 20:16:36
-
-**待执行任务数量**: 1
-
-## 📋 任务完成后的操作
-
-完成每个任务后,请更新 `.cursor/pending_tasks.json` 中对应任务的 `status` 为 `completed`,
-并填写 `code_name`(代码文件名)和 `code_path`(代码路径)。
-
-调度脚本会自动将完成的任务同步到数据库。
-
----
-
-## 🔴 任务 1: 导入原始数据到产品库存表
-
-- **任务ID**: `21`
-- **创建时间**: 2026-01-06 20:11:16
-- **创建者**: cursor
-
-### 📝 任务描述
-
-# Task: 导入原始数据到产品库存表
-
-## DataFlow Configuration
-- **Schema**: dags
-
-## Data Source
-- **Type**: RDBMS
-- **Host**: 192.168.3.143
-- **Port**: 5432
-- **Database**: dataops
-
-## Target Tables (DDL)
-```sql
-CREATE TABLE test_product_inventory (
-    updated_at timestamp COMMENT '更新时间',
-    created_at timestamp COMMENT '创建时间',
-    is_active boolean COMMENT '是否有效',
-    turnover_rate numeric(5, 2) COMMENT '周转率',
-    outbound_quantity_30d integer COMMENT '30天出库数量',
-    inbound_quantity_30d integer COMMENT '30天入库数量',
-    last_outbound_date date COMMENT '最近出库日期',
-    last_inbound_date date COMMENT '最近入库日期',
-    stock_status varchar(50) COMMENT '库存状态',
-    selling_price numeric(10, 2) COMMENT '销售价格',
-    unit_cost numeric(10, 2) COMMENT '单位成本',
-    max_stock integer COMMENT '最大库存',
-    safety_stock integer COMMENT '安全库存',
-    current_stock integer COMMENT '当前库存',
-    warehouse varchar(100) COMMENT '仓库',
-    supplier varchar(200) COMMENT '供应商',
-    brand varchar(100) COMMENT '品牌',
-    category varchar(100) COMMENT '类别',
-    sku varchar(50) COMMENT '商品货号',
-    id serial COMMENT '编号',
-    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '数据创建时间'
-);
-COMMENT ON TABLE test_product_inventory IS '产品库存表';
-```
-
-## Update Mode
-- **Mode**: Append (追加模式)
-- **Description**: 新数据将追加到目标表,不删除现有数据
-
-## Request Content
-从数据源导入数据到数据资源的产品库存表。 
-
-## Implementation Steps
-1. Create an n8n workflow to execute the data import task
-2. Configure the workflow to call `import_resource_data.py` Python script
-3. Pass the following parameters to the Python execution node:
-   - `--source-config`: JSON configuration for the remote data source
-   - `--target-table`: Target table name (data resource English name)
-   - `--update-mode`: append
-4. The Python script will automatically:
-   - Connect to the remote data source
-   - Extract data from the source table
-   - Write data to target table using append mode
-
----
-

+ 30 - 0
app/api/business_domain/routes.py

@@ -160,6 +160,16 @@ def bd_save():
             return jsonify(failed("新建时 name_zh 和 name_en 为必填项"))
 
         saved_data = save_business_domain(data)
+
+        # 检查是否是重复节点的情况
+        if isinstance(saved_data, dict) and saved_data.get("success") is False:
+            return jsonify(
+                failed(
+                    saved_data.get("message", "保存业务领域失败"),
+                    data=saved_data.get("existing_node"),
+                )
+            )
+
         return jsonify(success(saved_data))
     except Exception as e:
         logger.error(f"保存业务领域失败: {str(e)}")
@@ -175,6 +185,16 @@ def bd_update():
             return jsonify(failed("参数不完整"))
 
         updated_data = update_business_domain(data)
+
+        # 检查是否是重复节点的情况
+        if isinstance(updated_data, dict) and updated_data.get("success") is False:
+            return jsonify(
+                failed(
+                    updated_data.get("message", "更新业务领域失败"),
+                    data=updated_data.get("existing_node"),
+                )
+            )
+
         return jsonify(success(updated_data))
     except Exception as e:
         logger.error(f"更新业务领域失败: {str(e)}")
@@ -609,6 +629,16 @@ def bd_compose():
         data["meta_ids"] = meta_ids
 
         result_data = business_domain_compose(data)
+
+        # 检查是否是重复节点的情况
+        if isinstance(result_data, dict) and result_data.get("success") is False:
+            return jsonify(
+                failed(
+                    result_data.get("message", "组合创建业务领域失败"),
+                    data=result_data.get("existing_node"),
+                )
+            )
+
         response_data = {"business_domain": result_data}
         return jsonify(success(response_data))
     except Exception as e:

+ 12 - 5
app/api/meta_data/routes.py

@@ -77,9 +77,14 @@ def meta_node_list():
             except (TypeError, ValueError):
                 return default
 
-        # 分页参数
-        page = to_int(payload.get("current", 1), 1)
-        page_size = to_int(payload.get("size", 10), 10)
+        # 分页参数(size 未传则返回全部记录)
+        raw_page_size = payload.get("size", None)
+        if raw_page_size is None:
+            page_size = None
+            page = 1
+        else:
+            page_size = to_int(raw_page_size, 10)
+            page = to_int(payload.get("current", 1), 1)
 
         # 过滤参数
         name_en_filter = payload.get("name_en") or None
@@ -109,13 +114,15 @@ def meta_node_list():
         )
 
         # 返回结果
+        response_size = total_count if page_size is None else page_size
+        response_page = 1 if page_size is None else page
         return jsonify(
             success(
                 {
                     "records": result,
                     "total": total_count,
-                    "size": page_size,
-                    "current": page,
+                    "size": response_size,
+                    "current": response_page,
                 }
             )
         )

+ 82 - 0
app/core/business_domain/business_domain.py

@@ -834,6 +834,32 @@ def save_business_domain(data):
             raise ValueError("缺少必填字段: name_zh 或 name_en")
 
         with neo4j_driver.get_session() as session:
+            # 检查是否存在相同 name_en 的 BusinessDomain 节点
+            check_duplicate_cypher = """
+            MATCH (bd:BusinessDomain)
+            WHERE bd.name_en = $name_en
+            RETURN id(bd) as id, bd.name_zh as name_zh, bd.name_en as name_en
+            LIMIT 1
+            """
+            duplicate_result = session.run(
+                check_duplicate_cypher, {"name_en": name_en}
+            ).single()
+
+            if duplicate_result:
+                logger.warning(
+                    f"创建业务领域失败: 存在相同name_en的节点, "
+                    f"name_en={name_en}, existing_id={duplicate_result['id']}"
+                )
+                return {
+                    "success": False,
+                    "message": "当前业务域存在相同英文名的节点,请重新编辑命名中英文后再进行提交。",
+                    "existing_node": {
+                        "id": duplicate_result["id"],
+                        "name_zh": duplicate_result["name_zh"],
+                        "name_en": duplicate_result["name_en"],
+                    },
+                }
+
             # 构建节点属性
             node_props = {
                 "name_zh": name_zh,
@@ -1149,6 +1175,36 @@ def update_business_domain(data):
             raise ValueError(f"业务领域ID不是有效的整数: {domain_id}") from err
 
         with neo4j_driver.get_session() as session:
+            # 如果更新 name_en,检查是否与其他节点重复(排除自身)
+            new_name_en = data.get("name_en")
+            if new_name_en:
+                check_duplicate_cypher = """
+                MATCH (bd:BusinessDomain)
+                WHERE bd.name_en = $name_en AND id(bd) <> $domain_id
+                RETURN id(bd) as id, bd.name_zh as name_zh, bd.name_en as name_en
+                LIMIT 1
+                """
+                duplicate_result = session.run(
+                    check_duplicate_cypher,
+                    {"name_en": new_name_en, "domain_id": domain_id_int},
+                ).single()
+
+                if duplicate_result:
+                    logger.warning(
+                        f"更新业务领域失败: 存在相同name_en的其他节点, "
+                        f"name_en={new_name_en}, existing_id={duplicate_result['id']}, "
+                        f"current_id={domain_id_int}"
+                    )
+                    return {
+                        "success": False,
+                        "message": "当前业务域存在相同英文名的节点,请重新编辑命名中英文后再进行提交。",
+                        "existing_node": {
+                            "id": duplicate_result["id"],
+                            "name_zh": duplicate_result["name_zh"],
+                            "name_en": duplicate_result["name_en"],
+                        },
+                    }
+
             # 构建更新字段(过滤掉特殊字段和 None 值)
             # parsed_data 通过 INCLUDES 关系处理,不存储为节点属性
             update_fields = {}
@@ -1675,6 +1731,32 @@ def business_domain_compose(data):
             name_en = translated[0] if translated else name_zh
 
         with neo4j_driver.get_session() as session:
+            # 检查是否存在相同 name_en 的 BusinessDomain 节点
+            check_duplicate_cypher = """
+            MATCH (bd:BusinessDomain)
+            WHERE bd.name_en = $name_en
+            RETURN id(bd) as id, bd.name_zh as name_zh, bd.name_en as name_en
+            LIMIT 1
+            """
+            duplicate_result = session.run(
+                check_duplicate_cypher, {"name_en": name_en}
+            ).single()
+
+            if duplicate_result:
+                logger.warning(
+                    f"组合创建业务领域失败: 存在相同name_en的节点, "
+                    f"name_en={name_en}, existing_id={duplicate_result['id']}"
+                )
+                return {
+                    "success": False,
+                    "message": "当前业务域存在相同英文名的节点,请重新编辑命名中英文后再进行提交。",
+                    "existing_node": {
+                        "id": duplicate_result["id"],
+                        "name_zh": duplicate_result["name_zh"],
+                        "name_en": duplicate_result["name_en"],
+                    },
+                }
+
             # 构建节点属性
             node_props = {
                 "name_zh": name_zh,

+ 156 - 32
app/core/data_service/data_product_service.py

@@ -1459,11 +1459,27 @@ class DataOrderService:
     "tags": ["标签1", "标签2"]
 }}
 
-注意:
-1. business_domains 应该是可能存在的数据表或业务实体名称,如"人员信息"、"薪资数据"、"销售记录"等
-2. data_fields 应该是具体的数据字段名称,如"姓名"、"年龄"、"薪资"、"销售额"等
-3. purpose 简要描述数据的使用目的
-4. tags 是从需求描述中提取的标签信息,如"财务"、"销售"、"客户"、"订单"等,用于过滤匹配的业务领域。如果需求中没有明确提到标签,可以返回空数组 []
+提取规则:
+1. business_domains(业务领域):
+   - 提取可能存在的数据表或业务实体名称
+   - 例如:"人员信息"、"薪资数据"、"销售记录"、"产品库存汇总表"等
+
+2. data_fields(数据字段):
+   - 提取具体的数据字段名称
+   - 例如:"姓名"、"年龄"、"薪资"、"销售额"、"库存量"、"仓库名称"等
+
+3. purpose(数据用途):
+   - 简要描述数据的使用目的
+
+4. tags(标签):
+   - **重要**:只提取需求描述中明确使用"标签为xxx"、"标签是xxx"、"带有xxx标签"等表述中的标签名称
+   - 不要根据描述内容自行推断或提取主语作为标签
+   - 如果需求中没有明确提到"标签为xxx"的表述,必须返回空数组 []
+
+   示例:
+   - "从标签为数据模型的产品库存汇总表里提取库存量和仓库名称信息" → tags: ["数据模型"]
+   - "从标签为财务和销售的订单数据中查询金额" → tags: ["财务", "销售"]
+   - "从产品库存表里提取库存量和仓库名称信息" → tags: [](没有明确标签表述)
 """
 
             completion = client.chat.completions.create(
@@ -1805,7 +1821,12 @@ class DataOrderService:
         domain_ids: list[int],
     ) -> dict[str, Any]:
         """
-        分析多个 BusinessDomain 之间的连通性(通过共同的 DataMeta 字段)
+        分析多个 BusinessDomain 之间的连通性(通过共同的 DataMeta 字段或 ALIAS 关系)
+
+        连通性判断标准:
+        1. 两个 BusinessDomain 包含相同的 DataMeta(直接共享)
+        2. 两个 BusinessDomain 包含的 DataMeta 之间存在 ALIAS 关系(别名关联)
+        3. 上述 DataMeta 必须具有"键值"标签
 
         Args:
             domain_ids: BusinessDomain 节点 ID 列表
@@ -1826,39 +1847,139 @@ class DataOrderService:
 
             with neo4j_driver.get_session() as session:
                 # 查找多个 BusinessDomain 之间的共同 DataMeta 字段
+                # 条件1: 直接共享同一个 DataMeta,且该 DataMeta 有"键值"标签
+                # 条件2: 两个 DataMeta 之间有 ALIAS 关系,且至少一个有"键值"标签
                 cypher = """
+                // 场景1: 直接共享同一个 DataMeta(有"键值"标签)
                 MATCH (bd1:BusinessDomain)-[:INCLUDES]->(m:DataMeta)<-[:INCLUDES]-(bd2:BusinessDomain)
                 WHERE id(bd1) IN $domain_ids AND id(bd2) IN $domain_ids
                 AND id(bd1) < id(bd2)
-                RETURN id(bd1) as bd1_id, bd1.name_zh as bd1_name,
-                       id(bd2) as bd2_id, bd2.name_zh as bd2_name,
-                       collect({
-                           id: id(m),
-                           name_zh: m.name_zh,
-                           name_en: m.name_en
-                       }) as common_fields
+                // 检查 DataMeta 是否有"键值"标签
+                AND EXISTS {
+                    MATCH (m)-[:LABEL]->(label:DataLabel)
+                    WHERE label.name_zh = '键值'
+                }
+                WITH id(bd1) as bd1_id, bd1.name_zh as bd1_name,
+                     id(bd2) as bd2_id, bd2.name_zh as bd2_name,
+                     collect(DISTINCT {
+                         id: id(m),
+                         name_zh: m.name_zh,
+                         name_en: m.name_en,
+                         connection_type: 'direct'
+                     }) as direct_fields
+
+                RETURN bd1_id, bd1_name, bd2_id, bd2_name, direct_fields as common_fields
+
+                UNION
+
+                // 场景2: 通过 ALIAS 关系关联的 DataMeta(有"键值"标签)
+                // 情况2a: m1 是 m2 的别名 (m1)-[:ALIAS]->(m2)
+                MATCH (bd1:BusinessDomain)-[:INCLUDES]->(m1:DataMeta)-[:ALIAS]->(m2:DataMeta)<-[:INCLUDES]-(bd2:BusinessDomain)
+                WHERE id(bd1) IN $domain_ids AND id(bd2) IN $domain_ids
+                AND id(bd1) <> id(bd2)
+                // 检查 m1 或 m2 是否有"键值"标签
+                AND (
+                    EXISTS {
+                        MATCH (m1)-[:LABEL]->(label:DataLabel)
+                        WHERE label.name_zh = '键值'
+                    }
+                    OR EXISTS {
+                        MATCH (m2)-[:LABEL]->(label:DataLabel)
+                        WHERE label.name_zh = '键值'
+                    }
+                )
+                WITH CASE WHEN id(bd1) < id(bd2) THEN id(bd1) ELSE id(bd2) END as bd1_id,
+                     CASE WHEN id(bd1) < id(bd2) THEN bd1.name_zh ELSE bd2.name_zh END as bd1_name,
+                     CASE WHEN id(bd1) < id(bd2) THEN id(bd2) ELSE id(bd1) END as bd2_id,
+                     CASE WHEN id(bd1) < id(bd2) THEN bd2.name_zh ELSE bd1.name_zh END as bd2_name,
+                     m1, m2
+                WITH bd1_id, bd1_name, bd2_id, bd2_name,
+                     collect(DISTINCT {
+                         id: id(m1),
+                         name_zh: m1.name_zh,
+                         name_en: m1.name_en,
+                         alias_id: id(m2),
+                         alias_name_zh: m2.name_zh,
+                         alias_name_en: m2.name_en,
+                         connection_type: 'alias'
+                     }) as alias_fields
+
+                RETURN bd1_id, bd1_name, bd2_id, bd2_name, alias_fields as common_fields
+
+                UNION
+
+                // 情况2b: m1 和 m2 共享同一个主元数据(都是别名指向同一个 primary)
+                MATCH (bd1:BusinessDomain)-[:INCLUDES]->(m1:DataMeta)-[:ALIAS]->(primary:DataMeta)<-[:ALIAS]-(m2:DataMeta)<-[:INCLUDES]-(bd2:BusinessDomain)
+                WHERE id(bd1) IN $domain_ids AND id(bd2) IN $domain_ids
+                AND id(bd1) < id(bd2)
+                AND id(m1) <> id(m2)
+                // 检查 m1、m2 或 primary 是否有"键值"标签
+                AND (
+                    EXISTS {
+                        MATCH (m1)-[:LABEL]->(label:DataLabel)
+                        WHERE label.name_zh = '键值'
+                    }
+                    OR EXISTS {
+                        MATCH (m2)-[:LABEL]->(label:DataLabel)
+                        WHERE label.name_zh = '键值'
+                    }
+                    OR EXISTS {
+                        MATCH (primary)-[:LABEL]->(label:DataLabel)
+                        WHERE label.name_zh = '键值'
+                    }
+                )
+                WITH id(bd1) as bd1_id, bd1.name_zh as bd1_name,
+                     id(bd2) as bd2_id, bd2.name_zh as bd2_name,
+                     collect(DISTINCT {
+                         id: id(m1),
+                         name_zh: m1.name_zh,
+                         name_en: m1.name_en,
+                         alias_id: id(m2),
+                         alias_name_zh: m2.name_zh,
+                         alias_name_en: m2.name_en,
+                         primary_id: id(primary),
+                         primary_name_zh: primary.name_zh,
+                         connection_type: 'shared_primary'
+                     }) as shared_primary_fields
+
+                RETURN bd1_id, bd1_name, bd2_id, bd2_name, shared_primary_fields as common_fields
                 """
                 result = session.run(cypher, {"domain_ids": domain_ids})
 
-                connection_pairs = []
-                all_common_fields = []
+                # 使用字典合并相同 domain pair 的结果
+                pair_dict: dict[tuple[int, int], dict[str, Any]] = {}
 
                 for record in result:
-                    pair = {
-                        "domain1": {
-                            "id": record["bd1_id"],
-                            "name": record["bd1_name"],
-                        },
-                        "domain2": {
-                            "id": record["bd2_id"],
-                            "name": record["bd2_name"],
-                        },
-                        "common_fields": record["common_fields"],
-                    }
-                    connection_pairs.append(pair)
-                    all_common_fields.extend(record["common_fields"])
+                    bd1_id = record["bd1_id"]
+                    bd2_id = record["bd2_id"]
+                    pair_key = (bd1_id, bd2_id)
+
+                    if pair_key not in pair_dict:
+                        pair_dict[pair_key] = {
+                            "domain1": {
+                                "id": bd1_id,
+                                "name": record["bd1_name"],
+                            },
+                            "domain2": {
+                                "id": bd2_id,
+                                "name": record["bd2_name"],
+                            },
+                            "common_fields": [],
+                        }
+
+                    # 合并 common_fields
+                    pair_dict[pair_key]["common_fields"].extend(
+                        record["common_fields"]
+                    )
+
+                connection_pairs = list(pair_dict.values())
 
-                # 去重共同字段
+                # 收集所有共同字段并去重
+                all_common_fields = []
+                for pair in connection_pairs:
+                    all_common_fields.extend(pair["common_fields"])
+
+                # 去重共同字段(基于 id)
                 seen_ids = set()
                 unique_fields = []
                 for field in all_common_fields:
@@ -1883,12 +2004,15 @@ class DataOrderService:
                     "total_domain_count": len(domain_ids),
                     "common_fields": unique_fields,
                     "connection_pairs": connection_pairs,
-                    "reason": "找到可用于 JOIN 的共同字段"
+                    "reason": "找到可用于 JOIN 的共同键值字段"
                     if can_connect
-                    else "未找到可用于 JOIN 的共同字段",
+                    else "未找到可用于 JOIN 的共同键值字段(需要具有'键值'标签的共同或别名关联字段)",
                 }
 
-                logger.info(f"图谱连通性分析完成: can_connect={can_connect}")
+                logger.info(
+                    f"图谱连通性分析完成: can_connect={can_connect}, "
+                    f"pairs={len(connection_pairs)}, fields={len(unique_fields)}"
+                )
                 return analysis_result
 
         except Exception as e:

+ 8 - 6
app/core/meta_data/meta_data.py

@@ -332,10 +332,12 @@ def meta_list(
             count_record = count_result.single()
             total_count = count_record["count"] if count_record else 0
 
-            # 分页查询
-            skip = (page - 1) * page_size
-            params["skip"] = skip
-            params["limit"] = page_size
+            # 分页查询(page_size 为空时返回全部记录)
+            use_pagination = page_size is not None and page_size > 0
+            if use_pagination:
+                skip = (page - 1) * page_size
+                params["skip"] = skip
+                params["limit"] = page_size
             if tag_where_clause:
                 # 有 tag 过滤时,先匹配符合条件的节点
                 cypher = f"""
@@ -347,7 +349,7 @@ def meta_list(
                 {optional_match}
                 RETURN n, collect(DISTINCT t) as tags
                 ORDER BY n.name_zh
-                SKIP $skip LIMIT $limit
+                {"SKIP $skip LIMIT $limit" if use_pagination else ""}
                 """
             else:
                 cypher = f"""
@@ -356,7 +358,7 @@ def meta_list(
                 {optional_match}
                 RETURN n, collect(DISTINCT t) as tags
                 ORDER BY n.name_zh
-                SKIP $skip LIMIT $limit
+                {"SKIP $skip LIMIT $limit" if use_pagination else ""}
                 """
             result = session.run(cypher, **params)
 

+ 0 - 29
app/scripts/create_user_table.sql

@@ -1,29 +0,0 @@
--- 创建用户表SQL脚本
--- 用于在PostgreSQL数据库中创建用户表和相关索引
-
--- 创建用户表
-CREATE TABLE IF NOT EXISTS users (
-    id VARCHAR(100) PRIMARY KEY,
-    username VARCHAR(50) UNIQUE NOT NULL,
-    password VARCHAR(100) NOT NULL,
-    created_at FLOAT NOT NULL,
-    last_login FLOAT,
-    is_admin BOOLEAN DEFAULT FALSE
-);
-
--- 创建索引以加快查询速度
-CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
-
--- 创建默认管理员用户(密码为base64编码的'admin123')
-INSERT INTO users (id, username, password, created_at, is_admin)
-VALUES (
-    'admin-default-id',
-    'admin',
-    'YWRtaW4xMjM=',
-    extract(epoch from now()),
-    TRUE
-)
-ON CONFLICT (username) DO NOTHING;
-
--- 验证是否创建成功
-SELECT username, id, created_at, is_admin FROM users; 

+ 327 - 0
datafactory/datameta-agent-workflow.json

@@ -0,0 +1,327 @@
+{
+  "name": "DataMeta管理智能体",
+  "nodes": [
+    {
+      "name": "Chat Trigger",
+      "webhookId": "datameta-agent",
+      "id": "chat_trigger",
+      "typeVersion": 1.1,
+      "position": [0, 300],
+      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
+      "parameters": {
+        "public": true,
+        "mode": "hostedChat",
+        "options": {
+          "title": "DataMeta管理助手",
+          "subtitle": "我可以帮您管理元数据,支持查询、新增、更新、删除和标签管理",
+          "inputPlaceholder": "请输入您的需求,例如:查看所有元数据"
+        }
+      }
+    },
+    {
+      "name": "AI Agent",
+      "id": "ai_agent",
+      "typeVersion": 1.7,
+      "position": [300, 300],
+      "type": "@n8n/n8n-nodes-langchain.agent",
+      "parameters": {
+        "options": {
+          "systemMessage": "你是一个元数据(DataMeta)管理助手。你可以帮助用户执行以下操作:\n\n1. **查询元数据列表** - 使用 list_datameta 工具\n2. **获取元数据详情** - 使用 get_datameta 工具\n3. **新增元数据** - 使用 add_datameta 工具\n4. **更新元数据** - 使用 update_datameta 工具\n5. **删除元数据** - 使用 delete_datameta 工具\n6. **查看可用标签** - 使用 list_labels 工具\n\n请根据用户的需求选择合适的工具来执行操作。\n\n重要提示:\n- 新增元数据时,name_zh(中文名)和 data_type(数据类型)是必填字段\n- 更新和删除操作需要提供元数据的 id\n- 设置标签时,需要先获取标签列表,然后在新增或更新时通过 tag 字段传入标签ID数组\n\n请用中文回复用户。",
+          "maxIterations": 25
+        }
+      }
+    },
+    {
+      "name": "DeepSeek Chat Model",
+      "id": "deepseek_model",
+      "typeVersion": 1,
+      "position": [300, 500],
+      "type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
+      "parameters": {
+        "model": "deepseek-chat",
+        "options": {
+          "temperature": 0.7
+        }
+      },
+      "credentials": {
+        "deepSeekApi": {
+          "id": "DeepSeek account",
+          "name": "DeepSeek account"
+        }
+      }
+    },
+    {
+      "name": "list_datameta",
+      "id": "tool_list_datameta",
+      "typeVersion": 1.1,
+      "position": [500, 100],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/meta/node/list",
+        "specifyBody": "json",
+        "sendBody": true,
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "jsonBody": "={{ JSON.stringify({ current: $fromAI('current', '页码', 'number') || 1, name_zh: $fromAI('name_zh', '中文名筛选', 'string') || '', name_en: $fromAI('name_en', '英文名筛选', 'string') || '', category: $fromAI('category', '分类筛选', 'string') || '' }) }}",
+        "authentication": "none",
+        "toolDescription": "查询元数据列表(默认返回全部记录)。可选参数:current(页码,默认1), name_zh(中文名筛选), name_en(英文名筛选), category(分类筛选), tag(标签筛选数组)",
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    },
+    {
+      "name": "get_datameta",
+      "id": "tool_get_datameta",
+      "typeVersion": 1.1,
+      "position": [500, 200],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/meta/node/edit",
+        "specifyBody": "json",
+        "sendBody": true,
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "jsonBody": "={{ JSON.stringify({ id: $fromAI('id', '元数据ID', 'number') }) }}",
+        "authentication": "none",
+        "toolDescription": "根据ID获取元数据详情。必填参数:id(元数据ID,数字类型)",
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    },
+    {
+      "name": "add_datameta",
+      "id": "tool_add_datameta",
+      "typeVersion": 1.1,
+      "position": [500, 300],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/meta/node/add",
+        "sendBody": true,
+        "specifyBody": "keypair",
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "authentication": "none",
+        "toolDescription": "Add new metadata to the system. Call this tool when user wants to create/add a new metadata. The AI must provide name_zh (Chinese name) and data_type (like varchar, integer, string) parameters.",
+        "parametersBody": {
+          "values": [
+            {
+              "name": "name_zh"
+            },
+            {
+              "name": "data_type"
+            }
+          ]
+        },
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    },
+    {
+      "name": "update_datameta",
+      "id": "tool_update_datameta",
+      "typeVersion": 1.1,
+      "position": [500, 400],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/meta/node/update",
+        "sendBody": true,
+        "specifyBody": "keypair",
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "authentication": "none",
+        "toolDescription": "Update existing metadata. Required: id (metadata ID). Optional: data_type (new data type like varchar, string, integer).",
+        "parametersBody": {
+          "values": [
+            {
+              "name": "id"
+            },
+            {
+              "name": "data_type"
+            }
+          ]
+        },
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    },
+    {
+      "name": "delete_datameta",
+      "id": "tool_delete_datameta",
+      "typeVersion": 1.1,
+      "position": [500, 500],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/meta/node/delete",
+        "specifyBody": "json",
+        "sendBody": true,
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "jsonBody": "={{ JSON.stringify({ id: $fromAI('id', '元数据ID', 'number') }) }}",
+        "authentication": "none",
+        "toolDescription": "删除元数据。必填参数:id(要删除的元数据ID,数字类型)",
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    },
+    {
+      "name": "list_labels",
+      "id": "tool_list_labels",
+      "typeVersion": 1.1,
+      "position": [500, 600],
+      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
+      "parameters": {
+        "method": "POST",
+        "url": "https://company.citupro.com:18183/api/interface/labellist",
+        "specifyBody": "json",
+        "sendBody": true,
+        "sendHeaders": true,
+        "specifyHeaders": "keypair",
+        "jsonBody": "={{ JSON.stringify({ current: $fromAI('current', '页码', 'number') || 1, size: $fromAI('size', '每页数量', 'number') || 10, name_zh: $fromAI('name_zh', '中文名筛选', 'string') || '', name_en: $fromAI('name_en', '英文名筛选', 'string') || '' }) }}",
+        "authentication": "none",
+        "toolDescription": "查询可用的数据标签列表。可选参数:current(页码,默认1), size(每页数量,默认10), name_zh(中文名筛选), name_en(英文名筛选), category(分类筛选), group(分组筛选)",
+        "parametersHeaders": {
+          "values": [
+            {
+              "name": "Content-Type",
+              "valueProvider": "fieldValue",
+              "value": "application/json"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "connections": {
+    "Chat Trigger": {
+      "main": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "main",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "list_datameta": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "delete_datameta": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "get_datameta": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "update_datameta": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "DeepSeek Chat Model": {
+      "ai_languageModel": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_languageModel",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "add_datameta": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    },
+    "list_labels": {
+      "ai_tool": [
+        [
+          {
+            "node": "AI Agent",
+            "type": "ai_tool",
+            "index": 0
+          }
+        ]
+      ]
+    }
+  },
+  "settings": {
+    "executionOrder": "v1"
+  }
+}

+ 0 - 462
datafactory/scripts/task_43_产品库存表的原始数据导入.py

@@ -1,462 +0,0 @@
-#!/usr/bin/env python
-"""
-数据流任务脚本 - 产品库存表的原始数据导入
-
-任务ID: 43
-任务名称: 产品库存表的原始数据导入
-创建时间: 2026-01-21
-更新模式: Append (追加模式)
-
-描述: 从源表 public.product_inventory_table_raw_data 导入数据到目标表 dags.test_product_inventory
-"""
-
-from __future__ import annotations
-
-import os
-import sys
-from datetime import datetime
-from typing import Any
-
-import pandas as pd
-import psycopg2
-from loguru import logger
-
-# 添加项目根目录到Python路径
-PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-sys.path.insert(0, PROJECT_ROOT)
-
-# 任务配置
-TASK_ID = 43
-TASK_NAME = "产品库存表的原始数据导入"
-UPDATE_MODE = "append"
-
-# 源数据库配置
-SOURCE_CONFIG = {
-    "host": "192.168.3.143",
-    "port": 5432,
-    "database": "dataops",
-    "user": "postgres",
-    "password": "dataOps",
-}
-
-# 目标数据库配置
-TARGET_CONFIG = {
-    "host": "192.168.3.143",
-    "port": 5432,  # PostgreSQL 默认端口
-    "database": "dataops",
-    "user": "postgres",
-    "password": "dataOps",
-}
-
-# 目标表配置
-TARGET_SCHEMA = "dags"
-TARGET_TABLE = "test_product_inventory"
-
-
-def get_source_connection() -> psycopg2.extensions.connection:
-    """
-    获取源数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    conn = psycopg2.connect(
-        host=SOURCE_CONFIG["host"],
-        port=SOURCE_CONFIG["port"],
-        database=SOURCE_CONFIG["database"],
-        user=SOURCE_CONFIG["user"],
-        password=SOURCE_CONFIG["password"],
-    )
-    logger.info("源数据库连接成功")
-    return conn
-
-
-def get_target_connection() -> psycopg2.extensions.connection:
-    """
-    获取目标数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    conn = psycopg2.connect(
-        host=TARGET_CONFIG["host"],
-        port=TARGET_CONFIG["port"],
-        database=TARGET_CONFIG["database"],
-        user=TARGET_CONFIG["user"],
-        password=TARGET_CONFIG["password"],
-        options=f"-c search_path={TARGET_SCHEMA},public",
-    )
-    logger.info("目标数据库连接成功")
-    return conn
-
-
-def ensure_target_table_exists(conn: psycopg2.extensions.connection) -> None:
-    """
-    确保目标表存在,如果不存在则创建
-
-    Args:
-        conn: 目标数据库连接
-    """
-    cursor = conn.cursor()
-
-    try:
-        # 检查表是否存在
-        cursor.execute(
-            """
-            SELECT EXISTS(
-                SELECT 1 FROM information_schema.tables
-                WHERE table_schema = %s
-                AND table_name = %s
-            )
-        """,
-            (TARGET_SCHEMA, TARGET_TABLE),
-        )
-        result = cursor.fetchone()
-        exists = result[0] if result else False
-
-        if not exists:
-            logger.info(f"目标表不存在,正在创建 {TARGET_SCHEMA}.{TARGET_TABLE}...")
-
-            # 根据任务描述中的 DDL 创建表
-            create_table_sql = f"""
-            CREATE TABLE IF NOT EXISTS {TARGET_SCHEMA}.{TARGET_TABLE} (
-                id SERIAL,
-                sku VARCHAR(50),
-                product_name VARCHAR(200),
-                category VARCHAR(100),
-                brand VARCHAR(100),
-                supplier VARCHAR(200),
-                warehouse VARCHAR(100),
-                current_stock INTEGER,
-                safety_stock INTEGER,
-                max_stock INTEGER,
-                unit_cost NUMERIC(10, 2),
-                selling_price NUMERIC(10, 2),
-                stock_status VARCHAR(50),
-                last_inbound_date DATE,
-                last_outbound_date DATE,
-                inbound_quantity_30d INTEGER,
-                outbound_quantity_30d INTEGER,
-                turnover_rate NUMERIC(5, 2),
-                is_active BOOLEAN,
-                created_at TIMESTAMP,
-                updated_at TIMESTAMP,
-                create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-            );
-            """
-            cursor.execute(create_table_sql)
-
-            # 添加表注释
-            cursor.execute(
-                f"COMMENT ON TABLE {TARGET_SCHEMA}.{TARGET_TABLE} IS '产品库存表'"
-            )
-
-            # 添加列注释
-            column_comments = {
-                "id": "ID",
-                "sku": "SKU",
-                "product_name": "产品名称",
-                "category": "类别",
-                "brand": "品牌",
-                "supplier": "供应商",
-                "warehouse": "仓库",
-                "current_stock": "当前库存",
-                "safety_stock": "安全库存",
-                "max_stock": "最大库存",
-                "unit_cost": "单位成本",
-                "selling_price": "销售价格",
-                "stock_status": "库存状态",
-                "last_inbound_date": "最近入库日期",
-                "last_outbound_date": "最近出库日期",
-                "inbound_quantity_30d": "30天入库数量",
-                "outbound_quantity_30d": "30天出库数量",
-                "turnover_rate": "周转率",
-                "is_active": "是否启用",
-                "created_at": "创建时间",
-                "updated_at": "更新时间",
-                "create_time": "数据创建时间",
-            }
-            for col, comment in column_comments.items():
-                try:
-                    cursor.execute(
-                        f"COMMENT ON COLUMN {TARGET_SCHEMA}.{TARGET_TABLE}.{col} IS %s",
-                        (comment,),
-                    )
-                except Exception as e:
-                    logger.warning(f"添加列注释失败 {col}: {e}")
-
-            conn.commit()
-            logger.info(f"目标表 {TARGET_SCHEMA}.{TARGET_TABLE} 创建成功")
-        else:
-            logger.info(f"目标表 {TARGET_SCHEMA}.{TARGET_TABLE} 已存在")
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"创建目标表失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def extract_source_data(conn: psycopg2.extensions.connection) -> pd.DataFrame:
-    """
-    从源表提取数据
-
-    Args:
-        conn: 源数据库连接
-
-    Returns:
-        包含源数据的DataFrame
-    """
-    query = """
-    SELECT
-        id,
-        sku,
-        product_name,
-        category,
-        brand,
-        supplier,
-        warehouse,
-        current_stock,
-        safety_stock,
-        max_stock,
-        unit_cost,
-        selling_price,
-        stock_status,
-        last_inbound_date,
-        last_outbound_date,
-        inbound_quantity_30d,
-        outbound_quantity_30d,
-        turnover_rate,
-        is_active,
-        created_at,
-        updated_at
-    FROM public.product_inventory_table_raw_data
-    """
-
-    logger.info("正在从源表提取数据...")
-
-    try:
-        df = pd.read_sql(query, conn)
-        logger.info(f"成功提取 {len(df)} 条记录")
-        return df
-    except Exception as e:
-        logger.error(f"提取源数据失败: {e}")
-        raise
-
-
-def transform_data(df: pd.DataFrame) -> pd.DataFrame:
-    """
-    数据转换处理
-
-    Args:
-        df: 源数据DataFrame
-
-    Returns:
-        转换后的DataFrame
-    """
-    logger.info("正在执行数据转换...")
-
-    # 本任务为简单导入,无需复杂转换
-    # 源表和目标表字段基本一致,直接映射
-
-    logger.info(f"数据转换完成,共 {len(df)} 条记录")
-    return df
-
-
-def load_to_target(
-    df: pd.DataFrame,
-    conn: psycopg2.extensions.connection,
-    update_mode: str = "append",
-    batch_size: int = 1000,
-) -> int:
-    """
-    将数据加载到目标表
-
-    Args:
-        df: 要加载的DataFrame
-        conn: 目标数据库连接
-        update_mode: 更新模式(append 或 full)
-        batch_size: 批量插入大小
-
-    Returns:
-        插入的记录数
-    """
-    if df.empty:
-        logger.warning("没有数据需要加载")
-        return 0
-
-    logger.info(f"正在将 {len(df)} 条记录加载到目标表...")
-
-    target_table = f"{TARGET_SCHEMA}.{TARGET_TABLE}"
-    cursor = conn.cursor()
-    inserted_count = 0
-
-    try:
-        # 全量更新模式:先清空目标表
-        if update_mode.lower() == "full":
-            logger.info("全量更新模式:清空目标表...")
-            cursor.execute(f"TRUNCATE TABLE {target_table}")
-            logger.info("目标表已清空")
-
-        # 目标表结构准备插入的列(不包含 create_time,由数据库自动设置)
-        columns = [
-            "sku",
-            "product_name",
-            "category",
-            "brand",
-            "supplier",
-            "warehouse",
-            "current_stock",
-            "safety_stock",
-            "max_stock",
-            "unit_cost",
-            "selling_price",
-            "stock_status",
-            "last_inbound_date",
-            "last_outbound_date",
-            "inbound_quantity_30d",
-            "outbound_quantity_30d",
-            "turnover_rate",
-            "is_active",
-            "created_at",
-            "updated_at",
-        ]
-
-        # 构建插入SQL
-        placeholders = ", ".join(["%s"] * len(columns))
-        column_names = ", ".join(columns)
-        insert_sql = (
-            f"INSERT INTO {target_table} ({column_names}) VALUES ({placeholders})"
-        )
-
-        # 批量插入
-        for i in range(0, len(df), batch_size):
-            batch_df = df.iloc[i : i + batch_size]
-            records = []
-            for _, row in batch_df.iterrows():
-                record = tuple(
-                    None if pd.isna(row.get(col)) else row.get(col) for col in columns
-                )
-                records.append(record)
-
-            cursor.executemany(insert_sql, records)
-            inserted_count += len(records)
-            logger.debug(f"已插入 {inserted_count}/{len(df)} 条记录")
-
-        conn.commit()
-        logger.info(f"成功加载 {inserted_count} 条记录到 {target_table}")
-        return inserted_count
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"数据加载失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def main() -> dict[str, Any]:
-    """
-    主函数:执行ETL流程
-
-    Returns:
-        执行结果字典
-    """
-    result = {
-        "task_id": TASK_ID,
-        "task_name": TASK_NAME,
-        "status": "failed",
-        "records_extracted": 0,
-        "records_loaded": 0,
-        "error_message": None,
-        "execution_time": None,
-    }
-
-    start_time = datetime.now()
-    source_conn = None
-    target_conn = None
-
-    try:
-        logger.info("=" * 60)
-        logger.info(f"任务开始: {TASK_NAME}")
-        logger.info("=" * 60)
-
-        # 步骤1: 建立数据库连接
-        logger.info("[Step 1/5] 建立数据库连接...")
-        source_conn = get_source_connection()
-        target_conn = get_target_connection()
-
-        # 步骤2: 确保目标表存在(重要:必须在数据加载前执行)
-        logger.info("[Step 2/5] 检查/创建目标表...")
-        ensure_target_table_exists(target_conn)
-
-        # 步骤3: 从源表提取数据
-        logger.info("[Step 3/5] 提取源数据...")
-        df = extract_source_data(source_conn)
-        result["records_extracted"] = len(df)
-
-        # 步骤4: 数据转换
-        logger.info("[Step 4/5] 数据转换...")
-        df_transformed = transform_data(df)
-
-        # 步骤5: 加载到目标表
-        logger.info("[Step 5/5] 加载数据到目标表...")
-        records_loaded = load_to_target(
-            df_transformed, target_conn, update_mode=UPDATE_MODE
-        )
-        result["records_loaded"] = records_loaded
-
-        result["status"] = "success"
-        logger.info("=" * 60)
-        logger.info(
-            f"任务完成! 提取: {result['records_extracted']}, 加载: {result['records_loaded']}"
-        )
-        logger.info("=" * 60)
-
-    except Exception as e:
-        result["status"] = "failed"
-        result["error_message"] = str(e)
-        logger.error(f"任务执行失败: {e}")
-        raise
-
-    finally:
-        # 关闭数据库连接
-        if source_conn:
-            source_conn.close()
-            logger.debug("源数据库连接已关闭")
-        if target_conn:
-            target_conn.close()
-            logger.debug("目标数据库连接已关闭")
-
-        result["execution_time"] = str(datetime.now() - start_time)
-
-    return result
-
-
-if __name__ == "__main__":
-    # 配置日志
-    # 重要:日志输出到 stdout 而非 stderr,以便 n8n 工作流正确解析输出
-    logger.remove()
-    logger.add(
-        sys.stdout,
-        level="INFO",
-        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
-    )
-    logger.add(
-        os.path.join(PROJECT_ROOT, "logs", f"task_{TASK_ID}.log"),
-        level="DEBUG",
-        rotation="10 MB",
-        retention="7 days",
-        encoding="utf-8",
-    )
-
-    try:
-        result = main()
-        if result["status"] == "success":
-            sys.exit(0)
-        else:
-            sys.exit(1)
-    except Exception as e:
-        logger.exception(f"脚本执行异常: {e}")
-        sys.exit(1)

+ 0 - 415
datafactory/scripts/task_44_DF_DO202601210001.py

@@ -1,415 +0,0 @@
-#!/usr/bin/env python
-"""
-数据流任务脚本 - 仓库库存汇总表_数据流程
-
-任务ID: 44
-任务名称: DF_DO202601210001
-创建时间: 2026-01-21
-更新模式: Append (追加模式)
-
-关联信息:
-- Order ID: 26
-- Order No: DO202601210001
-- DataFlow ID: 2291
-- DataFlow Name: 仓库库存汇总表_数据流程
-- Product ID: 23
-
-描述: 从源表 test_product_inventory 提取仓库名称和库存数量,
-按仓库名称分组求和,输出到 warehouse_inventory_summary 表
-"""
-
-from __future__ import annotations
-
-import os
-import sys
-from datetime import datetime
-from typing import Any
-
-import pandas as pd
-import psycopg2
-from loguru import logger
-
-# 添加项目根目录到Python路径
-PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-sys.path.insert(0, PROJECT_ROOT)
-
-# 任务配置
-TASK_ID = 44
-TASK_NAME = "DF_DO202601210001"
-UPDATE_MODE = "append"
-
-# 源数据库配置(与目标相同)
-SOURCE_CONFIG = {
-    "host": "192.168.3.143",
-    "port": 5432,  # PostgreSQL 默认端口
-    "database": "dataops",
-    "user": "postgres",
-    "password": "dataOps",
-}
-
-# 目标数据库配置
-TARGET_CONFIG = {
-    "host": "192.168.3.143",
-    "port": 5432,  # PostgreSQL 默认端口
-    "database": "dataops",
-    "user": "postgres",
-    "password": "dataOps",
-}
-
-# 源表配置
-SOURCE_SCHEMA = "dags"
-SOURCE_TABLE = "test_product_inventory"
-
-# 目标表配置
-TARGET_SCHEMA = "dags"
-TARGET_TABLE = "warehouse_inventory_summary"
-
-
-def get_source_connection() -> psycopg2.extensions.connection:
-    """
-    获取源数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    conn = psycopg2.connect(
-        host=SOURCE_CONFIG["host"],
-        port=SOURCE_CONFIG["port"],
-        database=SOURCE_CONFIG["database"],
-        user=SOURCE_CONFIG["user"],
-        password=SOURCE_CONFIG["password"],
-        options=f"-c search_path={SOURCE_SCHEMA},public",
-    )
-    logger.info("源数据库连接成功")
-    return conn
-
-
-def get_target_connection() -> psycopg2.extensions.connection:
-    """
-    获取目标数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    conn = psycopg2.connect(
-        host=TARGET_CONFIG["host"],
-        port=TARGET_CONFIG["port"],
-        database=TARGET_CONFIG["database"],
-        user=TARGET_CONFIG["user"],
-        password=TARGET_CONFIG["password"],
-        options=f"-c search_path={TARGET_SCHEMA},public",
-    )
-    logger.info("目标数据库连接成功")
-    return conn
-
-
-def ensure_target_table_exists(conn: psycopg2.extensions.connection) -> None:
-    """
-    确保目标表存在,如果不存在则创建
-
-    Args:
-        conn: 目标数据库连接
-    """
-    cursor = conn.cursor()
-
-    try:
-        # 检查表是否存在
-        cursor.execute(
-            """
-            SELECT EXISTS(
-                SELECT 1 FROM information_schema.tables
-                WHERE table_schema = %s
-                AND table_name = %s
-            )
-        """,
-            (TARGET_SCHEMA, TARGET_TABLE),
-        )
-        result = cursor.fetchone()
-        exists = result[0] if result else False
-
-        if not exists:
-            logger.info(f"目标表不存在,正在创建 {TARGET_SCHEMA}.{TARGET_TABLE}...")
-
-            # 根据任务描述中的 DDL 创建表
-            create_table_sql = f"""
-            CREATE TABLE IF NOT EXISTS {TARGET_SCHEMA}.{TARGET_TABLE} (
-                warehouse_name VARCHAR(255),
-                total_inventory INTEGER,
-                create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-            );
-            """
-            cursor.execute(create_table_sql)
-
-            # 添加表注释
-            cursor.execute(
-                f"COMMENT ON TABLE {TARGET_SCHEMA}.{TARGET_TABLE} IS '仓库库存汇总表'"
-            )
-
-            # 添加列注释
-            column_comments = {
-                "warehouse_name": "仓库名称",
-                "total_inventory": "库存数量",
-                "create_time": "数据创建时间",
-            }
-            for col, comment in column_comments.items():
-                try:
-                    cursor.execute(
-                        f"COMMENT ON COLUMN {TARGET_SCHEMA}.{TARGET_TABLE}.{col} IS %s",
-                        (comment,),
-                    )
-                except Exception as e:
-                    logger.warning(f"添加列注释失败 {col}: {e}")
-
-            conn.commit()
-            logger.info(f"目标表 {TARGET_SCHEMA}.{TARGET_TABLE} 创建成功")
-        else:
-            logger.info(f"目标表 {TARGET_SCHEMA}.{TARGET_TABLE} 已存在")
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"创建目标表失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def extract_source_data(conn: psycopg2.extensions.connection) -> pd.DataFrame:
-    """
-    从源表提取数据
-
-    根据任务描述:
-    1. 从源数据'产品库存表'中提取'仓库名称'字段;
-    2. 对'产品库存表'中的'库存数量'字段进行求和计算;
-    3. 按'仓库名称'进行分组;
-    4. 最终输出数据格式包含'仓库名称'和对应的'库存数量'两个字段。
-
-    Args:
-        conn: 源数据库连接
-
-    Returns:
-        包含汇总数据的DataFrame
-    """
-    query = f"""
-    SELECT
-        warehouse AS warehouse_name,
-        SUM(current_stock) AS total_inventory
-    FROM {SOURCE_SCHEMA}.{SOURCE_TABLE}
-    WHERE warehouse IS NOT NULL
-    GROUP BY warehouse
-    ORDER BY warehouse
-    """
-
-    logger.info("正在从源表提取并汇总数据...")
-
-    try:
-        df = pd.read_sql(query, conn)
-        logger.info(f"成功提取 {len(df)} 条汇总记录")
-        return df
-    except Exception as e:
-        logger.error(f"提取源数据失败: {e}")
-        raise
-
-
-def transform_data(df: pd.DataFrame) -> pd.DataFrame:
-    """
-    数据转换处理
-
-    Args:
-        df: 源数据DataFrame
-
-    Returns:
-        转换后的DataFrame
-    """
-    logger.info("正在执行数据转换...")
-
-    # 数据已在SQL中完成汇总,此处仅做数据清洗
-    # 确保字段名称与目标表一致
-    df = df.rename(
-        columns={
-            "warehouse_name": "warehouse_name",
-            "total_inventory": "total_inventory",
-        }
-    )
-
-    # 处理空值:将 None/NaN 的库存数量设为 0
-    df["total_inventory"] = df["total_inventory"].fillna(0).astype(int)
-
-    logger.info(f"数据转换完成,共 {len(df)} 条记录")
-    return df
-
-
-def load_to_target(
-    df: pd.DataFrame,
-    conn: psycopg2.extensions.connection,
-    update_mode: str = "append",
-    batch_size: int = 1000,
-) -> int:
-    """
-    将数据加载到目标表
-
-    Args:
-        df: 要加载的DataFrame
-        conn: 目标数据库连接
-        update_mode: 更新模式(append 或 full)
-        batch_size: 批量插入大小
-
-    Returns:
-        插入的记录数
-    """
-    if df.empty:
-        logger.warning("没有数据需要加载")
-        return 0
-
-    logger.info(f"正在将 {len(df)} 条记录加载到目标表...")
-
-    target_table = f"{TARGET_SCHEMA}.{TARGET_TABLE}"
-    cursor = conn.cursor()
-    inserted_count = 0
-
-    try:
-        # 全量更新模式:先清空目标表
-        if update_mode.lower() == "full":
-            logger.info("全量更新模式:清空目标表...")
-            cursor.execute(f"TRUNCATE TABLE {target_table}")
-            logger.info("目标表已清空")
-
-        # 目标表结构准备插入的列(不包含 create_time,由数据库自动设置)
-        columns = ["warehouse_name", "total_inventory"]
-
-        # 构建插入SQL
-        placeholders = ", ".join(["%s"] * len(columns))
-        column_names = ", ".join(columns)
-        insert_sql = (
-            f"INSERT INTO {target_table} ({column_names}) VALUES ({placeholders})"
-        )
-
-        # 批量插入
-        for i in range(0, len(df), batch_size):
-            batch_df = df.iloc[i : i + batch_size]
-            records = []
-            for _, row in batch_df.iterrows():
-                record = tuple(
-                    None if pd.isna(row.get(col)) else row.get(col) for col in columns
-                )
-                records.append(record)
-
-            cursor.executemany(insert_sql, records)
-            inserted_count += len(records)
-            logger.debug(f"已插入 {inserted_count}/{len(df)} 条记录")
-
-        conn.commit()
-        logger.info(f"成功加载 {inserted_count} 条记录到 {target_table}")
-        return inserted_count
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"数据加载失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def main() -> dict[str, Any]:
-    """
-    主函数:执行ETL流程
-
-    Returns:
-        执行结果字典
-    """
-    result = {
-        "task_id": TASK_ID,
-        "task_name": TASK_NAME,
-        "status": "failed",
-        "records_extracted": 0,
-        "records_loaded": 0,
-        "error_message": None,
-        "execution_time": None,
-    }
-
-    start_time = datetime.now()
-    source_conn = None
-    target_conn = None
-
-    try:
-        logger.info("=" * 60)
-        logger.info(f"任务开始: {TASK_NAME}")
-        logger.info("=" * 60)
-
-        # 步骤1: 建立数据库连接
-        logger.info("[Step 1/5] 建立数据库连接...")
-        source_conn = get_source_connection()
-        target_conn = get_target_connection()
-
-        # 步骤2: 确保目标表存在(重要:必须在数据加载前执行)
-        logger.info("[Step 2/5] 检查/创建目标表...")
-        ensure_target_table_exists(target_conn)
-
-        # 步骤3: 从源表提取数据
-        logger.info("[Step 3/5] 提取并汇总源数据...")
-        df = extract_source_data(source_conn)
-        result["records_extracted"] = len(df)
-
-        # 步骤4: 数据转换
-        logger.info("[Step 4/5] 数据转换...")
-        df_transformed = transform_data(df)
-
-        # 步骤5: 加载到目标表
-        logger.info("[Step 5/5] 加载数据到目标表...")
-        records_loaded = load_to_target(
-            df_transformed, target_conn, update_mode=UPDATE_MODE
-        )
-        result["records_loaded"] = records_loaded
-
-        result["status"] = "success"
-        logger.info("=" * 60)
-        logger.info(
-            f"任务完成! 提取: {result['records_extracted']}, 加载: {result['records_loaded']}"
-        )
-        logger.info("=" * 60)
-
-    except Exception as e:
-        result["status"] = "failed"
-        result["error_message"] = str(e)
-        logger.error(f"任务执行失败: {e}")
-        raise
-
-    finally:
-        # 关闭数据库连接
-        if source_conn:
-            source_conn.close()
-            logger.debug("源数据库连接已关闭")
-        if target_conn:
-            target_conn.close()
-            logger.debug("目标数据库连接已关闭")
-
-        result["execution_time"] = str(datetime.now() - start_time)
-
-    return result
-
-
-if __name__ == "__main__":
-    # 配置日志
-    # 重要:日志输出到 stdout 而非 stderr,以便 n8n 工作流正确解析输出
-    logger.remove()
-    logger.add(
-        sys.stdout,
-        level="INFO",
-        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
-    )
-    logger.add(
-        os.path.join(PROJECT_ROOT, "logs", f"task_{TASK_ID}.log"),
-        level="DEBUG",
-        rotation="10 MB",
-        retention="7 days",
-        encoding="utf-8",
-    )
-
-    try:
-        result = main()
-        if result["status"] == "success":
-            sys.exit(0)
-        else:
-            sys.exit(1)
-    except Exception as e:
-        logger.exception(f"脚本执行异常: {e}")
-        sys.exit(1)

+ 0 - 232
docs/API_get_BD_list接口说明.md

@@ -1,232 +0,0 @@
-# GET /api/dataflow/get-BD-list 接口说明
-
-## 接口概述
-
-获取 Neo4j 图数据库中所有 BusinessDomain(业务域)节点的列表。
-
-## 接口信息
-
-- **路径**: `/api/dataflow/get-BD-list`
-- **方法**: `GET`
-- **认证**: 需要(根据项目配置)
-
-## 请求参数
-
-无需请求参数
-
-## 响应格式
-
-### 成功响应
-
-**HTTP状态码**: 200
-
-**响应体**:
-
-```json
-{
-  "code": 200,
-  "data": [
-    {
-      "id": 276,
-      "name_zh": "科室对照表",
-      "name_en": "TB_JC_KSDZB",
-      "tag": "数据资源"
-    },
-    {
-      "id": 277,
-      "name_zh": "科室对照表",
-      "name_en": "departments",
-      "tag": "数据模型"
-    }
-  ],
-  "message": "操作成功"
-}
-```
-
-### 响应字段说明
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| code | Integer | 响应状态码,200表示成功 |
-| data | Array | BusinessDomain节点列表 |
-| data[].id | Integer | 节点ID(Neo4j内部ID) |
-| data[].name_zh | String | 业务域中文名称 |
-| data[].name_en | String | 业务域英文名称 |
-| data[].tag | String | 标签名称,通过BELONGS_TO关系获取的DataLabel节点的中文名称 |
-| message | String | 响应消息 |
-
-### 错误响应
-
-**HTTP状态码**: 500
-
-**响应体**:
-
-```json
-{
-  "code": 500,
-  "message": "获取BusinessDomain列表失败: {错误详情}",
-  "data": {}
-}
-```
-
-## 实现逻辑
-
-### 1. 路由层(app/api/data_flow/routes.py)
-
-```python
-@bp.route('/get-BD-list', methods=['GET'])
-def get_business_domain_list():
-    """获取BusinessDomain节点列表"""
-    try:
-        logger.info("接收到获取BusinessDomain列表请求")
-        
-        # 调用服务层函数获取BusinessDomain列表
-        bd_list = DataFlowService.get_business_domain_list()
-        
-        res = success(bd_list, "操作成功")
-        return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
-    except Exception as e:
-        logger.error(f"获取BusinessDomain列表失败: {str(e)}")
-        res = failed(f'获取BusinessDomain列表失败: {str(e)}', 500, {})
-        return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
-```
-
-### 2. 服务层(app/core/data_flow/dataflows.py)
-
-```python
-@staticmethod
-def get_business_domain_list() -> List[Dict[str, Any]]:
-    """
-    获取BusinessDomain节点列表
-    
-    Returns:
-        BusinessDomain节点列表,每个节点包含 id, name_zh, name_en, tag
-    """
-    try:
-        logger.info("开始查询BusinessDomain节点列表")
-        
-        with connect_graph().session() as session:
-            # 查询所有BusinessDomain节点及其BELONGS_TO关系指向的标签
-            query = """
-            MATCH (bd:BusinessDomain)
-            OPTIONAL MATCH (bd)-[:BELONGS_TO]->(label:DataLabel)
-            RETURN id(bd) as id, 
-                   bd.name_zh as name_zh, 
-                   bd.name_en as name_en,
-                   label.name_zh as tag
-            ORDER BY bd.create_time DESC
-            """
-            
-            result = session.run(query)
-            
-            bd_list = []
-            for record in result:
-                bd_item = {
-                    "id": record["id"],
-                    "name_zh": record["name_zh"] if record["name_zh"] else "",
-                    "name_en": record["name_en"] if record["name_en"] else "",
-                    "tag": record["tag"] if record["tag"] else ""
-                }
-                bd_list.append(bd_item)
-            
-            logger.info(f"成功查询到 {len(bd_list)} 个BusinessDomain节点")
-            return bd_list
-            
-    except Exception as e:
-        logger.error(f"查询BusinessDomain节点列表失败: {str(e)}")
-        raise e
-```
-
-### 3. Cypher 查询说明
-
-```cypher
-MATCH (bd:BusinessDomain)
-OPTIONAL MATCH (bd)-[:BELONGS_TO]->(label:DataLabel)
-RETURN id(bd) as id, 
-       bd.name_zh as name_zh, 
-       bd.name_en as name_en,
-       label.name_zh as tag
-ORDER BY bd.create_time DESC
-```
-
-**查询逻辑**:
-1. 匹配所有 `BusinessDomain` 节点
-2. 可选匹配其通过 `BELONGS_TO` 关系指向的 `DataLabel` 节点
-3. 返回 BusinessDomain 的 id、中文名、英文名
-4. 返回 DataLabel 的中文名作为 tag
-5. 按创建时间降序排列
-
-## 使用示例
-
-### cURL 示例
-
-```bash
-curl -X GET http://localhost:5000/api/dataflow/get-BD-list \
-  -H "Content-Type: application/json"
-```
-
-### JavaScript 示例
-
-```javascript
-fetch('http://localhost:5000/api/dataflow/get-BD-list', {
-  method: 'GET',
-  headers: {
-    'Content-Type': 'application/json'
-  }
-})
-.then(response => response.json())
-.then(data => {
-  console.log('BusinessDomain列表:', data.data);
-  data.data.forEach(bd => {
-    console.log(`ID: ${bd.id}, 名称: ${bd.name_zh} (${bd.name_en}), 标签: ${bd.tag}`);
-  });
-})
-.catch(error => console.error('Error:', error));
-```
-
-### Python 示例
-
-```python
-import requests
-
-url = "http://localhost:5000/api/dataflow/get-BD-list"
-response = requests.get(url)
-
-if response.status_code == 200:
-    result = response.json()
-    bd_list = result['data']
-    
-    for bd in bd_list:
-        print(f"ID: {bd['id']}, 名称: {bd['name_zh']} ({bd['name_en']}), 标签: {bd['tag']}")
-else:
-    print(f"请求失败: {response.text}")
-```
-
-## 注意事项
-
-1. **标签字段(tag)**:
-   - tag 字段通过查询 BusinessDomain 节点的 BELONGS_TO 关系获取
-   - 如果节点没有 BELONGS_TO 关系,tag 字段为空字符串
-   - 标签的值来自 DataLabel 节点的 name_zh 属性
-
-2. **排序规则**:
-   - 结果按 BusinessDomain 节点的 create_time 降序排列
-   - 最新创建的节点排在前面
-
-3. **空值处理**:
-   - 如果 name_zh、name_en 或 tag 为 null,会转换为空字符串 ""
-   - 保证前端不会收到 null 值
-
-4. **性能考虑**:
-   - 该接口返回所有 BusinessDomain 节点,不支持分页
-   - 如果节点数量很大,建议后续添加分页功能
-
-## 相关接口
-
-- `POST /api/model/data/save` - 创建数据模型时会自动创建 BusinessDomain 节点
-- 其他数据流相关接口参见 `/api/dataflow/` 路径下的接口
-
-## 更新历史
-
-- **2024-11-28**: 初始版本,实现基本的 BusinessDomain 节点列表查询功能
-

+ 0 - 258
docs/BUSINESS_RULES.md

@@ -1,258 +0,0 @@
-# DataOps Platform - Business Rules & Validation Standards
-
-## Overview
-This document defines the core business rules, validation standards, and processing workflows for the DataOps platform. These rules ensure data integrity, consistent API behavior, and reliable business logic execution.
-
-## 1. Data Validation Rules
-
-### 1.1 General Field Validation
-**Rule ID**: `VALIDATION_001`
-
-#### Format Validation Rules
-```python
-# Email validation  
-- Must match pattern: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
-- Invalid format generates ERROR
-
-# Array fields validation
-- Must be array type if present
-```
-
-## 2. API Response Standards
-
-### 2.1 Standard Response Format
-**Rule ID**: `API_RESPONSE_001`
-
-All API responses MUST follow this structure:
-```json
-{
-    "success": boolean,
-    "message": string,
-    "data": any,
-    "code": number (optional)
-}
-```
-
-#### Success Response Example
-```json
-{
-    "success": true,
-    "message": "操作成功",
-    "data": { ... }
-}
-```
-
-#### Error Response Example
-```json
-{
-    "success": false,
-    "message": "详细错误描述",
-    "data": null,
-    "code": 400
-}
-```
-
-### 2.2 HTTP Status Code Rules
-**Rule ID**: `API_STATUS_001`
-
-- `200`: Successful operation
-- `400`: Bad request (validation errors, missing parameters)
-- `404`: Resource not found
-- `500`: Internal server error
-
-### 2.3 Content-Type Headers
-**Rule ID**: `API_HEADERS_001`
-
-- All API responses: `application/json; charset=utf-8`
-- File downloads: Preserve original content-type
-- CORS headers automatically configured
-
-## 3. Database Rules
-
-### 3.1 Data Integrity Rules
-**Rule ID**: `DB_INTEGRITY_001`
-
-#### Timestamp Management
-```python
-# Use East Asia timezone for all timestamps
-from datetime import datetime
-import pytz
-```
-
-### 3.2 Data Model Rules
-**Rule ID**: `DB_MODEL_001`
-
-#### Field Constraints
-```python
-# String fields
-name: max_length=100, nullable=False
-email: max_length=100, nullable=True
-
-# JSON fields - use for structured data
-```
-
-## 4. File Processing Rules
-
-### 4.1 File Upload Rules
-**Rule ID**: `FILE_UPLOAD_001`
-
-#### Allowed Extensions
-```python
-ALLOWED_EXTENSIONS = {
-    'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 
-    'xlsx', 'xls', 'csv', 'sql', 'dll'
-}
-```
-
-#### Storage Rules
-- Development: Local filesystem
-- Production: MinIO object storage
-- File path tracking in database
-
-## 5. Business Logic Rules
-
-### 5.1 Graph Processing Rules
-**Rule ID**: `BUSINESS_LOGIC_001`
-
-#### Neo4j Graph Processing
-- Maximum traversal depth: 10 levels
-- Duplicate node prevention
-- Proper relationship management
-
-### 5.2 Query Processing Rules
-**Rule ID**: `BUSINESS_LOGIC_002`
-
-#### Graph Query Optimization
-```python
-# Use recursive traversal for label-based queries
-# Pattern: (start_node)-[*1..10]->(end_node)
-```
-
-## 6. Security Rules
-
-### 6.1 Input Validation
-**Rule ID**: `SECURITY_001`
-
-#### Sanitization Requirements
-- All user inputs MUST be validated
-- SQL injection prevention through SQLAlchemy ORM
-- XSS prevention through proper encoding
-- File upload validation (extension, size, content-type)
-
-#### Authentication & Authorization
-- Environment variables for sensitive data
-- API key validation for external services
-- CORS configuration for cross-origin requests
-
-### 6.2 Error Handling
-**Rule ID**: `SECURITY_002`
-
-#### Information Disclosure Prevention
-- Generic error messages for production
-- Detailed logging for debugging
-- No sensitive data in error responses
-- Stack traces only in development mode
-
-## 7. Configuration Rules
-
-### 7.1 Environment-Specific Rules
-**Rule ID**: `CONFIG_001`
-
-#### Development Environment
-- Debug mode: ON
-- Detailed logging: ON
-- Local database connections
-- Console logging: ON
-
-#### Production Environment  
-- Debug mode: OFF
-- Info-level logging only
-- Remote database connections
-- File logging only
-- Security headers enforced
-
-### 7.2 Service Integration Rules
-**Rule ID**: `CONFIG_002`
-
-#### External Service Configuration
-```python
-# LLM Services (Qwen API)
-- API key from environment variables
-- Fallback to default for development
-- Rate limiting and retry logic
-
-# Database Services
-- Connection pooling enabled
-- Health check (pool_pre_ping: True)
-- Connection recycling (300 seconds)
-```
-
-## 8. Logging & Monitoring Rules
-
-### 8.1 Logging Standards
-**Rule ID**: `LOGGING_001`
-
-#### Log Format
-```python
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s'
-```
-
-#### Log Levels
-- **DEBUG**: Development detailed information
-- **INFO**: General operational information
-- **WARNING**: Validation warnings, non-critical issues
-- **ERROR**: Error conditions, exceptions
-- **CRITICAL**: System failures
-
-#### Log Rotation
-- Development: Console + file logging
-- Production: File logging only
-- UTF-8 encoding for Chinese character support
-
-## 9. Performance Rules
-
-### 9.1 Database Performance
-**Rule ID**: `PERFORMANCE_001`
-
-#### Query Optimization
-- Use proper indexing for frequently queried fields
-- Batch processing for large datasets (batch_size: 1000)
-- Connection pooling (pool_size: 10, max_overflow: 20)
-
-#### Caching Strategy
-- Session-based caching for Neo4j queries
-- API response caching for static data
-
-## 10. Compliance & Audit Rules
-
-### 10.1 Data Tracking
-**Rule ID**: `AUDIT_001`
-
-#### Change Tracking
-- All data modifications logged with timestamp
-- User attribution for all operations
-
-#### Data Retention
-- Archive processed files
-- Maintain processing history
-
----
-
-## Rule Enforcement
-
-### Implementation Guidelines
-1. **Validation**: Implement validation functions
-2. **Error Handling**: Use standardized error response format
-3. **Testing**: Create unit tests for each business rule
-4. **Documentation**: Update API documentation when rules change
-
-### Rule Violation Handling
-- **Critical violations**: Return HTTP 400/500 with detailed error message
-- **Warning violations**: Log warning, continue processing
-- **Data quality issues**: Create audit records for manual review
-
-### Review Process
-- Monthly review of business rules effectiveness
-- Update rules based on operational feedback
-- Version control for rule changes
-- Impact assessment for rule modifications

+ 0 - 219
docs/CHANGELOG_auto_deploy.md

@@ -1,219 +0,0 @@
-# 自动部署功能更新日志
-
-## 版本:2026-01-07
-
-### 新增功能
-
-#### 1. 自动部署到生产服务器
-
-- 任务完成后自动将脚本和工作流部署到生产服务器 192.168.3.143
-- 支持 SSH 密码认证
-- 自动创建远程目录
-- 自动设置脚本执行权限
-
-#### 2. 智能工作流文件查找
-
-系统会自动查找并部署相关的 n8n 工作流文件:
-- 与脚本同目录的 `n8n_workflow_*.json` 文件
-- `datafactory/n8n_workflows/` 目录下的工作流
-- 根据任务名称匹配的工作流文件
-
-#### 3. 新增命令行参数
-
-| 参数 | 说明 |
-|------|------|
-| `--enable-deploy` | 启用自动部署(默认启用) |
-| `--no-deploy` | 禁用自动部署 |
-| `--deploy-now TASK_ID` | 立即部署指定任务 |
-| `--test-connection` | 测试 SSH 连接 |
-
-#### 4. 新增函数
-
-- `get_ssh_connection()` - 建立 SSH 连接
-- `test_ssh_connection()` - 测试连接
-- `deploy_script_to_production()` - 部署脚本
-- `deploy_n8n_workflow_to_production()` - 部署工作流
-- `auto_deploy_completed_task()` - 自动部署任务
-
-### 修改内容
-
-#### 1. 全局配置
-
-新增生产服务器配置:
-```python
-PRODUCTION_SERVER = {
-    "host": "192.168.3.143",
-    "port": 22,
-    "username": "ubuntu",
-    "password": "citumxl2357",
-    "script_path": "/opt/dataops-platform/datafactory/scripts",
-    "workflow_path": "/opt/dataops-platform/n8n/workflows",
-}
-```
-
-新增全局变量:
-```python
-ENABLE_AUTO_DEPLOY: bool = True  # 默认启用自动部署
-```
-
-#### 2. sync_completed_tasks_to_db() 函数
-
-在任务同步到数据库后,自动调用部署功能:
-```python
-# 自动部署到生产服务器(如果启用)
-if ENABLE_AUTO_DEPLOY:
-    logger.info(f"🚀 开始自动部署任务 {task_id} 到生产服务器...")
-    if auto_deploy_completed_task(t):
-        logger.info(f"✅ 任务 {task_id} 已成功部署到生产服务器")
-    else:
-        logger.warning(f"⚠️ 任务 {task_id} 部署到生产服务器失败")
-```
-
-#### 3. main() 函数
-
-新增命令处理逻辑:
-- 测试连接命令处理
-- 立即部署命令处理
-- 自动部署开关控制
-
-### 依赖要求
-
-新增依赖:
-```
-paramiko>=2.7.0  # SSH 连接和文件传输
-```
-
-安装方式:
-```bash
-pip install paramiko
-# 或
-python scripts/install_deploy_deps.py
-```
-
-### 使用示例
-
-#### 基本使用
-```bash
-# 启动 Agent 循环模式(自动部署)
-python scripts/auto_execute_tasks.py --chat-loop --use-agent
-
-# 禁用自动部署
-python scripts/auto_execute_tasks.py --chat-loop --use-agent --no-deploy
-```
-
-#### 测试和调试
-```bash
-# 测试 SSH 连接
-python scripts/auto_execute_tasks.py --test-connection
-
-# 手动部署指定任务
-python scripts/auto_execute_tasks.py --deploy-now 123
-```
-
-### 文件结构
-
-```
-DataOps-platform-new/
-├── scripts/
-│   ├── auto_execute_tasks.py          # 主脚本(已更新)
-│   └── install_deploy_deps.py         # 依赖安装脚本(新增)
-├── docs/
-│   ├── auto_deploy_guide.md           # 详细使用指南(新增)
-│   ├── auto_deploy_quick_reference.md # 快速参考(新增)
-│   └── CHANGELOG_auto_deploy.md       # 更新日志(本文件)
-└── tasks/
-    └── pending_tasks.json              # 任务状态文件
-```
-
-### 部署流程
-
-```
-┌─────────────────┐
-│  任务完成检测    │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  同步到数据库    │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  建立 SSH 连接   │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  上传 Python 脚本│
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  查找工作流文件  │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  上传工作流文件  │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  设置文件权限    │
-└────────┬────────┘
-         │
-         ▼
-┌─────────────────┐
-│  部署完成        │
-└─────────────────┘
-```
-
-### 安全说明
-
-1. **密码存储**:当前密码明文存储在代码中,建议后续改用环境变量或密钥认证
-2. **网络安全**:确保生产服务器仅在内网访问
-3. **权限控制**:使用最小权限原则,定期审计部署日志
-
-### 已知限制
-
-1. 仅支持单个生产服务器
-2. 不支持自动回滚
-3. 不支持部署历史记录
-4. 密码明文存储
-
-### 未来计划
-
-- [ ] 支持 SSH 密钥认证
-- [ ] 支持多服务器部署
-- [ ] 部署版本管理
-- [ ] 自动回滚机制
-- [ ] 部署通知功能
-- [ ] 部署前后钩子脚本
-
-### 测试建议
-
-1. 首次使用前运行 `--test-connection` 测试连接
-2. 使用 `--deploy-now` 手动部署单个任务进行测试
-3. 确认部署成功后再启用自动部署循环模式
-4. 定期检查生产服务器上的文件和权限
-
-### 故障排查
-
-详见:[auto_deploy_guide.md](./auto_deploy_guide.md#故障排查)
-
-### 技术支持
-
-如遇问题,请检查:
-1. 网络连接是否正常
-2. SSH 服务是否运行
-3. 用户权限是否足够
-4. paramiko 是否正确安装
-5. 日志输出的错误信息
-
-### 贡献者
-
-- 初始实现:2026-01-07
-
-### 许可证
-
-与主项目保持一致

+ 0 - 253
docs/DEPLOYMENT_GUIDE.md

@@ -1,253 +0,0 @@
-# DataOps Platform 部署指南
-
-## 📋 本次更新内容
-
-### 时区修正
-将所有时间字段从 UTC 时间改为东八区(Asia/Shanghai)时间。
-
-### 修改的文件
-- ✅ `app/core/common/timezone_utils.py` - 新增时区工具模块(兼容 Python 3.8+)
-- ✅ `app/models/data_product.py` - 时间字段修正
-- ✅ `app/models/metadata_review.py` - 时间字段修正
-- ✅ `app/core/data_service/data_product_service.py` - 时间处理修正
-- ✅ `app/core/meta_data/redundancy_check.py` - 时间处理修正
-- ✅ `app/core/business_domain/business_domain.py` - 时间处理修正
-
-### 新增的文件
-- 📄 `scripts/fix_startup.sh` - 自动修复脚本
-- 📄 `scripts/diagnose_issue.sh` - 问题诊断脚本
-- 📄 `scripts/TROUBLESHOOTING.md` - 故障排查指南
-- 📄 `QUICK_FIX.md` - 快速修复指南
-- 📄 `PYTHON38_COMPATIBILITY.md` - Python 3.8 兼容性说明
-- 📄 `docs/timezone_fix_summary.md` - 时区修正总结
-
-## 🚀 部署步骤(生产环境 Python 3.8)
-
-### 前置条件检查
-
-```bash
-# 1. 检查 Python 版本
-python --version
-# 应该显示: Python 3.8.x
-
-# 2. 检查 backports.zoneinfo 是否已安装
-cd /opt/dataops-platform
-source venv/bin/activate
-python -c "import backports.zoneinfo; print('backports.zoneinfo 已安装')"
-# 如果已安装,应该显示: backports.zoneinfo 已安装
-```
-
-### 步骤 1: 上传代码到服务器
-
-**方法 A: 使用 Git(推荐)**
-```bash
-cd /opt/dataops-platform
-git pull origin main
-```
-
-**方法 B: 使用 SCP 上传**
-```bash
-# 在本地执行
-scp -r app/ scripts/ docs/ *.md ubuntu@your-server:/opt/dataops-platform/
-```
-
-### 步骤 2: 运行自动修复脚本
-
-```bash
-# 在服务器上执行
-cd /opt/dataops-platform/scripts
-sudo chmod +x fix_startup.sh diagnose_issue.sh
-sudo ./fix_startup.sh
-```
-
-### 步骤 3: 验证部署
-
-```bash
-# 1. 检查服务状态
-sudo supervisorctl status dataops-platform
-# 预期输出: dataops-platform RUNNING pid xxx, uptime x:xx:xx
-
-# 2. 测试健康检查
-curl http://localhost:5500/api/system/health
-# 预期输出: {"status":"healthy",...}
-
-# 3. 测试时区功能
-cd /opt/dataops-platform
-source venv/bin/activate
-python -c "from app.core.common.timezone_utils import now_china_naive; print('当前东八区时间:', now_china_naive())"
-# 预期输出: 当前东八区时间: 2026-01-12 18:30:45.123456
-```
-
-## 🔧 技术细节
-
-### Python 3.8 兼容性
-
-代码已自动适配 Python 3.8 和 3.9+:
-
-```python
-# app/core/common/timezone_utils.py
-try:
-    # Python 3.9+
-    from zoneinfo import ZoneInfo
-except ImportError:
-    # Python 3.8 使用 backports
-    from backports.zoneinfo import ZoneInfo
-```
-
-### 依赖要求
-
-| Python 版本 | zoneinfo 模块 | 系统依赖 |
-|------------|--------------|---------|
-| 3.8 | `backports.zoneinfo` (pip) | `tzdata` (apt) |
-| 3.9+ | 标准库 `zoneinfo` | `tzdata` (apt) |
-
-### 当前生产环境配置
-
-- **Python 版本**: 3.8
-- **已安装**: `backports.zoneinfo`
-- **需确认**: 系统 `tzdata` 包
-
-## ⚠️ 可能遇到的问题
-
-### 问题 1: 应用启动失败
-
-**症状**:
-```bash
-[ERROR] dataops-platform 重启失败!
-tail: cannot open '/opt/dataops-platform/logs/gunicorn_error.log' for reading: No such file or directory
-```
-
-**原因**: 缺少系统时区数据
-
-**解决**:
-```bash
-sudo apt-get update
-sudo apt-get install -y tzdata
-sudo supervisorctl restart dataops-platform
-```
-
-### 问题 2: ModuleNotFoundError: No module named 'zoneinfo'
-
-**症状**:
-```python
-ModuleNotFoundError: No module named 'zoneinfo'
-```
-
-**原因**: Python 3.8 环境未安装 `backports.zoneinfo`
-
-**解决**:
-```bash
-cd /opt/dataops-platform
-source venv/bin/activate
-pip install backports.zoneinfo
-sudo supervisorctl restart dataops-platform
-```
-
-### 问题 3: ZoneInfoNotFoundError
-
-**症状**:
-```python
-ZoneInfoNotFoundError: 'No time zone found with key Asia/Shanghai'
-```
-
-**原因**: 系统缺少时区数据库
-
-**解决**:
-```bash
-sudo apt-get update
-sudo apt-get install -y tzdata
-sudo supervisorctl restart dataops-platform
-```
-
-## 📊 验证清单
-
-部署完成后,请验证以下功能:
-
-- [ ] 服务启动成功(`supervisorctl status` 显示 RUNNING)
-- [ ] 健康检查接口返回 200
-- [ ] 时区模块正常工作
-- [ ] 创建新数据订单,检查 `created_at` 字段
-- [ ] 更新订单状态,检查 `updated_at` 和 `processed_at` 字段
-- [ ] 注册数据产品,检查时间字段
-- [ ] 查看数据产品,检查 `last_viewed_at` 字段
-
-## 🔍 故障排查
-
-### 查看实时日志
-
-```bash
-# Supervisor 错误日志(最重要)
-sudo tail -f /var/log/supervisor/dataops-platform-stderr.log
-
-# Gunicorn 错误日志
-tail -f /opt/dataops-platform/logs/gunicorn_error.log
-
-# Gunicorn 访问日志
-tail -f /opt/dataops-platform/logs/gunicorn_access.log
-```
-
-### 运行诊断脚本
-
-```bash
-cd /opt/dataops-platform/scripts
-sudo ./diagnose_issue.sh
-```
-
-### 手动启动测试
-
-```bash
-cd /opt/dataops-platform
-source venv/bin/activate
-gunicorn -c gunicorn_config.py 'app:create_app()'
-# 按 Ctrl+C 停止
-```
-
-## 📚 相关文档
-
-- **快速修复**: `QUICK_FIX.md`
-- **Python 3.8 兼容性**: `PYTHON38_COMPATIBILITY.md`
-- **故障排查**: `scripts/TROUBLESHOOTING.md`
-- **时区修正总结**: `docs/timezone_fix_summary.md`
-
-## 🎯 回滚方案
-
-如果部署后出现严重问题,可以回滚到之前的版本:
-
-```bash
-# 1. 回滚代码
-cd /opt/dataops-platform
-git reset --hard HEAD~1
-
-# 2. 重启服务
-sudo supervisorctl restart dataops-platform
-
-# 3. 验证服务
-sudo supervisorctl status dataops-platform
-```
-
-## 📞 技术支持
-
-如果遇到问题,请提供以下信息:
-
-1. **诊断输出**:
-   ```bash
-   cd /opt/dataops-platform/scripts
-   sudo ./diagnose_issue.sh > ~/diagnosis.log 2>&1
-   ```
-
-2. **错误日志**:
-   ```bash
-   sudo tail -100 /var/log/supervisor/dataops-platform-stderr.log > ~/error.log
-   ```
-
-3. **系统信息**:
-   ```bash
-   python --version
-   cat /etc/os-release
-   ```
-
----
-
-**部署日期**: 2026-01-12  
-**版本**: v1.0 - 时区修正  
-**兼容性**: Python 3.8+

+ 1044 - 0
docs/DataMeta管理智能体技术文档.md

@@ -0,0 +1,1044 @@
+# DataMeta 管理智能体技术文档
+
+## 1. 概述
+
+DataMeta 管理智能体是一个基于 n8n 构建的智能对话系统,用于管理元数据(DataMeta)和数据标签(DataLabel)的增删改查操作。该智能体采用**主 Agent + 子 Agent**的架构设计,通过自然语言交互实现元数据的全生命周期管理。
+
+### 1.1 基本信息
+
+| 属性       | 值                                                     |
+| -------- | ----------------------------------------------------- |
+| 主工作流名称   | DataMeta管理智能体                                         |
+| 主工作流 ID  | `74AYpVBKwYt8fhjx7rMzc`                               |
+| 子工作流名称   | 新增元数据子Agent                                           |
+| 子工作流 ID  | `Uv2FXcr80GtxBGxv`                                    |
+| AI 模型    | DeepSeek                                              |
+| API 基础地址 | `https://company.citupro.com:18183`                   |
+| 访问地址     | `https://n8n.citupro.com/webhook/datameta-agent/chat` |
+
+### 1.2 功能清单
+
+| 功能      | 描述            | 处理节点                         |
+| ------- | ------------- | ---------------------------- |
+| 新增元数据   | 多轮对话引导收集信息并创建 | 子工作流 Add Metadata Agent      |
+| 查询元数据列表 | 分页查询,支持筛选     | CRUD Agent → list_datameta   |
+| 获取元数据详情 | 按 ID 查询详细信息   | CRUD Agent → get_datameta    |
+| 更新元数据   | 修改现有元数据字段     | CRUD Agent → update_datameta |
+| 删除元数据   | 按 ID 删除(需确认)  | CRUD Agent → delete_datameta |
+| 查看标签列表  | 获取可用标签        | CRUD Agent → list_labels     |
+
+---
+
+## 2. 系统架构
+
+### 2.1 整体架构图
+
+![221782da-259e-444c-a9fc-6815bf7ee507](file:///C:/Users/Administrator/Pictures/Typedown/221782da-259e-444c-a9fc-6815bf7ee507.png)
+
+### 2.2 意图分类与路由流程
+
+```mermaid
+flowchart LR
+    subgraph Input
+        direction LR
+        UI["💬 用户消息"]
+    end
+
+    subgraph Classification
+        direction LR
+        IC["🧠 Intent Classifier"]
+        MEM["📝 历史对话记忆"]
+    end
+
+    subgraph Routing
+        direction LR
+        SW{"🔀 Switch"}
+    end
+
+    subgraph Outputs
+        direction LR
+        O1["📤 新增元数据子流程"]
+        O2["🤖 CRUD Agent"]
+        O3["🤖 默认CRUD Agent"]
+    end
+
+    UI --> IC
+    MEM --> IC
+    IC -->|意图标签| SW
+    SW -->|包含 add_metadata| O1
+    SW -->|包含 other_operations| O2
+    SW -->|其他/默认| O3
+
+    style Input fill:#e3f2fd
+    style Classification fill:#f3e5f5
+    style Routing fill:#fff8e1
+    style Outputs fill:#e8f5e9
+```
+
+### 2.3 新增元数据多轮对话流程
+
+```mermaid
+sequenceDiagram
+    participant U as 👤 用户
+    participant M as 🧠 主Agent
+    participant S as 🤖 子Agent
+    participant API as 🌐 后端API
+
+    U->>M: "我想新增一个元数据"
+    M->>M: Intent Classifier 识别为 add_metadata
+    M->>S: 调用子工作流 (user_message, session_id)
+
+    S->>U: "请输入元数据的中文名称"
+    U->>M: "订单状态"
+    M->>S: 继续对话流程
+
+    S->>API: translate_name(node_name: "订单状态")
+    API-->>S: "order_status"
+    S->>U: "英文名称已翻译为: order_status,请选择数据类型"
+
+    U->>M: "varchar"
+    M->>S: 继续对话流程
+    S->>U: "是否启用该元数据?"
+
+    U->>M: "启用"
+    M->>S: 继续对话流程
+    S->>U: "请选择分类: 系统类/应用类/DataOps"
+
+    U->>M: "应用类"
+    M->>S: 继续对话流程
+
+    Note over S: 继续收集: 归属、描述、标签...
+
+    S->>U: "请确认以上信息"
+    U->>M: "确认"
+    M->>S: 提交创建
+
+    S->>API: add_datameta(name_zh, name_en, data_type, ...)
+    API-->>S: 创建成功,ID: 2325
+    S->>U: "✅ 元数据创建成功!ID: 2325"
+```
+
+---
+
+## 3. 节点配置详解
+
+### 3.1 主工作流节点
+
+#### 3.1.1 Chat Trigger
+
+| 配置项        | 值                                      |
+| ---------- | -------------------------------------- |
+| 节点类型       | `@n8n/n8n-nodes-langchain.chatTrigger` |
+| 版本         | 1.1                                    |
+| Webhook ID | `datameta-agent`                       |
+| 公开访问       | `true`                                 |
+
+**UI 配置:**
+
+```json
+{
+  "title": "DataMeta管理助手",
+  "subtitle": "我可以帮您管理元数据,支持查询、新增、更新、删除和标签管理",
+  "inputPlaceholder": "请输入您的需求,例如:查看所有元数据"
+}
+```
+
+**输出数据结构:**
+
+```json
+{
+  "chatInput": "用户输入的消息",
+  "sessionId": "会话唯一标识"
+}
+```
+
+#### 3.1.2 Intent Classifier (意图分类器)
+
+| 配置项    | 值                                |
+| ------ | -------------------------------- |
+| 节点类型   | `@n8n/n8n-nodes-langchain.agent` |
+| 版本     | 1.7                              |
+| 最大迭代次数 | 3                                |
+
+**输入配置:**
+
+```
+promptType: define
+text: ={{ $json.chatInput }}
+```
+
+**系统提示语 (System Message):**
+
+```
+你是一个意图分类器。分析用户的输入,判断用户的意图类型。
+
+## 重要:多轮对话上下文
+
+你有记忆功能,可以看到之前的对话历史。如果用户之前表达了新增元数据的意图,并且当前正在进行新增元数据的多轮对话流程中(例如:用户正在回答中文名、英文名、数据类型、状态、分类等问题),那么当前输入应该继续被分类为 add_metadata。
+
+**判断是否在新增元数据流程中的依据:**
+- 之前的对话中用户说过"新增"、"添加"、"创建"元数据
+- 系统正在询问元数据的各个字段(中文名、英文名、数据类型、状态、分类、归属、描述、标签)
+- 用户的当前输入是在回答这些字段的值
+- **系统展示了元数据信息汇总并询问确认**
+- **用户正在回应确认请求(无论是确认、否认还是修改)**
+
+**特别注意:确认阶段的用户回复**
+如果对话历史中系统最近展示了"元数据信息汇总"并询问"请确认以上信息是否正确",那么用户的任何回复都应该分类为 add_metadata,包括:
+- 确认类:"确认"、"正确"、"没问题"、"可以"
+- 否认类:"不正确"、"有问题"、"不对"、"错了"
+- 修改类:"修改xxx"、"把xxx改成xxx"、"xxx不对"
+
+## 意图分类
+
+请将用户意图分类为以下类型之一:
+
+1. **add_metadata** - 用户想要新增、添加、创建元数据,或者正在新增元数据的多轮对话流程中
+   - 关键词:新增、添加、创建、新建、录入
+   - 或者:用户正在回答新增元数据流程中的问题
+   - 或者:用户正在确认/修改元数据信息汇总
+
+2. **other_operations** - 其他所有操作
+   - 查询、列出、搜索元数据
+   - 查看元数据详情
+   - 更新、修改已存在的元数据(不是新增流程中的修改)
+   - 删除元数据
+   - 查看标签
+   - 其他任何操作
+
+## 输出格式
+
+你必须严格按照以下JSON格式输出,不要输出任何其他内容:
+
+{"intent": "add_metadata"}
+
+或
+
+{"intent": "other_operations"}
+
+只输出JSON,不要有任何解释或其他文字。
+```
+
+#### 3.1.3 Intent Switch (意图路由)
+
+| 配置项  | 值                       |
+| ---- | ----------------------- |
+| 节点类型 | `n8n-nodes-base.switch` |
+| 版本   | 3.4                     |
+| 模式   | rules                   |
+
+**路由规则:**
+
+| 输出端口 | 名称    | 条件                                         | 目标节点                          |
+| ---- | ----- | ------------------------------------------ | ----------------------------- |
+| 0    | 新增元数据 | `$json.output` contains "add_metadata"     | Execute Add Metadata Workflow |
+| 1    | 其他操作  | `$json.output` contains "other_operations" | CRUD Agent                    |
+| 2    | 默认    | fallback                                   | CRUD Agent                    |
+
+**完整配置:**
+
+```json
+{
+  "mode": "rules",
+  "rules": {
+    "values": [
+      {
+        "conditions": {
+          "conditions": [
+            {
+              "leftValue": "={{ $json.output }}",
+              "rightValue": "add_metadata",
+              "operator": { "type": "string", "operation": "contains" }
+            }
+          ],
+          "combinator": "and"
+        },
+        "renameOutput": true,
+        "outputKey": "新增元数据"
+      },
+      {
+        "conditions": {
+          "conditions": [
+            {
+              "leftValue": "={{ $json.output }}",
+              "rightValue": "other_operations",
+              "operator": { "type": "string", "operation": "contains" }
+            }
+          ],
+          "combinator": "and"
+        },
+        "renameOutput": true,
+        "outputKey": "其他操作"
+      }
+    ]
+  },
+  "options": {
+    "fallbackOutput": "extra",
+    "renameFallbackOutput": "默认"
+  }
+}
+```
+
+#### 3.1.4 Execute Add Metadata Workflow (执行子工作流)
+
+| 配置项     | 值                                |
+| ------- | -------------------------------- |
+| 节点类型    | `n8n-nodes-base.executeWorkflow` |
+| 版本      | 1.3                              |
+| 子工作流 ID | `Uv2FXcr80GtxBGxv`               |
+| 等待完成    | `true`                           |
+
+**参数传递配置:**
+
+```json
+{
+  "workflowInputs": {
+    "mappingMode": "defineBelow",
+    "value": {
+      "user_message": "={{ $('Chat Trigger').item.json.chatInput }}",
+      "session_id": "={{ $('Chat Trigger').item.json.sessionId }}"
+    }
+  }
+}
+```
+
+#### 3.1.5 Prepare CRUD Input (准备 CRUD 输入)
+
+| 配置项   | 值                    |
+| ----- | -------------------- |
+| 节点类型  | `n8n-nodes-base.set` |
+| 版本    | 3.4                  |
+| 节点 ID | `prepare-crud-input` |
+
+**用途:** 在 Intent Switch 路由到 CRUD Agent 之前,从 Chat Trigger 提取原始用户输入和会话 ID,确保 CRUD Agent 收到的是用户的实际请求,而不是 Intent Classifier 的输出。
+
+**配置:**
+
+```json
+{
+  "mode": "manual",
+  "duplicateItem": false,
+  "assignments": {
+    "assignments": [
+      {
+        "id": "chatInput",
+        "name": "chatInput",
+        "value": "={{ $('Chat Trigger').item.json.chatInput }}",
+        "type": "string"
+      },
+      {
+        "id": "sessionId",
+        "name": "sessionId",
+        "value": "={{ $('Chat Trigger').item.json.sessionId }}",
+        "type": "string"
+      }
+    ]
+  },
+  "options": {}
+}
+```
+
+> **为什么需要这个节点?**
+> 
+> Intent Switch 接收的输入是 Intent Classifier 的输出(如 `{"intent": "other_operations"}`)。如果直接将这个输出传递给 CRUD Agent,CRUD Agent 会收到意图分类结果而不是用户的实际请求,导致无法正确执行查询、更新等操作。
+> 
+> 通过 `Prepare CRUD Input` 节点,我们显式地从 Chat Trigger 获取原始的 `chatInput` 和 `sessionId`,确保 CRUD Agent 总是收到正确的用户消息。
+
+#### 3.1.6 CRUD Agent
+
+| 配置项    | 值                                |
+| ------ | -------------------------------- |
+| 节点类型   | `@n8n/n8n-nodes-langchain.agent` |
+| 版本     | 1.7                              |
+| 最大迭代次数 | 25                               |
+
+**输入配置:**
+
+```
+promptType: define
+text: ={{ $json.chatInput }}
+```
+
+> **注意:** CRUD Agent 的 `text` 参数从 `Prepare CRUD Input` 节点获取 `$json.chatInput`,而不是直接从 Chat Trigger 获取。这是因为 CRUD Agent 的上游节点是 `Prepare CRUD Input`。
+
+**系统提示语 (System Message):**
+
+```
+你是一个元数据管理智能体,负责帮助用户管理DataMeta(元数据)和DataLabel(数据标签)。
+
+## 你的职责
+
+根据用户的意图,选择合适的工具来完成任务:
+
+### 1. 查询元数据列表
+- 当用户想要**查看、列出、搜索**元数据时
+- 使用 `list_datameta` 工具
+- 可以按关键词、分类等条件筛选
+
+### 2. 获取元数据详情
+- 当用户想要**查看某个具体元数据的详细信息**时
+- 使用 `get_datameta` 工具
+- 需要提供元数据ID
+
+### 3. 更新元数据
+- 当用户想要**修改、更新**现有元数据时
+- 使用 `update_datameta` 工具
+- 需要提供元数据ID和要修改的字段
+
+### 4. 删除元数据
+- 当用户想要**删除**元数据时
+- 使用 `delete_datameta` 工具
+- 需要提供元数据ID
+- 删除前请确认用户意图
+
+### 5. 查看标签列表
+- 当用户想要**查看可用的标签**时
+- 使用 `list_labels` 工具
+
+## 回复要求
+
+- 请用中文回复用户
+- 清晰地展示操作结果
+- 如果操作失败,解释原因并提供建议
+```
+
+### 3.2 子工作流节点
+
+#### 3.2.1 Execute Workflow Trigger
+
+| 配置项  | 值                                       |
+| ---- | --------------------------------------- |
+| 节点类型 | `n8n-nodes-base.executeWorkflowTrigger` |
+| 版本   | 1.1                                     |
+
+**输入参数定义:**
+
+```json
+{
+  "inputSource": "workflowInputs",
+  "workflowInputs": {
+    "values": [
+      { "name": "user_message", "type": "string" },
+      { "name": "session_id", "type": "string" }
+    ]
+  }
+}
+```
+
+#### 3.2.2 Add Metadata Agent
+
+| 配置项    | 值                                |
+| ------ | -------------------------------- |
+| 节点类型   | `@n8n/n8n-nodes-langchain.agent` |
+| 版本     | 1.7                              |
+| 最大迭代次数 | 25                               |
+
+**输入配置:**
+
+```
+promptType: define
+text: ={{ $json.user_message }}
+```
+
+**系统提示语 (System Message):**
+
+```
+你是一个专门负责新增元数据的助手。你的任务是通过多轮对话收集用户输入的元数据信息,然后创建新的元数据。
+
+## 信息收集流程
+
+按以下顺序依次收集信息:
+
+### 步骤1:收集中文名称(必填)
+- 询问用户:"请输入元数据的中文名称"
+- 记录 name_zh
+
+### 步骤2:自动翻译英文名称并等待确认
+- 获取中文名称后,立即调用 translate_name 工具进行翻译
+- 展示翻译结果给用户:"英文名称已自动翻译为: xxx"
+- **重要:必须等待用户确认或提供修改后的英文名**
+- 询问用户:"请确认英文名称是否正确,或输入您希望使用的英文名称"
+- 用户可能的回复:
+  - "确认"、"好的"、"可以"、"没问题" → 使用翻译结果
+  - 提供新的英文名称 → 使用用户提供的名称
+- 确认后记录 name_en,然后才进入步骤3
+
+### 步骤3:收集数据类型(必填)
+- 询问用户:"请选择数据类型,常见类型有: varchar, integer, string, number, boolean, date"
+- **等待用户输入**
+- 记录 data_type
+
+### 步骤4:收集使用状态(可选)
+- 询问用户:"是否启用该元数据?请回复"启用"或"禁用"(默认启用)"
+- **等待用户回复**
+- 用户可能的回复:
+  - "启用"、"是"、"默认"、"确认" → status = true
+  - "禁用"、"否" → status = false
+- 确认后记录 status
+
+### 步骤5:选择分类(可选)
+- 询问用户:"请选择元数据分类:系统类 / 应用类 / DataOps,或输入"跳过""
+- **等待用户回复**
+- 用户可能的回复:
+  - "系统类"、"应用类"、"DataOps" → 记录对应分类
+  - "跳过"、"无"、"不选" → 留空
+- 确认后记录 category
+
+### 步骤6:输入制作单位/归属部门(可选)
+- 询问用户:"请输入制作单位/归属部门,或输入"跳过""
+- **等待用户回复**
+- 用户可能的回复:
+  - 具体部门名称 → 记录部门名称
+  - "跳过"、"无"、"不填" → 留空
+- 确认后记录 affiliation
+
+### 步骤7:输入描述(可选)
+- 询问用户:"请输入元数据描述,或输入"跳过""
+- **等待用户回复**
+- 用户可能的回复:
+  - 具体描述内容 → 记录描述
+  - "跳过"、"无"、"不填" → 留空
+- 确认后记录 describe
+
+### 步骤8:选择标签(可选)
+- 先调用 list_labels 获取可用标签列表
+- 展示标签选项给用户(显示ID和名称)
+- 询问用户:"请选择要添加的标签(输入标签ID,多个用逗号分隔),或输入"跳过""
+- **等待用户回复**
+- 用户可能的回复:
+  - 标签ID(如 "51" 或 "51,59,82") → 记录标签ID数组
+  - "跳过"、"无"、"不选" → 留空
+- 确认后记录 tag
+
+### 步骤9:确认并提交
+- 展示所有收集到的信息汇总给用户确认
+- **等待用户确认**
+- **用户回复处理:**
+  - 如果用户回复"确认"、"正确"、"没问题"、"可以"、"提交" → 调用 add_datameta 工具提交
+  - 如果用户回复"不正确"、"有问题"、"不对"、"需要修改" → 询问用户"请告诉我您要修改哪个字段?"
+  - 如果用户指定了要修改的字段(如"修改中文名"、"把数据类型改成integer") → 直接询问该字段的新值
+  - 修改完成后,重新展示更新后的信息汇总,再次询问确认
+
+### 步骤10:处理返回结果
+
+**情况1:完全匹配(已存在)**
+- 如果返回信息包含"已存在"或"完全匹配"
+- 从返回信息中提取已存在的元数据ID
+- 调用 get_datameta 获取详情并展示给用户
+- 告知用户:"该元数据已存在,无需重复创建"
+
+**情况2:疑似重复**
+- 如果返回信息包含"疑似重复"
+- 展示新创建的元数据信息
+- 提示用户:"元数据已创建成功,但发现疑似重复的元数据,已创建审核记录。"
+- 询问用户:"是否需要前往审核页面处理?"
+- 如果用户需要审核,提供链接:https://company.citupro.com:18183/dataReview
+
+**情况3:成功创建**
+- 展示新创建的元数据完整信息
+- 告知用户创建成功
+
+## 回复要求
+- 请用中文回复用户
+- 在多轮对话中记住用户已提供的所有字段,不要重复询问
+- 如果用户一次性提供了多个字段,直接记录并继续下一步
+```
+
+---
+
+## 4. Simple Memory 配置
+
+### 4.1 主工作流 Memory
+
+#### Simple Memory Intent (意图分类器记忆)
+
+| 配置项           | 值                                             |
+| ------------- | --------------------------------------------- |
+| 节点类型          | `@n8n/n8n-nodes-langchain.memoryBufferWindow` |
+| 版本            | 1.3                                           |
+| 上下文窗口长度       | 20                                            |
+| Session ID 类型 | customKey                                     |
+| Session Key   | `=intent-{{ $json.sessionId }}`               |
+
+**用途:** 记住用户的对话历史,以便 Intent Classifier 能够识别用户是否正在进行新增元数据的多轮对话流程。
+
+**关键配置:**
+
+```json
+{
+  "contextWindowLength": 20,
+  "sessionIdType": "customKey",
+  "sessionKey": "=intent-{{ $json.sessionId }}"
+}
+```
+
+> **重要**:Session Key 使用 `intent-` 前缀以区分不同 Agent 的记忆空间。
+
+#### Simple Memory CRUD (CRUD Agent 记忆)
+
+| 配置项           | 值                                             |
+| ------------- | --------------------------------------------- |
+| 节点类型          | `@n8n/n8n-nodes-langchain.memoryBufferWindow` |
+| 版本            | 1.3                                           |
+| 上下文窗口长度       | 20                                            |
+| Session ID 类型 | customKey                                     |
+| Session Key   | `=crud-{{ $json.sessionId }}`                 |
+
+**用途:** 记住用户在执行查询、更新、删除等操作时的上下文。
+
+**关键配置:**
+
+```json
+{
+  "contextWindowLength": 20,
+  "sessionIdType": "customKey",
+  "sessionKey": "=crud-{{ $json.sessionId }}"
+}
+```
+
+> **重要:Memory 隔离机制**
+> 
+> `Simple Memory Intent` 和 `Simple Memory CRUD` 必须使用不同的 sessionKey 前缀(`intent-` 和 `crud-`),以确保两个 Agent 的对话记忆完全隔离。
+> 
+> **问题背景:** 如果两个 Memory 使用相同的 sessionKey,Intent Classifier 输出的意图分类结果(如 `{"intent": "other_operations"}`)会被存入记忆。当 CRUD Agent 读取记忆时,会读到这个不相关的意图分类结果,导致 CRUD Agent 无法正确理解用户的实际请求。
+> 
+> **解决方案:** 通过添加前缀(`intent-` 或 `crud-`),使得即使在同一个 session 中,两个 Agent 的记忆也完全独立,互不干扰。
+
+### 4.2 子工作流 Memory
+
+#### Simple Memory Sub (子 Agent 记忆)
+
+| 配置项           | 值                                             |
+| ------------- | --------------------------------------------- |
+| 节点类型          | `@n8n/n8n-nodes-langchain.memoryBufferWindow` |
+| 版本            | 1.3                                           |
+| 上下文窗口长度       | 20                                            |
+| Session ID 类型 | customKey                                     |
+| Session Key   | `={{ $json.session_id }}`                     |
+
+**用途:** 使用从主工作流传递的 `session_id` 作为会话标识,保持多轮对话的上下文连续性。
+
+**关键配置:**
+
+```json
+{
+  "contextWindowLength": 20,
+  "sessionIdType": "customKey",
+  "sessionKey": "={{ $json.session_id }}"
+}
+```
+
+---
+
+## 5. HTTP 工具配置
+
+### 5.1 主工作流工具
+
+#### list_datameta (查询元数据列表)
+
+| 配置项          | 值                                                      |
+| ------------ | ------------------------------------------------------ |
+| 方法           | POST                                                   |
+| URL          | `https://company.citupro.com:18183/api/meta/node/list` |
+| Content-Type | application/json                                       |
+
+**工具描述:**
+
+```
+查询元数据列表。可选参数:current(页码,默认1), size(每页数量,默认10), name_zh(中文名筛选), name_en(英文名筛选), category(分类筛选)
+```
+
+**请求体配置 (JSON 表达式):**
+
+```javascript
+={{ JSON.stringify({ 
+  current: $fromAI('current', '页码', 'number') || 1, 
+  size: $fromAI('size', '每页数量', 'number') || 10, 
+  name_zh: $fromAI('name_zh', '中文名筛选', 'string') || '', 
+  name_en: $fromAI('name_en', '英文名筛选', 'string') || '', 
+  category: $fromAI('category', '分类筛选', 'string') || '' 
+}) }}
+```
+
+#### get_datameta (获取元数据详情)
+
+| 配置项          | 值                                                      |
+| ------------ | ------------------------------------------------------ |
+| 方法           | POST                                                   |
+| URL          | `https://company.citupro.com:18183/api/meta/node/edit` |
+| Content-Type | application/json                                       |
+
+**工具描述:**
+
+```
+Get metadata details by ID. Required: id (metadata ID as number).
+```
+
+**请求体参数:**
+| 参数名 | 说明 |
+|--------|------|
+| id | 元数据 ID |
+
+#### update_datameta (更新元数据)
+
+| 配置项          | 值                                                        |
+| ------------ | -------------------------------------------------------- |
+| 方法           | POST                                                     |
+| URL          | `https://company.citupro.com:18183/api/meta/node/update` |
+| Content-Type | application/json                                         |
+
+**工具描述:**
+
+```
+Update existing metadata. Required: id (metadata ID as number). Optional: data_type (new data type like varchar, string, integer).
+```
+
+**请求体参数:**
+| 参数名 | 说明 |
+|--------|------|
+| id | 元数据 ID (必填) |
+| data_type | 新的数据类型 (可选) |
+
+#### delete_datameta (删除元数据)
+
+| 配置项          | 值                                                        |
+| ------------ | -------------------------------------------------------- |
+| 方法           | POST                                                     |
+| URL          | `https://company.citupro.com:18183/api/meta/node/delete` |
+| Content-Type | application/json                                         |
+
+**工具描述:**
+
+```
+Delete metadata by ID. Required: id (metadata ID as number to delete).
+```
+
+**请求体参数:**
+| 参数名 | 说明 |
+|--------|------|
+| id | 要删除的元数据 ID |
+
+#### list_labels (查询标签列表)
+
+| 配置项          | 值                                                           |
+| ------------ | ----------------------------------------------------------- |
+| 方法           | POST                                                        |
+| URL          | `https://company.citupro.com:18183/api/interface/labellist` |
+| Content-Type | application/json                                            |
+
+**工具描述:**
+
+```
+Query available data labels list. Returns label ID, Chinese name, English name.
+```
+
+**请求体参数 (固定值):**
+| 参数名 | 值 |
+|--------|-----|
+| current | 1 |
+| size | 10 |
+
+### 5.2 子工作流工具
+
+#### translate_name (翻译名称)
+
+| 配置项          | 值                                                        |
+| ------------ | -------------------------------------------------------- |
+| 方法           | POST                                                     |
+| URL          | `https://company.citupro.com:18183/api/system/translate` |
+| Content-Type | application/json                                         |
+
+**工具描述:**
+
+```
+Translate Chinese name to English name. Required: node_name (Chinese name to translate). Returns translated English name.
+```
+
+**请求体参数:**
+| 参数名 | 说明 |
+|--------|------|
+| node_name | 要翻译的中文名称 |
+
+#### add_datameta (新增元数据)
+
+| 配置项          | 值                                                     |
+| ------------ | ----------------------------------------------------- |
+| 方法           | POST                                                  |
+| URL          | `https://company.citupro.com:18183/api/meta/node/add` |
+| Content-Type | application/json                                      |
+
+**工具描述:**
+
+```
+Add new metadata. Required: name_zh (Chinese name), data_type. Optional: name_en, status (default true), category (one of: 系统类, 应用类, DataOps), affiliation, describe, tag (array of tag IDs).
+```
+
+**请求体参数:**
+| 参数名 | 必填 | 说明 |
+|--------|------|------|
+| name_zh | ✅ | 中文名称 |
+| data_type | ✅ | 数据类型 |
+| name_en | ❌ | 英文名称 |
+| status | ❌ | 启用状态 (默认 true) |
+| category | ❌ | 分类 (系统类/应用类/DataOps) |
+| affiliation | ❌ | 归属部门 |
+| describe | ❌ | 描述 |
+| tag | ❌ | 标签 ID 数组 |
+
+#### list_labels (查询标签列表 - 子工作流)
+
+| 配置项          | 值                                                           |
+| ------------ | ----------------------------------------------------------- |
+| 方法           | POST                                                        |
+| URL          | `https://company.citupro.com:18183/api/interface/labellist` |
+| Content-Type | application/json                                            |
+
+**工具描述:**
+
+```
+Query available labels list filtered by DataOps category. Returns label ID, Chinese name, English name.
+```
+
+**请求体参数 (固定值):**
+| 参数名 | 值 | 说明 |
+|--------|-----|------|
+| current | 1 | 页码 |
+| size | 50 | 每页数量 |
+| category_filter | DataOps | 分类过滤,只返回 DataOps 类别的标签 |
+
+> **说明**:子工作流中的 `list_labels` 工具添加了 `category_filter` 参数,只返回 DataOps 分类下的标签,便于用户在新增元数据时选择相关标签。
+
+---
+
+## 6. DeepSeek 模型配置
+
+### 6.1 模型实例
+
+| 实例名称                       | 用途                      | Temperature |
+| -------------------------- | ----------------------- | ----------- |
+| DeepSeek Chat Model        | Intent Classifier 意图分类  | 0.3         |
+| DeepSeek Chat Model CRUD   | CRUD Agent 操作执行         | 0.7         |
+| DeepSeek Chat Model (子工作流) | Add Metadata Agent 新增流程 | 0.7         |
+
+### 6.2 凭证配置
+
+```json
+{
+  "credentials": {
+    "deepSeekApi": {
+      "id": "N8PgEL20Nk88KXMp",
+      "name": "DeepSeek account"
+    }
+  }
+}
+```
+
+---
+
+## 7. 工作流设置
+
+### 7.1 主工作流设置
+
+```json
+{
+  "executionOrder": "v1",
+  "binaryMode": "separate",
+  "availableInMCP": false,
+  "callerPolicy": "workflowsFromSameOwner"
+}
+```
+
+### 7.2 子工作流设置
+
+```json
+{
+  "executionOrder": "v1",
+  "saveDataErrorExecution": "all",
+  "saveDataSuccessExecution": "all",
+  "saveManualExecutions": true,
+  "saveExecutionProgress": true,
+  "callerPolicy": "any",
+  "availableInMCP": false
+}
+```
+
+**关键配置说明:**
+
+- `callerPolicy: "any"` - 允许任何工作流调用此子工作流
+
+---
+
+## 8. 连接关系图
+
+### 8.1 主工作流连接
+
+```mermaid
+graph LR
+    subgraph Trigger["触发层"]
+        CT["Chat Trigger"]
+    end
+
+    subgraph Intent["意图识别层"]
+        IC["Intent Classifier"]
+        DSM1["DeepSeek Model"]
+        SMI["Simple Memory Intent"]
+    end
+
+    subgraph Routing["路由层"]
+        SW["Intent Switch"]
+    end
+
+    subgraph DataPrep["数据准备层"]
+        PCI["Prepare CRUD Input"]
+    end
+
+    subgraph Execution["执行层"]
+        EW["Execute Add Metadata Workflow"]
+        CA["CRUD Agent"]
+        DSM2["DeepSeek Model CRUD"]
+        SMC["Simple Memory CRUD"]
+    end
+
+    subgraph Tools["工具层"]
+        T1["list_datameta"]
+        T2["get_datameta"]
+        T3["update_datameta"]
+        T4["delete_datameta"]
+        T5["list_labels"]
+    end
+
+    CT -->|main| IC
+    DSM1 -->|ai_languageModel| IC
+    SMI -->|ai_memory| IC
+    IC -->|main| SW
+    SW -->|"output[0]"| EW
+    SW -->|"output[1]"| PCI
+    SW -->|"output[2]"| PCI
+    PCI -->|main| CA
+    DSM2 -->|ai_languageModel| CA
+    SMC -->|ai_memory| CA
+    T1 -->|ai_tool| CA
+    T2 -->|ai_tool| CA
+    T3 -->|ai_tool| CA
+    T4 -->|ai_tool| CA
+    T5 -->|ai_tool| CA
+```
+
+### 8.2 子工作流连接
+
+```mermaid
+graph LR
+    subgraph Trigger["触发层"]
+        EWT["Execute Workflow Trigger"]
+    end
+
+    subgraph Agent["Agent 层"]
+        AMA["Add Metadata Agent"]
+        DSM["DeepSeek Model"]
+        SMS["Simple Memory Sub"]
+    end
+
+    subgraph Tools["工具层"]
+        ST1["translate_name"]
+        ST2["add_datameta"]
+        ST3["list_labels"]
+        ST4["get_datameta"]
+    end
+
+    EWT -->|main| AMA
+    DSM -->|ai_languageModel| AMA
+    SMS -->|ai_memory| AMA
+    ST1 -->|ai_tool| AMA
+    ST2 -->|ai_tool| AMA
+    ST3 -->|ai_tool| AMA
+    ST4 -->|ai_tool| AMA
+```
+
+---
+
+## 9. API 端点汇总
+
+| 端点                         | 方法   | 用途      |
+| -------------------------- | ---- | ------- |
+| `/api/meta/node/list`      | POST | 查询元数据列表 |
+| `/api/meta/node/edit`      | POST | 获取元数据详情 |
+| `/api/meta/node/add`       | POST | 新增元数据   |
+| `/api/meta/node/update`    | POST | 更新元数据   |
+| `/api/meta/node/delete`    | POST | 删除元数据   |
+| `/api/interface/labellist` | POST | 获取标签列表  |
+| `/api/system/translate`    | POST | 翻译中文名称  |
+
+---
+
+## 10. 测试验证
+
+### 10.1 功能测试结果
+
+| 测试场景  | 输入示例                    | 期望路由         | 测试结果 |
+| ----- | ----------------------- | ------------ | ---- |
+| 新增元数据 | "我想新增一个元数据"             | → 子工作流       | ✅ 通过 |
+| 查询列表  | "查看所有元数据"               | → CRUD Agent | ✅ 通过 |
+| 查看详情  | "查看ID为2158的元数据"         | → CRUD Agent | ✅ 通过 |
+| 更新元数据 | "把ID为2158的数据类型改成bigint" | → CRUD Agent | ✅ 通过 |
+| 删除元数据 | "删除ID为2325的元数据"         | → CRUD Agent | ✅ 通过 |
+| 查看标签  | "查看可用的标签列表"             | → CRUD Agent | ✅ 通过 |
+
+### 10.2 多轮对话测试
+
+| 步骤  | 用户输入                 | 系统响应           | 状态  |
+| --- | -------------------- | -------------- | --- |
+| 1   | "我想新增一个元数据,中文名叫订单状态" | 自动翻译英文名,询问数据类型 | ✅   |
+| 2   | "varchar"            | 记录,询问是否启用      | ✅   |
+| 3   | "启用"                 | 记录,询问分类        | ✅   |
+| 4   | "应用类"                | 记录,询问归属        | ✅   |
+| 5   | "跳过"                 | 询问描述           | ✅   |
+| 6   | "用于记录订单的当前状态"        | 展示标签列表,询问选择    | ✅   |
+| 7   | "跳过"                 | 展示信息汇总,请求确认    | ✅   |
+| 8   | "确认,提交创建"            | 创建成功,返回 ID     | ✅   |
+
+---
+
+## 11. 注意事项
+
+### 11.1 架构设计要点
+
+1. **意图分类与路由分离**:使用 Switch 节点进行明确的路由控制,而非依赖 AI Agent 自行选择工具
+2. **子工作流独立性**:新增元数据流程封装在独立的子工作流中,便于维护和扩展
+3. **会话状态管理**:通过 `session_id` 在主工作流和子工作流之间传递会话标识,保持多轮对话的连续性
+
+### 11.2 配置注意事项
+
+1. **子工作流必须激活**:被 Execute Workflow 调用的子工作流需要处于激活状态
+2. **callerPolicy 设置**:子工作流的 `callerPolicy` 需设置为 `"any"` 以允许外部调用
+3. **Memory Session Key**:子工作流的 Simple Memory 必须使用 `customKey` 类型,并绑定传入的 `session_id`
+
+### 11.3 扩展建议
+
+1. 可以添加更多的意图分类类型,如"批量操作"、"数据导出"等
+2. 可以为其他复杂操作创建独立的子工作流
+3. 可以添加用户权限验证节点
+
+---
+
+## 12. 版本信息
+
+| 项目       | 版本                                         |
+| -------- | ------------------------------------------ |
+| 文档版本     | 1.1                                        |
+| 创建日期     | 2026-02-05                                 |
+| 最后更新     | 2026-02-05                                 |
+| 主工作流版本   | 159+                                       |
+| 子工作流版本   | 18                                         |
+| n8n 节点版本 | Agent 1.7, Switch 3.4, Memory 1.3, Set 3.4 |
+
+### 12.1 更新日志
+
+**v1.1 (2026-02-05)**
+
+- 新增 `Prepare CRUD Input` 节点,解决 CRUD Agent 接收到 Intent Classifier 输出而非用户原始输入的问题
+- 修改 Simple Memory 的 sessionKey 配置,添加 `intent-` 和 `crud-` 前缀以隔离不同 Agent 的对话记忆
+- 更新架构图和连接图以反映新的节点结构
+
+**v1.0 (2026-02-05)**
+
+- 初始版本发布

+ 0 - 154
docs/PYTHON38_COMPATIBILITY.md

@@ -1,154 +0,0 @@
-# Python 3.8 兼容性说明
-
-## 概述
-
-DataOps Platform 的时区功能已兼容 Python 3.8 和 Python 3.9+。
-
-## 时区模块兼容性
-
-### 代码实现
-
-`app/core/common/timezone_utils.py` 使用了兼容性导入:
-
-```python
-try:
-    # Python 3.9+
-    from zoneinfo import ZoneInfo
-except ImportError:
-    # Python 3.8 使用 backports
-    from backports.zoneinfo import ZoneInfo
-```
-
-### 依赖要求
-
-#### Python 3.8 环境
-
-1. **必须安装**: `backports.zoneinfo`
-   ```bash
-   pip install backports.zoneinfo
-   ```
-
-2. **系统依赖**: `tzdata`
-   ```bash
-   sudo apt-get update
-   sudo apt-get install -y tzdata
-   ```
-
-#### Python 3.9+ 环境
-
-1. **标准库**: 内置 `zoneinfo`,无需额外安装
-2. **系统依赖**: `tzdata`
-   ```bash
-   sudo apt-get update
-   sudo apt-get install -y tzdata
-   ```
-
-## 生产环境配置
-
-### 当前生产环境
-
-- **Python 版本**: 3.8
-- **已安装**: `backports.zoneinfo`
-- **需要**: 确保系统已安装 `tzdata`
-
-### 验证安装
-
-```bash
-# 进入虚拟环境
-cd /opt/dataops-platform
-source venv/bin/activate
-
-# 测试时区模块
-python -c "
-try:
-    from zoneinfo import ZoneInfo
-    print('使用标准库 zoneinfo (Python 3.9+)')
-except ImportError:
-    from backports.zoneinfo import ZoneInfo
-    print('使用 backports.zoneinfo (Python 3.8)')
-
-from datetime import datetime
-tz = ZoneInfo('Asia/Shanghai')
-now = datetime.now(tz)
-print(f'当前东八区时间: {now}')
-"
-```
-
-预期输出:
-```
-使用 backports.zoneinfo (Python 3.8)
-当前东八区时间: 2026-01-12 18:30:45.123456+08:00
-```
-
-## requirements.txt 更新
-
-确保 `requirements.txt` 包含以下内容:
-
-```txt
-# Python 3.8 时区支持
-backports.zoneinfo>=0.2.1; python_version < "3.9"
-```
-
-这样可以:
-- Python 3.8 环境自动安装 `backports.zoneinfo`
-- Python 3.9+ 环境跳过安装(使用标准库)
-
-## 常见问题
-
-### Q1: ModuleNotFoundError: No module named 'zoneinfo'
-
-**原因**: Python 3.8 环境未安装 `backports.zoneinfo`
-
-**解决**:
-```bash
-pip install backports.zoneinfo
-```
-
-### Q2: ZoneInfoNotFoundError: 'No time zone found with key Asia/Shanghai'
-
-**原因**: 系统缺少时区数据
-
-**解决**:
-```bash
-sudo apt-get update
-sudo apt-get install -y tzdata
-```
-
-### Q3: 如何确认当前使用的是哪个模块?
-
-**测试代码**:
-```python
-import sys
-try:
-    from zoneinfo import ZoneInfo
-    print(f"使用标准库 zoneinfo: {sys.modules['zoneinfo']}")
-except ImportError:
-    from backports.zoneinfo import ZoneInfo
-    print(f"使用 backports.zoneinfo: {sys.modules['backports.zoneinfo']}")
-```
-
-## 升级路径
-
-### 从 Python 3.8 升级到 3.9+
-
-当升级 Python 版本时:
-
-1. **无需修改代码**: 兼容性导入会自动使用标准库
-2. **可选清理**: 卸载 `backports.zoneinfo`
-   ```bash
-   pip uninstall backports.zoneinfo
-   ```
-3. **验证**: 运行测试确保时区功能正常
-
-## 性能考虑
-
-- `backports.zoneinfo` 和标准库 `zoneinfo` 性能基本相同
-- 两者都使用系统的 IANA 时区数据库
-- 升级到 Python 3.9+ 后无性能差异
-
-## 相关文件
-
-- 时区工具模块: `app/core/common/timezone_utils.py`
-- 修复脚本: `scripts/fix_startup.sh`
-- 故障排查: `scripts/TROUBLESHOOTING.md`
-- 快速修复: `QUICK_FIX.md`

+ 0 - 123
docs/QUICK_FIX.md

@@ -1,123 +0,0 @@
-# 🚨 DataOps Platform 启动失败快速修复
-
-## 问题现象
-
-```bash
-[ERROR] dataops-platform 重启失败!
-tail: cannot open '/opt/dataops-platform/logs/gunicorn_error.log' for reading: No such file or directory
-```
-
-## 🔧 快速修复(3 步)
-
-### 在服务器上执行以下命令:
-
-```bash
-# 1. 上传最新代码到服务器
-# (在本地执行,或使用 git pull)
-
-# 2. 进入脚本目录
-cd /opt/dataops-platform/scripts
-
-# 3. 运行自动修复脚本
-sudo chmod +x fix_startup.sh
-sudo ./fix_startup.sh
-```
-
-## ✅ 如果修复成功
-
-你会看到:
-```
-✓ dataops-platform 启动成功!
-✓ 健康检查通过! HTTP 状态码: 200
-访问地址: http://localhost:5500
-```
-
-## ❌ 如果修复失败
-
-### 方案 A: 查看详细诊断
-
-```bash
-cd /opt/dataops-platform/scripts
-sudo chmod +x diagnose_issue.sh
-sudo ./diagnose_issue.sh
-```
-
-### 方案 B: 查看错误日志
-
-```bash
-# 最重要的日志(通常能看到真正的错误)
-sudo tail -50 /var/log/supervisor/dataops-platform-stderr.log
-```
-
-### 方案 C: 手动修复核心问题
-
-最可能的原因是缺少时区数据(因为我们刚添加了 `zoneinfo` 模块):
-
-```bash
-# 安装时区数据
-sudo apt-get update
-sudo apt-get install -y tzdata
-
-# 创建日志目录
-sudo mkdir -p /opt/dataops-platform/logs
-sudo chown ubuntu:ubuntu /opt/dataops-platform/logs
-
-# 重启服务
-sudo supervisorctl restart dataops-platform
-
-# 查看状态
-sudo supervisorctl status dataops-platform
-```
-
-## 📋 验证修复
-
-```bash
-# 1. 检查服务状态(应该显示 RUNNING)
-sudo supervisorctl status dataops-platform
-
-# 2. 测试健康检查接口
-curl http://localhost:5500/api/system/health
-
-# 3. 测试时区功能
-cd /opt/dataops-platform
-source venv/bin/activate
-python -c "from app.core.common.timezone_utils import now_china_naive; print('当前东八区时间:', now_china_naive())"
-```
-
-## 🔍 根本原因
-
-我们最近的代码修改引入了 `zoneinfo` 模块用于处理东八区时间。
-
-**重要**: 
-- Python 3.9+ 使用标准库 `zoneinfo`
-- Python 3.8 需要使用 `backports.zoneinfo` 包
-- 两者都需要系统安装 `tzdata` 包
-
-修改的文件:
-- `app/core/common/timezone_utils.py` - 新增的时区工具模块(已兼容 Python 3.8)
-- `app/models/data_product.py` - 使用东八区时间
-- `app/models/metadata_review.py` - 使用东八区时间
-- 其他服务层文件
-
-## 📞 需要帮助?
-
-如果以上方法都无法解决,请提供:
-
-1. 诊断输出:
-   ```bash
-   cd /opt/dataops-platform/scripts
-   sudo ./diagnose_issue.sh > ~/diagnosis.log 2>&1
-   cat ~/diagnosis.log
-   ```
-
-2. 错误日志:
-   ```bash
-   sudo tail -100 /var/log/supervisor/dataops-platform-stderr.log
-   ```
-
-## 📚 详细文档
-
-查看完整的故障排查指南:
-```bash
-cat /opt/dataops-platform/scripts/TROUBLESHOOTING.md
-```

+ 0 - 166
docs/QUICK_START_N8N_TOOLS.md

@@ -1,166 +0,0 @@
-# 🚀 Data-governance 工作流配置快速开始
-
-**⏱️ 预计时间**: 15-20 分钟  
-**🎯 目标**: 完成元数据管理工作流的 Tools 配置
-
----
-
-## ✅ 前置条件检查
-
-在开始之前,确认:
-- [x] 已创建 Data-governance 工作流(ID: `tWfjLZE1FmMfQAIn`)
-- [x] 后端 API 接口已部署(192.168.3.143:5000)
-- [x] AI Agent 系统消息已更新
-- [x] 可以访问 n8n 界面(https://n8n.citupro.com)
-
----
-
-## 📝 3 步完成配置
-
-### 步骤 1: 登录并打开工作流
-
-1. 访问 https://n8n.citupro.com
-2. 登录账号
-3. 进入 Workflows → 找到 "Data-governance" → 点击打开
-
-### 步骤 2: 添加检查元数据工具
-
-#### 2.1 添加节点
-- 点击 "+" → 搜索 "HTTP Request Tool" → 选择(LangChain分类下的)
-
-#### 2.2 配置
-```
-【基本信息】
-Name: check_metadata
-Description: 检查元数据中文名是否已经存在。需要参数:name_zh(元数据中文名)。返回exists字段表示是否存在(true/false)
-
-【HTTP配置】
-Method: GET
-URL: http://192.168.3.143:5000/api/meta/check?name_zh={{ $parameter.name_zh }}
-Authentication: None
-
-【参数定义】
-点击 "Add Placeholder" 添加:
-- Name: name_zh
-- Description: 元数据中文名
-```
-
-#### 2.3 连接
-- 从该节点拖线到 "AI Agent"
-- 选择连接类型:**ai_tool** (重要!)
-
----
-
-### 步骤 3: 添加创建元数据工具
-
-#### 3.1 添加节点
-- 再次点击 "+" → 搜索 "HTTP Request Tool" → 选择
-
-#### 3.2 配置
-```
-【基本信息】
-Name: create_metadata
-Description: 创建新的元数据。需要参数:name_zh(中文名,必填), data_type(数据类型,默认string), description(描述信息,选填)。返回创建结果
-
-【HTTP配置】
-Method: POST
-URL: http://192.168.3.143:5000/api/meta/node/add
-Authentication: None
-Send Body: ✅ 启用
-Body Content Type: JSON
-
-【JSON Body】(重要:复制粘贴)
-{
-  "name_zh": "={{ $parameter.name_zh }}",
-  "data_type": "={{ $parameter.data_type || 'string' }}",
-  "describe": "={{ $parameter.description || '' }}",
-  "source": "data-governance-workflow",
-  "status": true
-}
-
-【参数定义】
-点击 "Add Placeholder" 三次添加:
-1. Name: name_zh, Description: 元数据中文名(必填)
-2. Name: data_type, Description: 数据类型(string/int/float等,默认string)
-3. Name: description, Description: 描述信息(选填)
-```
-
-#### 3.3 连接
-- 从该节点拖线到 "AI Agent"
-- 选择连接类型:**ai_tool**
-
----
-
-### 完成!保存并激活
-
-1. 点击右上角 "Save" 保存
-2. 确认 "Active" 开关为开启状态
-3. 完成!🎉
-
----
-
-## 🧪 立即测试
-
-### 测试 URL
-```
-https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-```
-
-### 测试对话
-```
-用户: "是,我要创建元数据"
-AI: (询问元数据信息)
-
-用户: "中文名:测试字段001,类型:string,描述:这是测试"
-AI: (检查→创建→返回成功)
-```
-
-**预期结果**: 
-- ✅ AI 询问信息
-- ✅ AI 检查是否存在
-- ✅ AI 创建元数据
-- ✅ 返回成功消息
-
----
-
-## ❓ 遇到问题?
-
-### 问题 1: AI 不调用工具
-**解决**: 
-- 检查 Tool 是否通过 `ai_tool` 连接(不是 main)
-- 检查 Description 是否清晰
-
-### 问题 2: API 调用失败
-**解决**:
-- 测试 URL 是否可访问:`http://192.168.3.143:5000/api/meta/check?name_zh=test`
-- 检查网络连接
-
-### 问题 3: 参数未传递
-**解决**:
-- 检查 Placeholder Definitions 是否正确定义
-- 检查 JSON Body 中的表达式语法
-
----
-
-## 📚 详细文档
-
-需要更多信息?查看:
-- **完整配置指南**: `docs/n8n_add_tools_guide.md`
-- **设计方案**: `docs/n8n_improved_workflow_design.md`
-- **故障排除**: `docs/n8n_chat_trigger_error_diagnosis.md`
-- **工作总结**: `docs/n8n_workflow_enhancement_summary.md`
-
----
-
-## ✅ 配置检查清单
-
-完成后确认:
-- [ ] 添加了 2 个 HTTP Request Tool 节点
-- [ ] check_metadata 配置正确
-- [ ] create_metadata 配置正确
-- [ ] 两个 Tool 都通过 ai_tool 连接到 AI Agent
-- [ ] 工作流已保存并激活
-- [ ] 测试通过
-
-**全部完成?恭喜!您的数据治理工作流已准备就绪!** 🎉
-

+ 0 - 131
docs/README.md

@@ -1,131 +0,0 @@
-# Python项目模板
-
-这是一个现代化的Python项目模板,使用FastAPI框架构建。
-
-## 项目特性
-
-- 🚀 **FastAPI**: 现代、快速的Web框架
-- 🗄️ **SQLAlchemy**: 强大的ORM框架
-- 🔧 **Alembic**: 数据库迁移工具
-- 📊 **Pandas & NumPy**: 数据处理和分析
-- 🧪 **Pytest**: 测试框架
-- 🎨 **Black**: 代码格式化
-- 📝 **Loguru**: 现代化日志系统
-
-## 快速开始
-
-### 1. 安装依赖
-
-```bash
-# 创建虚拟环境
-python -m venv venv
-
-# 激活虚拟环境 (Windows)
-venv\Scripts\activate
-
-# 激活虚拟环境 (Linux/Mac)
-source venv/bin/activate
-
-# 安装依赖
-pip install -r requirements.txt
-```
-
-### 2. 环境配置
-
-复制 `.env.example` 文件为 `.env` 并配置环境变量:
-
-```bash
-cp .env.example .env
-```
-
-### 3. 运行项目
-
-```bash
-# 开发模式
-uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
-
-# 生产模式
-uvicorn app.main:app --host 0.0.0.0 --port 8000
-```
-
-### 4. 访问API文档
-
-- Swagger UI: http://localhost:8000/docs
-- ReDoc: http://localhost:8000/redoc
-
-## 项目结构
-
-```
-new-python-project/
-├── app/                    # 应用主目录
-│   ├── __init__.py
-│   ├── main.py            # 应用入口
-│   ├── api/               # API路由
-│   ├── core/              # 核心配置
-│   ├── models/            # 数据模型
-│   ├── schemas/           # Pydantic模式
-│   ├── services/          # 业务逻辑
-│   └── utils/             # 工具函数
-├── tests/                 # 测试文件
-├── alembic/               # 数据库迁移
-├── .env.example           # 环境变量示例
-├── requirements.txt        # 项目依赖
-└── README.md              # 项目说明
-```
-
-## 开发指南
-
-### 代码格式化
-
-```bash
-# 格式化代码
-black .
-
-# 检查代码风格
-flake8 .
-
-# 类型检查
-mypy .
-```
-
-### 运行测试
-
-```bash
-# 运行所有测试
-pytest
-
-# 运行特定测试文件
-pytest tests/test_api.py
-
-# 生成覆盖率报告
-pytest --cov=app tests/
-```
-
-## 数据库操作
-
-### 创建迁移
-
-```bash
-alembic revision --autogenerate -m "描述变更"
-```
-
-### 应用迁移
-
-```bash
-alembic upgrade head
-```
-
-## 贡献指南
-
-1. Fork 项目
-2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
-3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
-4. 推送到分支 (`git push origin feature/AmazingFeature`)
-5. 打开 Pull Request
-
-## 许可证
-
-本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
-
-
-

+ 0 - 239
docs/RELEASE_NOTES.md

@@ -1,239 +0,0 @@
-# Release Notes - 时区修正版本
-
-## 版本信息
-
-- **版本号**: v1.0-timezone-fix
-- **发布日期**: 2026-01-12
-- **兼容性**: Python 3.8+
-
-## 🎯 主要变更
-
-### 时区统一修正
-
-将所有时间字段从 UTC 时间改为东八区(Asia/Shanghai)时间,确保时间显示符合中国用户习惯。
-
-#### 影响的数据表
-
-| 表名 | 修正的字段 |
-|-----|-----------|
-| `data_orders` | `created_at`, `updated_at`, `processed_at` |
-| `data_products` | `created_at`, `updated_at`, `last_updated_at`, `last_viewed_at` |
-| `metadata_review_records` | `created_at`, `updated_at`, `resolved_at` |
-| `metadata_version_history` | `created_at` |
-
-## ✨ 新增功能
-
-### 1. 时区工具模块
-
-新增 `app/core/common/timezone_utils.py`,提供统一的时区处理:
-
-- `now_china()` - 获取当前东八区时间(带时区信息)
-- `now_china_naive()` - 获取当前东八区时间(用于数据库存储)
-- `to_china_time()` - 转换任意时区到东八区
-- `utc_to_china_naive()` - UTC 转东八区(不带时区信息)
-
-### 2. 自动化运维脚本
-
-新增多个运维脚本,简化部署和故障排查:
-
-- `scripts/fix_startup.sh` - 自动修复启动问题
-- `scripts/diagnose_issue.sh` - 全面的问题诊断
-- `scripts/TROUBLESHOOTING.md` - 详细的故障排查指南
-
-### 3. 完整的文档
-
-- `QUICK_FIX.md` - 快速修复指南
-- `PYTHON38_COMPATIBILITY.md` - Python 3.8 兼容性说明
-- `DEPLOYMENT_GUIDE.md` - 完整部署指南
-- `docs/timezone_fix_summary.md` - 技术实现总结
-
-## 🔧 技术改进
-
-### Python 版本兼容性
-
-代码已完全兼容 Python 3.8 和 3.9+:
-
-```python
-try:
-    from zoneinfo import ZoneInfo  # Python 3.9+
-except ImportError:
-    from backports.zoneinfo import ZoneInfo  # Python 3.8
-```
-
-### 代码质量提升
-
-- ✅ 类型注解优化:`Optional[X]` → `X | None`
-- ✅ 异常处理优化:`try-except-pass` → `contextlib.suppress()`
-- ✅ 集合创建优化:`set(generator)` → `{set comprehension}`
-- ✅ 通过所有 linter 检查
-
-## 📝 修改的文件
-
-### 核心代码(6 个文件)
-
-1. `app/core/common/timezone_utils.py` - **新建**
-2. `app/models/data_product.py` - 修改 DataProduct 和 DataOrder 模型
-3. `app/models/metadata_review.py` - 修改两个模型和一个函数
-4. `app/core/data_service/data_product_service.py` - 修改 9 处时间处理
-5. `app/core/meta_data/redundancy_check.py` - 修改时间处理 + 代码优化
-6. `app/core/business_domain/business_domain.py` - 修改时间处理
-
-### 运维脚本(3 个文件)
-
-1. `scripts/fix_startup.sh` - **新建**
-2. `scripts/diagnose_issue.sh` - **新建**
-3. `scripts/TROUBLESHOOTING.md` - **新建**
-
-### 文档(5 个文件)
-
-1. `QUICK_FIX.md` - **新建**
-2. `PYTHON38_COMPATIBILITY.md` - **新建**
-3. `DEPLOYMENT_GUIDE.md` - **新建**
-4. `RELEASE_NOTES.md` - **新建**(本文件)
-5. `docs/timezone_fix_summary.md` - **新建**
-
-## ⚙️ 部署要求
-
-### 系统依赖
-
-```bash
-sudo apt-get install -y tzdata
-```
-
-### Python 依赖
-
-**Python 3.8**:
-```bash
-pip install backports.zoneinfo
-```
-
-**Python 3.9+**:
-无需额外安装(使用标准库)
-
-## 🚀 部署步骤
-
-### 快速部署(推荐)
-
-```bash
-# 1. 上传代码
-cd /opt/dataops-platform
-git pull origin main
-
-# 2. 运行自动修复脚本
-cd scripts
-sudo chmod +x fix_startup.sh
-sudo ./fix_startup.sh
-
-# 3. 验证部署
-sudo supervisorctl status dataops-platform
-curl http://localhost:5500/api/system/health
-```
-
-详细步骤请参考 `DEPLOYMENT_GUIDE.md`
-
-## ⚠️ 重要提示
-
-### 已有数据
-
-数据库中已存在的记录可能使用 UTC 时间。新旧数据会混合存在。
-
-**可选的数据迁移**(将历史数据统一为东八区时间):
-
-```sql
--- 将 UTC 时间转换为东八区时间(+8小时)
-UPDATE data_orders 
-SET created_at = created_at + INTERVAL '8 hours',
-    updated_at = updated_at + INTERVAL '8 hours',
-    processed_at = processed_at + INTERVAL '8 hours'
-WHERE created_at < '2026-01-12 00:00:00';
-```
-
-完整的迁移 SQL 请参考 `docs/timezone_fix_summary.md`
-
-### API 变更
-
-所有 API 返回的时间字段现在表示**东八区时间**,而不是 UTC 时间。
-
-**示例**:
-```json
-{
-  "created_at": "2026-01-12T18:30:45",  // 东八区时间
-  "updated_at": "2026-01-12T18:30:45"   // 东八区时间
-}
-```
-
-## 🐛 已知问题
-
-无
-
-## 🔄 回滚方案
-
-如果需要回滚:
-
-```bash
-cd /opt/dataops-platform
-git reset --hard HEAD~1
-sudo supervisorctl restart dataops-platform
-```
-
-## 📊 测试建议
-
-部署后请验证以下功能:
-
-1. ✅ 创建新数据订单,检查 `created_at` 是否为东八区当前时间
-2. ✅ 更新订单状态,检查 `updated_at` 和 `processed_at`
-3. ✅ 注册数据产品,检查时间字段
-4. ✅ 查看数据产品,检查 `last_viewed_at` 更新
-5. ✅ 创建元数据审核记录,检查时间字段
-
-## 📞 支持
-
-### 快速修复
-
-遇到问题?运行快速修复脚本:
-
-```bash
-cd /opt/dataops-platform/scripts
-sudo ./fix_startup.sh
-```
-
-### 问题诊断
-
-运行诊断脚本获取详细信息:
-
-```bash
-cd /opt/dataops-platform/scripts
-sudo ./diagnose_issue.sh
-```
-
-### 查看日志
-
-```bash
-# 最重要的日志
-sudo tail -f /var/log/supervisor/dataops-platform-stderr.log
-```
-
-### 文档索引
-
-- 🚀 **快速开始**: `QUICK_FIX.md`
-- 📖 **部署指南**: `DEPLOYMENT_GUIDE.md`
-- 🐍 **Python 3.8**: `PYTHON38_COMPATIBILITY.md`
-- 🔧 **故障排查**: `scripts/TROUBLESHOOTING.md`
-- 📚 **技术细节**: `docs/timezone_fix_summary.md`
-
-## 👥 贡献者
-
-- AI Assistant - 时区修正实现
-- 项目团队 - 需求提出和测试
-
-## 📅 下一步计划
-
-- [ ] 监控生产环境运行情况
-- [ ] 收集用户反馈
-- [ ] 考虑是否需要迁移历史数据
-
----
-
-**发布人**: AI Assistant  
-**发布日期**: 2026-01-12  
-**文档版本**: 1.0

+ 0 - 248
docs/TASK_EXECUTION_QUICK_START.md

@@ -1,248 +0,0 @@
-# 任务自动执行 - 快速开始指南
-
-## 🎯 问题与解决方案
-
-### 问题诊断
-
-您遇到的问题:
-- ✅ task-manager MCP已读取任务
-- ✅ 任务状态已改为processing
-- ❌ **但Cursor没有收到任务指令,没有开始执行**
-
-### 根本原因
-
-**MCP协议是被动的**:
-- MCP工具必须被主动调用才会执行
-- 即使MCP读取了任务,也只是返回文本结果
-- **不会自动触发Cursor执行任何操作**
-
-### 解决方案
-
-我们提供了3种方式让Cursor自动感知并执行任务。
-
----
-
-## 🚀 方式1:最简单 - 直接在Cursor中说(推荐)
-
-在Cursor Chat中输入:
-
-```
-请检查并执行所有pending任务
-```
-
-或者:
-
-```
-@task-manager 执行所有pending任务
-```
-
-Cursor会自动:
-1. 调用`get_pending_tasks`获取任务
-2. 对每个任务调用`execute_task`
-3. 根据任务描述生成Python代码
-4. 自动更新任务状态为completed
-
-**优点**:
-- ✅ 最简单,无需配置
-- ✅ 立即执行
-- ✅ 适合临时使用
-
-**缺点**:
-- ❌ 需要手动触发
-
----
-
-## 🤖 方式2:自动化 - Python自动执行脚本(推荐生产环境)
-
-### 2.1 执行一次检查
-
-```bash
-python scripts/auto_execute_tasks.py --once
-```
-
-### 2.2 持续监控(每5分钟检查一次)
-
-```bash
-python scripts/auto_execute_tasks.py --interval 300
-```
-
-### 2.3 在后台运行
-
-**Windows(PowerShell):**
-```powershell
-Start-Process python -ArgumentList "scripts/auto_execute_tasks.py" -WindowStyle Hidden
-```
-
-**Linux/Mac:**
-```bash
-nohup python scripts/auto_execute_tasks.py > logs/auto_execute.log 2>&1 &
-```
-
-**优点**:
-- ✅ 真正的自动化
-- ✅ 无需人工干预
-- ✅ 适合生产环境
-
-**缺点**:
-- ❌ 需要运行额外的Python进程
-- ❌ 需要安装psycopg2:`pip install psycopg2-binary`
-
----
-
-## 📋 方式3:任务提示 - Cursor Agent(友好提示)
-
-### 执行一次
-
-```bash
-python scripts/cursor_task_agent.py --once
-```
-
-### 守护进程模式
-
-```bash
-python scripts/cursor_task_agent.py --daemon --interval 300
-```
-
-这个脚本会:
-1. 从数据库读取pending任务
-2. 为每个任务创建Markdown提示文件
-3. 文件保存在`.cursor/task_prompts/`目录
-4. 用户打开提示文件可以看到任务详情
-
-**优点**:
-- ✅ 友好的用户界面
-- ✅ 任务信息可视化
-
-**缺点**:
-- ❌ 不是完全自动化
-- ❌ 用户仍需手动执行
-
----
-
-## 📚 完整文档
-
-详细说明请参阅:[docs/CURSOR_AUTO_TASK_EXECUTION.md](./CURSOR_AUTO_TASK_EXECUTION.md)
-
----
-
-## ✅ 当前状态
-
-### 已完成
-
-1. ✅ **分析问题**:MCP与Cursor的互动机制
-2. ✅ **创建脚本**:
-   - `scripts/auto_execute_tasks.py` - 自动执行脚本
-   - `scripts/cursor_task_agent.py` - 任务提示脚本
-3. ✅ **完成文档**:
-   - `docs/CURSOR_AUTO_TASK_EXECUTION.md` - 完整指南
-   - `docs/TASK_EXECUTION_QUICK_START.md` - 快速开始
-4. ✅ **测试验证**:成功执行task_id=8的任务
-
-### 测试结果
-
-**任务8执行情况**:
-- 任务ID:8
-- 任务名称:从数据源中导入科室对照表
-- 状态:✅ completed
-- 生成文件:
-  - `app/core/data_flow/import_dept_mapping.py` - 导入脚本
-  - `app/core/data_flow/import_dept_config.json` - 配置文件
-
----
-
-## 🎉 立即开始
-
-### 对于当前pending任务
-
-在Cursor Chat中输入:
-```
-请检查并执行所有pending任务
-```
-
-### 设置自动化(推荐)
-
-1. 打开新终端
-2. 运行:
-```bash
-python scripts/auto_execute_tasks.py
-```
-
-这个脚本会每5分钟自动检查新任务并执行它们。
-
----
-
-## 💡 使用建议
-
-### 开发环境
-- 使用**方式1**(直接在Cursor中说)
-- 简单快速,适合调试
-
-### 生产环境
-- 使用**方式2**(自动执行脚本)
-- 在后台运行,完全自动化
-- 建议设置为系统服务
-
-### 团队协作
-- 使用**方式3**(任务提示)+ **方式1**
-- Agent创建任务提示
-- 开发者看到提示后在Cursor中执行
-
----
-
-## 🔧 故障排查
-
-### 问题:脚本报错"ModuleNotFoundError"
-
-**解决**:
-```bash
-pip install psycopg2-binary
-```
-
-### 问题:无法连接数据库
-
-**检查**:
-1. PostgreSQL服务是否运行
-2. `mcp-servers/task-manager/config.json` 中的数据库URI是否正确
-3. 网络连接是否正常
-
-### 问题:任务一直是processing状态
-
-**原因**:任务被执行但没有调用`update_task_status`更新状态
-
-**解决**:
-1. 手动更新任务状态(在Cursor中):
-```
-调用工具: update_task_status
-参数: {
-  "task_id": <任务ID>,
-  "status": "completed",
-  "code_name": "<文件名>.py",
-  "code_path": "app/core/data_flow"
-}
-```
-
-2. 或重置任务为pending(在数据库中):
-```sql
-UPDATE task_list 
-SET status = 'pending' 
-WHERE task_id = <任务ID>;
-```
-
----
-
-## 📞 获取帮助
-
-如有问题,请查看:
-- 完整文档:`docs/CURSOR_AUTO_TASK_EXECUTION.md`
-- 日志文件:`logs/cursor_task_agent.log` 或 `logs/auto_execute.log`
-- Task Manager README:`mcp-servers/task-manager/README.md`
-
----
-
-**祝您使用愉快!🚀**
-
-
-
-
-
-

+ 0 - 522
docs/Task_Manager_MCP_说明.md

@@ -1,522 +0,0 @@
-# Task Manager MCP 工作流程说明
-
-## 问题诊断
-
-### 现象
-Task Manager MCP 能够读取到任务,但是没有自动生成对应的代码。
-
-### 原因分析
-
-Task Manager MCP 的工作流程分为以下步骤:
-
-1. **任务读取** ✅ - MCP 从数据库读取任务
-2. **返回执行指令** ✅ - MCP 返回任务描述和执行指令
-3. **AI 生成代码** ❌ - **这一步需要 AI 主动执行**
-4. **更新任务状态** ❌ - **AI 必须调用 MCP 工具更新状态**
-
-### 核心问题
-
-**MCP 只负责任务管理,不负责代码生成**。代码生成需要 AI 来完成。
-
-## MCP 工作流程
-
-### 1. 执行任务 (`execute_task`)
-
-```javascript
-mcp_task-manager_execute_task({
-  task_id: 7,
-  auto_complete: true
-})
-```
-
-**返回内容**:
-- 任务信息(ID、名称、描述)
-- 任务描述(包含 DDL、需求等)
-- **执行指令**(告诉 AI 如何生成代码)
-- **更新状态的提醒**(AI 必须调用 update_task_status)
-
-**注意**: 这个函数**不会**自动生成代码,只是返回指令。
-
-### 2. AI 生成代码
-
-AI 需要:
-1. 阅读任务描述
-2. 理解需求
-3. 生成 Python 代码
-4. 保存到指定路径
-
-### 3. 更新任务状态 (`update_task_status`)
-
-```javascript
-mcp_task-manager_update_task_status({
-  task_id: 7,
-  status: "completed",
-  code_name: "import_dept_data.py",
-  code_path: "app/core/data_flow"
-})
-```
-
-**这一步是必须的**,否则任务会一直停留在 "processing" 状态。
-
-## 完整流程示例
-
-### 任务 7: 导入数据资源的科室对照表
-
-#### 步骤 1: 读取任务
-
-```javascript
-// 获取任务详情
-mcp_task-manager_get_task_by_id({ task_id: 7 })
-
-// 返回
-{
-  "task_id": 7,
-  "task_name": "导入数据资源的科室对照表",
-  "status": "pending",
-  "task_description": "# Task: 导入数据资源的科室对照表\n..."
-}
-```
-
-#### 步骤 2: 执行任务
-
-```javascript
-// 执行任务
-mcp_task-manager_execute_task({ 
-  task_id: 7, 
-  auto_complete: true 
-})
-
-// 返回执行指令
-{
-  "message": "请生成代码并更新任务状态...",
-  "task_description": "...",
-  "steps": [
-    "分析需求",
-    "生成代码",
-    "更新任务状态"
-  ]
-}
-```
-
-**注意**: 此时任务状态变为 "processing"。
-
-#### 步骤 3: AI 生成代码
-
-AI 执行以下操作:
-
-```python
-# 1. 创建代码文件
-write("app/core/data_flow/import_dept_data.py", code_content)
-
-# 2. 验证代码
-python -m py_compile app/core/data_flow/import_dept_data.py
-```
-
-#### 步骤 4: 更新任务状态
-
-```javascript
-// AI 调用 MCP 工具更新状态
-mcp_task-manager_update_task_status({
-  task_id: 7,
-  status: "completed",
-  code_name: "import_dept_data.py",
-  code_path: "app/core/data_flow"
-})
-
-// 返回
-{
-  "task_id": 7,
-  "status": "completed",
-  "update_time": "2025-11-28T10:33:37.833Z"
-}
-```
-
-## 任务状态流转
-
-```
-pending (待处理)
-    ↓
-  execute_task 调用
-    ↓
-processing (处理中)
-    ↓
-  AI 生成代码
-    ↓
-  update_task_status 调用
-    ↓
-completed (已完成)
-```
-
-## 常见问题
-
-### Q1: 为什么任务一直停留在 "processing" 状态?
-
-**原因**: AI 没有调用 `update_task_status` 更新状态。
-
-**解决方案**: 在生成代码后,必须调用 `update_task_status`。
-
-### Q2: 任务状态是 "processing",还能再次执行吗?
-
-**不能**。只有 "pending" 状态的任务才能执行。
-
-**解决方案**: 
-```javascript
-// 先更新为 pending
-mcp_task-manager_update_task_status({
-  task_id: 7,
-  status: "pending"
-})
-
-// 再执行
-mcp_task-manager_execute_task({ task_id: 7 })
-```
-
-### Q3: MCP 会自动生成代码吗?
-
-**不会**。MCP 只负责:
-- 任务管理(读取、更新、删除)
-- 返回执行指令
-
-**代码生成**由 AI 负责。
-
-### Q4: 如何查看所有待处理的任务?
-
-```javascript
-// 查看 pending 状态的任务
-mcp_task-manager_get_pending_tasks()
-
-// 查看所有任务
-mcp_task-manager_get_all_tasks({ limit: 20 })
-```
-
-## MCP 工具列表
-
-| 工具 | 功能 | 参数 |
-|------|------|------|
-| `get_pending_tasks` | 获取待处理任务列表 | 无 |
-| `get_task_by_id` | 根据ID获取任务详情 | `task_id` |
-| `execute_task` | 执行任务(返回指令) | `task_id`, `auto_complete` |
-| `update_task_status` | 更新任务状态 | `task_id`, `status`, `code_name`, `code_path` |
-| `process_all_tasks` | 批量处理所有待处理任务 | `auto_poll` |
-| `create_task` | 创建新任务 | `task_name`, `task_description`, `create_by` |
-| `get_all_tasks` | 获取所有任务(调试用) | `limit` |
-
-## 最佳实践
-
-### 1. 自动化流程
-
-```javascript
-// 1. 获取所有待处理任务
-const pendingTasks = mcp_task-manager_get_pending_tasks()
-
-// 2. 逐个执行
-for (const task of pendingTasks) {
-  // 执行任务(获取指令)
-  const instructions = mcp_task-manager_execute_task({
-    task_id: task.task_id,
-    auto_complete: true
-  })
-  
-  // AI 生成代码
-  // ... (AI 操作)
-  
-  // 更新状态
-  mcp_task-manager_update_task_status({
-    task_id: task.task_id,
-    status: "completed",
-    code_name: "generated_file.py",
-    code_path: "app/core/data_flow"
-  })
-}
-```
-
-### 2. 错误处理
-
-```javascript
-try {
-  // 执行任务
-  const result = mcp_task-manager_execute_task({ task_id: 7 })
-  
-  // AI 生成代码
-  // ...
-  
-  // 更新为完成
-  mcp_task-manager_update_task_status({
-    task_id: 7,
-    status: "completed"
-  })
-} catch (error) {
-  // 更新为失败
-  mcp_task-manager_update_task_status({
-    task_id: 7,
-    status: "failed"
-  })
-}
-```
-
-### 3. 批量处理
-
-```javascript
-// 启用自动轮询(每5分钟检查一次)
-mcp_task-manager_process_all_tasks({
-  auto_poll: true
-})
-```
-
-## 任务描述格式
-
-### 由 DataFlow 自动生成
-
-当创建 DataFlow 时,系统会自动生成任务描述:
-
-```markdown
-# Task: {任务名称}
-
-## Data Source
-- Type: postgresql
-- Host: 10.52.31.104
-- Port: 5432
-- Database: hospital_db
-
-## Source Tables (DDL)
-```sql
-CREATE TABLE source_table (...);
-```
-
-## Target Tables (DDL)
-```sql
-CREATE TABLE target_table (
-    ...,
-    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-);
-```
-
-## Update Mode
-- Mode: Append (追加模式)
-
-## Request Content
-{rule 字段的内容}
-
-## Implementation Steps
-1. Extract data from source tables
-2. Apply transformation logic
-3. Write data to target table
-4. Generate Python program
-5. Generate n8n workflow
-```
-
-## 生成的代码结构
-
-### 重要要求:目标表检测和自动创建
-
-**所有数据流脚本必须包含以下功能:**
-
-1. **目标表检测功能**:在数据加载之前,必须检查目标表是否存在
-2. **自动创建表**:如果目标表不存在,脚本必须根据任务描述中的 DDL 自动创建目标表
-3. **表结构匹配**:创建的表结构必须与任务描述中的 DDL 完全一致
-4. **Schema 支持**:必须正确处理 schema(如 `dags`、`public` 等)
-
-### 标准模板(包含表检测功能)
-
-```python
-"""
-{任务名称}
-
-功能:{任务描述}
-模式:{update_mode}
-作者:cursor
-创建时间:{当前日期}
-"""
-
-import os
-import sys
-from datetime import datetime
-from typing import Any
-
-import pandas as pd
-import psycopg2
-from loguru import logger
-
-# 添加项目根目录到Python路径
-PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-sys.path.insert(0, PROJECT_ROOT)
-
-from app.config.config import config, current_env
-
-# 获取配置
-app_config = config[current_env]
-
-
-def ensure_target_table_exists(conn: psycopg2.extensions.connection) -> None:
-    """
-    确保目标表存在,如果不存在则创建
-    
-    重要:此函数必须根据任务描述中的目标表 DDL 来实现
-    
-    Args:
-        conn: 目标数据库连接
-    """
-    cursor = conn.cursor()
-    target_table = "target_table_name"  # 根据任务描述设置
-    target_schema = "public"  # 根据任务描述设置(如 dags, public 等)
-
-    try:
-        # 检查表是否存在
-        cursor.execute("""
-            SELECT EXISTS(
-                SELECT 1 FROM information_schema.tables
-                WHERE table_schema = %s
-                AND table_name = %s
-            )
-        """, (target_schema, target_table))
-        result = cursor.fetchone()
-        exists = result[0] if result else False
-
-        if not exists:
-            logger.info(f"目标表不存在,正在创建 {target_schema}.{target_table}...")
-            
-            # 根据任务描述中的 DDL 创建表
-            create_table_sql = f"""
-            CREATE TABLE IF NOT EXISTS {target_schema}.{target_table} (
-                -- 根据任务描述中的 DDL 添加所有列
-                id SERIAL PRIMARY KEY,
-                create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-            );
-            """
-            cursor.execute(create_table_sql)
-
-            # 添加表注释和列注释
-            # 根据任务描述中的 COMMENT 添加
-            
-            conn.commit()
-            logger.info(f"目标表 {target_schema}.{target_table} 创建成功")
-        else:
-            logger.info(f"目标表 {target_schema}.{target_table} 已存在")
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"创建目标表失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def main() -> dict[str, Any]:
-    """
-    主函数:执行ETL流程
-    
-    重要:必须按以下顺序执行:
-    1. 建立数据库连接
-    2. 确保目标表存在(调用 ensure_target_table_exists)
-    3. 提取源数据
-    4. 数据转换
-    5. 加载到目标表
-    """
-    # ... 实现代码 ...
-    
-    # 步骤2: 确保目标表存在(必须在数据加载前执行)
-    logger.info("[Step 2/5] 检查/创建目标表...")
-    ensure_target_table_exists(target_conn)
-    
-    # ... 其他步骤 ...
-```
-
-### 参考模板文件
-
-完整的脚本模板请参考:`docs/script_template_with_table_check.py`
-
-## 与 DataFlow 集成
-
-### DataFlow 创建时自动生成任务
-
-在 `app/core/data_flow/dataflows.py` 的 `create_dataflow` 函数中:
-
-```python
-# 保存到 task_list 表
-task_insert_sql = text("""
-    INSERT INTO public.task_list 
-    (task_name, task_description, status, code_name, code_path, create_by, create_time)
-    VALUES 
-    (:task_name, :task_description, :status, :code_name, :code_path, :create_by, :create_time)
-""")
-
-task_params = {
-    'task_name': script_name,
-    'task_description': task_description_md,  # Markdown 格式的任务描述
-    'status': 'pending',
-    'code_name': script_name,
-    'code_path': 'app/core/data_flow',
-    'create_by': 'cursor',
-    'create_time': current_time
-}
-
-db.session.execute(task_insert_sql, task_params)
-```
-
-### 任务描述包含的信息
-
-1. ✅ **数据源信息**: 从 Neo4j 获取(如果有)
-2. ✅ **源表 DDL**: 根据 source_table IDs 生成
-3. ✅ **目标表 DDL**: 根据 target_table IDs 生成(含 create_time)
-4. ✅ **更新模式**: append 或 full
-5. ✅ **规则说明**: rule 字段内容
-6. ✅ **实施步骤**: 标准化步骤
-
-## 总结
-
-### MCP 的职责
-- ✅ 任务的 CRUD 操作
-- ✅ 返回任务描述和执行指令
-- ✅ 更新任务状态
-- ❌ **不负责生成代码**
-
-### AI 的职责
-- ✅ 读取 MCP 返回的任务描述
-- ✅ 理解需求并生成代码
-- ✅ 保存代码到指定路径
-- ✅ **必须调用 MCP 更新任务状态**
-
-### 关键要点
-
-1. **`execute_task` 不会自动生成代码**,只返回指令
-2. **AI 必须主动生成代码**
-3. **AI 必须调用 `update_task_status`** 更新状态
-4. 只有 "pending" 状态的任务才能执行
-5. 任务描述由 DataFlow 自动生成,格式标准化
-
-## 示例:任务 7 的完整流程
-
-```javascript
-// 1. 查看任务
-mcp_task-manager_get_task_by_id({ task_id: 7 })
-// 状态: pending
-
-// 2. 执行任务
-mcp_task-manager_execute_task({ task_id: 7, auto_complete: true })
-// 状态: processing
-// 返回: 任务描述 + 执行指令
-
-// 3. AI 生成代码
-write("app/core/data_flow/import_dept_data.py", code)
-// 文件已创建
-
-// 4. 更新状态
-mcp_task-manager_update_task_status({
-  task_id: 7,
-  status: "completed",
-  code_name: "import_dept_data.py",
-  code_path: "app/core/data_flow"
-})
-// 状态: completed ✅
-```
-
-## 相关文件
-
-- `app/core/data_flow/dataflows.py` - DataFlow 创建时写入 task_list
-- `docs/DataFlow_task_list优化说明.md` - 任务描述生成逻辑
-- `app/core/data_flow/import_dept_data.py` - 任务 7 生成的代码
-
-## 更新历史
-
-- **2025-11-28**: 诊断 Task Manager MCP 工作流程,明确 AI 和 MCP 的职责划分
-

+ 0 - 729
docs/api_bd_compose_guide.md

@@ -1,729 +0,0 @@
-# 业务领域组合创建接口 - 前端开发指南
-
-> 本文档为前端开发人员提供业务领域组合创建 (`/compose`) 接口的详细使用说明。
-
-## 目录
-
-- [接口概述](#接口概述)
-- [接口规格](#接口规格)
-- [请求参数详解](#请求参数详解)
-- [响应格式](#响应格式)
-- [前端实现示例](#前端实现示例)
-- [典型使用场景](#典型使用场景)
-- [错误处理](#错误处理)
-- [最佳实践](#最佳实践)
-
----
-
-## 接口概述
-
-### 功能说明
-
-该接口用于从已有的元数据中**组合创建新的业务领域**。用户可以从不同的业务领域中选择需要的元数据(DataMeta),将它们组合到一个新的业务领域中。
-
-### 业务流程
-
-```
-┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
-│   用户选择      │     │   提交创建      │     │   返回结果      │
-│   元数据列表    │ ──► │   请求          │ ──► │   新业务领域    │
-└─────────────────┘     └─────────────────┘     └─────────────────┘
-```
-
-### 核心特性
-
-- ✅ 支持选择多个元数据
-- ✅ 自动翻译中文名称为英文(若未提供)
-- ✅ 支持关联标签和数据源
-- ✅ 自动创建业务领域与元数据的 `INCLUDES` 关系
-
----
-
-## 接口规格
-
-### 基本信息
-
-| 项目 | 说明 |
-|------|------|
-| **请求URL** | `POST /api/bd/compose` |
-| **Content-Type** | `application/json` |
-| **请求方法** | POST |
-
-### 请求头
-
-```http
-Content-Type: application/json
-```
-
----
-
-## 请求参数详解
-
-### 参数列表
-
-| 参数名 | 类型 | 必填 | 默认值 | 说明 |
-|--------|------|------|--------|------|
-| name_zh | string | ✅ 是 | - | 业务领域中文名称 |
-| name_en | string | 否 | 自动翻译 | 业务领域英文名称 |
-| id_list | array | ✅ 是 | - | 选中的元数据ID列表 |
-| describe | string | 否 | - | 业务领域描述 |
-| type | string | 否 | - | 业务领域类型 |
-| category | string | 否 | - | 业务领域分类 |
-| tag | array/integer | 否 | - | 标签ID或标签对象数组 |
-| data_source | integer | 否 | - | 数据源ID |
-
-### id_list 参数格式
-
-`id_list` 支持两种格式,前端可根据实际情况选择:
-
-#### 格式一:纯ID数组(推荐)
-
-最简洁的格式,直接传递元数据的 ID 列表:
-
-```json
-{
-  "name_zh": "销售分析域",
-  "id_list": [123, 456, 789]
-}
-```
-
-#### 格式二:对象数组
-
-兼容旧格式,传递包含 `id` 字段的对象数组:
-
-```json
-{
-  "name_zh": "销售分析域",
-  "id_list": [
-    { "id": 123 },
-    { "id": 456 },
-    { "id": 789 }
-  ]
-}
-```
-
-### tag 参数格式
-
-`tag` 参数支持多种格式:
-
-```javascript
-// 格式1:单个标签ID
-{ "tag": 100 }
-
-// 格式2:标签ID数组
-{ "tag": [100, 101, 102] }
-
-// 格式3:标签对象数组
-{ "tag": [{ "id": 100 }, { "id": 101 }] }
-
-// 格式4:完整标签对象
-{ "tag": [{ "id": 100, "name_zh": "核心业务", "name_en": "core" }] }
-```
-
-### 可选字段说明
-
-以下字段均为可选,根据业务需求选择性传递:
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| leader | string | 负责人 |
-| organization | string | 所属组织 |
-| status | string | 状态 |
-| keywords | string | 关键词 |
-| data_sensitivity | string | 数据敏感性 |
-| frequency | string | 更新频率 |
-| url | string | 相关URL |
-| storage_location | string | 存储位置 |
-
----
-
-## 响应格式
-
-### 成功响应
-
-```json
-{
-  "code": 200,
-  "message": "操作成功",
-  "data": {
-    "business_domain": {
-      "id": 12350,
-      "name_zh": "销售分析域",
-      "name_en": "sales_analysis_domain",
-      "describe": "整合订单和客户数据的销售分析业务领域",
-      "type": "analysis",
-      "category": "业务分析",
-      "create_time": "2025-01-09 10:30:00",
-      "update_time": "2025-01-09 10:30:00",
-      "tag": [
-        {
-          "id": 100,
-          "name_zh": "核心业务",
-          "name_en": "core_business"
-        }
-      ]
-    }
-  }
-}
-```
-
-### 响应字段说明
-
-| 字段路径 | 类型 | 说明 |
-|----------|------|------|
-| code | integer | 状态码,200 表示成功 |
-| message | string | 操作结果消息 |
-| data.business_domain | object | 创建的业务领域详情 |
-| data.business_domain.id | integer | 新创建的业务领域节点ID |
-| data.business_domain.name_zh | string | 中文名称 |
-| data.business_domain.name_en | string | 英文名称 |
-| data.business_domain.tag | array | 关联的标签列表 |
-
-### 失败响应
-
-```json
-{
-  "code": 500,
-  "message": "组合创建业务领域失败",
-  "data": null,
-  "error": "具体错误信息"
-}
-```
-
----
-
-## 前端实现示例
-
-### 使用 Axios
-
-```javascript
-import axios from 'axios';
-
-/**
- * 组合创建业务领域
- * @param {Object} params - 创建参数
- * @param {string} params.name_zh - 中文名称(必填)
- * @param {string} [params.name_en] - 英文名称(可选,自动翻译)
- * @param {number[]} params.id_list - 元数据ID列表(必填)
- * @param {string} [params.describe] - 描述
- * @param {string} [params.type] - 类型
- * @param {string} [params.category] - 分类
- * @param {number|number[]} [params.tag] - 标签ID
- * @param {number} [params.data_source] - 数据源ID
- * @returns {Promise<Object>} 创建结果
- */
-async function composeBusinessDomain(params) {
-  try {
-    const response = await axios.post('/api/bd/compose', params);
-    
-    if (response.data.code === 200) {
-      return {
-        success: true,
-        data: response.data.data.business_domain
-      };
-    } else {
-      return {
-        success: false,
-        message: response.data.message,
-        error: response.data.error
-      };
-    }
-  } catch (error) {
-    console.error('组合创建业务领域失败:', error);
-    throw error;
-  }
-}
-
-// 使用示例
-const result = await composeBusinessDomain({
-  name_zh: '客户订单分析域',
-  describe: '整合客户信息和订单数据的分析业务领域',
-  id_list: [101, 102, 103, 201, 202],  // 选中的元数据ID
-  tag: [100],  // 标签ID
-  category: '业务分析'
-});
-
-if (result.success) {
-  console.log('创建成功,新业务领域ID:', result.data.id);
-} else {
-  console.error('创建失败:', result.message);
-}
-```
-
-### 使用 Fetch API
-
-```javascript
-/**
- * 组合创建业务领域
- */
-async function composeBusinessDomain(params) {
-  const response = await fetch('/api/bd/compose', {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json'
-    },
-    body: JSON.stringify(params)
-  });
-  
-  const data = await response.json();
-  
-  if (data.code !== 200) {
-    throw new Error(data.message || '创建失败');
-  }
-  
-  return data.data.business_domain;
-}
-
-// 使用示例
-try {
-  const newDomain = await composeBusinessDomain({
-    name_zh: '销售报表域',
-    id_list: [301, 302, 303]
-  });
-  console.log('创建成功:', newDomain);
-} catch (error) {
-  console.error('创建失败:', error.message);
-}
-```
-
-### Vue 3 组件示例
-
-```vue
-<template>
-  <div class="compose-form">
-    <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-      <el-form-item label="中文名称" prop="name_zh">
-        <el-input v-model="form.name_zh" placeholder="请输入业务领域中文名称" />
-      </el-form-item>
-      
-      <el-form-item label="英文名称" prop="name_en">
-        <el-input v-model="form.name_en" placeholder="可选,不填则自动翻译" />
-      </el-form-item>
-      
-      <el-form-item label="描述" prop="describe">
-        <el-input type="textarea" v-model="form.describe" :rows="3" />
-      </el-form-item>
-      
-      <el-form-item label="元数据" prop="id_list">
-        <div class="selected-meta">
-          <el-tag 
-            v-for="id in form.id_list" 
-            :key="id" 
-            closable 
-            @close="removeMeta(id)"
-          >
-            {{ getMetaName(id) }}
-          </el-tag>
-        </div>
-        <el-button type="primary" text @click="openMetaSelector">
-          选择元数据
-        </el-button>
-      </el-form-item>
-      
-      <el-form-item>
-        <el-button type="primary" @click="handleSubmit" :loading="loading">
-          创建业务领域
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive } from 'vue';
-import { ElMessage } from 'element-plus';
-import axios from 'axios';
-
-const formRef = ref(null);
-const loading = ref(false);
-
-const form = reactive({
-  name_zh: '',
-  name_en: '',
-  describe: '',
-  id_list: []
-});
-
-const rules = {
-  name_zh: [
-    { required: true, message: '请输入中文名称', trigger: 'blur' }
-  ],
-  id_list: [
-    { 
-      required: true, 
-      validator: (rule, value, callback) => {
-        if (!value || value.length === 0) {
-          callback(new Error('请至少选择一个元数据'));
-        } else {
-          callback();
-        }
-      }, 
-      trigger: 'change' 
-    }
-  ]
-};
-
-// 选择元数据
-const openMetaSelector = () => {
-  // 打开元数据选择器对话框
-  // 实现略...
-};
-
-// 移除已选元数据
-const removeMeta = (id) => {
-  const index = form.id_list.indexOf(id);
-  if (index > -1) {
-    form.id_list.splice(index, 1);
-  }
-};
-
-// 获取元数据名称(用于显示)
-const getMetaName = (id) => {
-  // 根据ID获取元数据名称
-  // 实现略...
-  return `元数据-${id}`;
-};
-
-// 提交表单
-const handleSubmit = async () => {
-  try {
-    await formRef.value.validate();
-    
-    loading.value = true;
-    
-    const response = await axios.post('/api/bd/compose', {
-      name_zh: form.name_zh,
-      name_en: form.name_en || undefined,
-      describe: form.describe || undefined,
-      id_list: form.id_list
-    });
-    
-    if (response.data.code === 200) {
-      ElMessage.success('业务领域创建成功');
-      const newDomain = response.data.data.business_domain;
-      console.log('新建业务领域:', newDomain);
-      // 跳转到详情页或刷新列表
-    } else {
-      ElMessage.error(response.data.message || '创建失败');
-    }
-  } catch (error) {
-    if (error.name !== 'Error') {
-      // 非表单验证错误
-      ElMessage.error('创建失败: ' + error.message);
-    }
-  } finally {
-    loading.value = false;
-  }
-};
-</script>
-```
-
-### React 组件示例
-
-```jsx
-import React, { useState } from 'react';
-import axios from 'axios';
-
-function ComposeBusinessDomain({ selectedMetaIds, onSuccess }) {
-  const [form, setForm] = useState({
-    name_zh: '',
-    name_en: '',
-    describe: ''
-  });
-  const [loading, setLoading] = useState(false);
-  const [error, setError] = useState(null);
-
-  const handleChange = (e) => {
-    const { name, value } = e.target;
-    setForm(prev => ({ ...prev, [name]: value }));
-  };
-
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    
-    if (!form.name_zh.trim()) {
-      setError('请输入中文名称');
-      return;
-    }
-    
-    if (!selectedMetaIds || selectedMetaIds.length === 0) {
-      setError('请至少选择一个元数据');
-      return;
-    }
-
-    setLoading(true);
-    setError(null);
-
-    try {
-      const response = await axios.post('/api/bd/compose', {
-        name_zh: form.name_zh,
-        name_en: form.name_en || undefined,
-        describe: form.describe || undefined,
-        id_list: selectedMetaIds
-      });
-
-      if (response.data.code === 200) {
-        onSuccess?.(response.data.data.business_domain);
-      } else {
-        setError(response.data.message || '创建失败');
-      }
-    } catch (err) {
-      setError(err.response?.data?.message || err.message);
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  return (
-    <form onSubmit={handleSubmit}>
-      <div className="form-group">
-        <label>中文名称 *</label>
-        <input
-          type="text"
-          name="name_zh"
-          value={form.name_zh}
-          onChange={handleChange}
-          placeholder="请输入业务领域中文名称"
-          required
-        />
-      </div>
-
-      <div className="form-group">
-        <label>英文名称</label>
-        <input
-          type="text"
-          name="name_en"
-          value={form.name_en}
-          onChange={handleChange}
-          placeholder="可选,不填则自动翻译"
-        />
-      </div>
-
-      <div className="form-group">
-        <label>描述</label>
-        <textarea
-          name="describe"
-          value={form.describe}
-          onChange={handleChange}
-          rows={3}
-        />
-      </div>
-
-      <div className="form-group">
-        <label>已选元数据</label>
-        <div className="selected-count">
-          已选择 {selectedMetaIds?.length || 0} 个元数据
-        </div>
-      </div>
-
-      {error && <div className="error-message">{error}</div>}
-
-      <button type="submit" disabled={loading}>
-        {loading ? '创建中...' : '创建业务领域'}
-      </button>
-    </form>
-  );
-}
-
-export default ComposeBusinessDomain;
-```
-
----
-
-## 典型使用场景
-
-### 场景一:从多个业务领域选取元数据
-
-用户浏览多个现有业务领域,从中选取需要的元数据字段,组合成新的业务领域。
-
-```javascript
-// 用户从订单域选取了 order_id, order_amount
-// 从客户域选取了 customer_id, customer_name
-// 组合创建 "订单客户分析域"
-
-const selectedMetaIds = [
-  101,  // order_id
-  102,  // order_amount
-  201,  // customer_id
-  202   // customer_name
-];
-
-await composeBusinessDomain({
-  name_zh: '订单客户分析域',
-  describe: '用于分析订单与客户关联关系',
-  id_list: selectedMetaIds,
-  category: '分析域'
-});
-```
-
-### 场景二:快速创建子域
-
-从一个大的业务领域中选取部分元数据,创建更细粒度的子域。
-
-```javascript
-// 从 "销售管理" 域中选取部分字段创建 "销售统计" 子域
-await composeBusinessDomain({
-  name_zh: '销售统计域',
-  id_list: [301, 302, 303, 304],
-  tag: [100],  // 关联 "核心业务" 标签
-  data_source: 1  // 关联数据源
-});
-```
-
-### 场景三:跨数据源整合
-
-整合来自不同数据源的元数据,创建统一的业务视图。
-
-```javascript
-await composeBusinessDomain({
-  name_zh: '统一客户视图',
-  describe: '整合CRM、ERP、电商系统的客户数据',
-  id_list: [
-    ...crmCustomerFieldIds,
-    ...erpCustomerFieldIds,
-    ...ecommerceCustomerFieldIds
-  ],
-  category: 'MDM',
-  tag: [200]  // "主数据" 标签
-});
-```
-
----
-
-## 错误处理
-
-### 错误码说明
-
-| 错误消息 | 原因 | 解决方案 |
-|----------|------|----------|
-| 请求数据不能为空 | 未发送请求体 | 检查请求是否包含 JSON body |
-| name_zh 为必填项 | 未提供中文名称 | 添加 name_zh 参数 |
-| id_list 为必填项 | 未提供元数据列表 | 添加 id_list 参数 |
-| 创建业务领域节点失败 | Neo4j 数据库错误 | 联系后端排查 |
-
-### 前端错误处理示例
-
-```javascript
-async function handleComposeError(error, response) {
-  // 网络错误
-  if (!response) {
-    showMessage('网络连接失败,请检查网络');
-    return;
-  }
-  
-  const { code, message, error: errorDetail } = response.data;
-  
-  switch (message) {
-    case 'name_zh 为必填项':
-      showMessage('请输入业务领域中文名称');
-      focusField('name_zh');
-      break;
-      
-    case 'id_list 为必填项':
-      showMessage('请至少选择一个元数据');
-      break;
-      
-    default:
-      showMessage(`创建失败: ${message}`);
-      console.error('详细错误:', errorDetail);
-  }
-}
-```
-
----
-
-## 最佳实践
-
-### 1. 参数校验
-
-在调用接口前进行前端校验,减少无效请求:
-
-```javascript
-function validateComposeParams(params) {
-  const errors = [];
-  
-  if (!params.name_zh?.trim()) {
-    errors.push('中文名称不能为空');
-  }
-  
-  if (!params.id_list || params.id_list.length === 0) {
-    errors.push('请至少选择一个元数据');
-  }
-  
-  if (params.name_zh && params.name_zh.length > 100) {
-    errors.push('中文名称不能超过100个字符');
-  }
-  
-  return errors;
-}
-```
-
-### 2. 去重处理
-
-确保 id_list 中没有重复的 ID:
-
-```javascript
-const uniqueIds = [...new Set(selectedMetaIds)];
-```
-
-### 3. 用户体验优化
-
-```javascript
-// 1. 添加加载状态
-setLoading(true);
-
-// 2. 成功后给予反馈
-ElMessage.success({
-  message: `业务领域"${result.name_zh}"创建成功`,
-  duration: 3000
-});
-
-// 3. 提供快捷操作
-showDialog({
-  title: '创建成功',
-  message: '是否立即查看新创建的业务领域?',
-  confirmText: '查看详情',
-  cancelText: '继续创建',
-  onConfirm: () => router.push(`/bd/detail/${result.id}`)
-});
-```
-
-### 4. 批量选择元数据
-
-提供高效的元数据选择方式:
-
-```javascript
-// 支持按业务领域批量选择
-async function selectMetaFromDomain(domainId) {
-  const metaList = await fetchDomainMeta(domainId);
-  return metaList.map(meta => meta.id);
-}
-
-// 支持搜索过滤
-async function searchAndSelectMeta(keyword) {
-  const results = await searchMeta(keyword);
-  // 显示选择器...
-}
-```
-
----
-
-## 更新日志
-
-| 版本 | 日期 | 更新内容 |
-|------|------|----------|
-| 2.0.0 | 2025-01-09 | 简化 id_list 格式,支持纯ID数组;移除 domain_id 参数 |
-| 1.0.0 | 2025-12-06 | 初始版本 |
-
----
-
-## 相关接口
-
-- [获取业务领域列表](/api/bd/list) - 获取可选择的业务领域
-- [搜索关联元数据](/api/bd/search) - 获取业务领域下的元数据
-- [获取业务领域详情](/api/bd/detail) - 查看创建结果
-- [获取标签列表](/api/bd/labellist) - 获取可关联的标签
-
----
-
-> 如有问题,请联系后端开发团队。

+ 0 - 664
docs/api_get_script.md

@@ -1,664 +0,0 @@
-# DataFlow 脚本获取接口 - 前端开发指南
-
-## 接口概述
-
-该接口用于根据 DataFlow ID 获取关联的 Python 脚本内容,支持前端代码预览和展示功能。
-
----
-
-## 接口信息
-
-| 项目 | 说明 |
-|------|------|
-| **接口路径** | `/api/dataflow/get-script/<dataflow_id>` |
-| **请求方法** | `GET` |
-| **Content-Type** | `application/json` |
-| **认证方式** | 根据项目配置(如有) |
-
----
-
-## 请求参数
-
-### URL 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| `dataflow_id` | Integer | 是 | DataFlow 节点的 ID(Neo4j 节点 ID) |
-
-### 请求示例
-
-```
-GET /api/dataflow/get-script/12345
-```
-
----
-
-## 返回数据格式
-
-### 成功响应
-
-```json
-{
-  "code": 200,
-  "message": "获取脚本成功",
-  "data": {
-    "script_path": "app/core/data_flow/scripts/sync_talent_data.py",
-    "script_content": "#!/usr/bin/env python3\n\"\"\"人才数据同步脚本\"\"\"\n\nimport pandas as pd\nfrom app.config.config import get_config_by_env\n\ndef main():\n    config = get_config_by_env()\n    # 脚本主逻辑...\n    print('执行完成')\n\nif __name__ == '__main__':\n    main()\n",
-    "script_type": "python",
-    "dataflow_id": 12345,
-    "dataflow_name": "人才数据同步",
-    "dataflow_name_en": "sync_talent_data"
-  }
-}
-```
-
-### 返回字段说明
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| `code` | Integer | 状态码,200 表示成功 |
-| `message` | String | 响应消息 |
-| `data.script_path` | String | 脚本文件的相对路径 |
-| `data.script_content` | String | 脚本文件的完整内容 |
-| `data.script_type` | String | 脚本类型,可选值:`python`、`javascript`、`typescript`、`sql`、`shell`、`text` |
-| `data.dataflow_id` | Integer | DataFlow 节点 ID |
-| `data.dataflow_name` | String | DataFlow 中文名称 |
-| `data.dataflow_name_en` | String | DataFlow 英文名称 |
-
----
-
-## 错误响应
-
-### 1. DataFlow 不存在或脚本路径为空(400)
-
-```json
-{
-  "code": 400,
-  "message": "未找到 ID 为 12345 的 DataFlow 节点",
-  "data": {}
-}
-```
-
-或
-
-```json
-{
-  "code": 400,
-  "message": "DataFlow (ID: 12345) 的 script_path 属性为空",
-  "data": {}
-}
-```
-
-### 2. 脚本文件不存在(404)
-
-```json
-{
-  "code": 404,
-  "message": "脚本文件不存在: /opt/dataops-platform/app/core/data_flow/scripts/missing_script.py",
-  "data": {}
-}
-```
-
-### 3. 服务器内部错误(500)
-
-```json
-{
-  "code": 500,
-  "message": "获取脚本失败: 数据库连接异常",
-  "data": {}
-}
-```
-
-### 错误码汇总
-
-| 错误码 | 说明 | 处理建议 |
-|--------|------|----------|
-| 400 | 参数错误或 DataFlow 脚本路径为空 | 检查 DataFlow ID 是否正确,确认该 DataFlow 已关联脚本 |
-| 404 | 脚本文件不存在 | 检查服务器上脚本文件是否存在,可能需要重新生成脚本 |
-| 500 | 服务器内部错误 | 联系后端开发人员检查日志 |
-
----
-
-## 前端集成指南
-
-### 1. Axios 请求封装
-
-```javascript
-// api/dataflow.js
-import axios from 'axios'
-
-const BASE_URL = process.env.VUE_APP_API_BASE_URL || ''
-
-/**
- * 获取 DataFlow 关联的脚本内容
- * @param {number} dataflowId - DataFlow 节点 ID
- * @returns {Promise} 返回脚本信息
- */
-export function getDataFlowScript(dataflowId) {
-  return axios.get(`${BASE_URL}/api/dataflow/get-script/${dataflowId}`)
-}
-```
-
-### 2. Vue 3 组件示例(使用 Composition API)
-
-```vue
-<template>
-  <div class="script-viewer">
-    <!-- 头部信息 -->
-    <div class="script-header" v-if="scriptData">
-      <div class="script-info">
-        <h3>{{ scriptData.dataflow_name }}</h3>
-        <span class="script-path">{{ scriptData.script_path }}</span>
-      </div>
-      <div class="script-actions">
-        <el-button type="primary" size="small" @click="copyScript">
-          <el-icon><CopyDocument /></el-icon>
-          复制代码
-        </el-button>
-        <el-button size="small" @click="downloadScript">
-          <el-icon><Download /></el-icon>
-          下载
-        </el-button>
-      </div>
-    </div>
-
-    <!-- 加载状态 -->
-    <div class="loading-container" v-if="loading">
-      <el-skeleton :rows="15" animated />
-    </div>
-
-    <!-- 错误提示 -->
-    <el-alert
-      v-if="error"
-      :title="error.title"
-      :description="error.message"
-      type="error"
-      show-icon
-      :closable="false"
-    />
-
-    <!-- 代码展示区域 -->
-    <div class="code-container" v-if="scriptData && !loading">
-      <prism-editor
-        v-model="scriptData.script_content"
-        :highlight="highlighter"
-        :readonly="true"
-        line-numbers
-        class="code-editor"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted, computed } from 'vue'
-import { useRoute } from 'vue-router'
-import { ElMessage } from 'element-plus'
-import { CopyDocument, Download } from '@element-plus/icons-vue'
-import { PrismEditor } from 'vue-prism-editor'
-import 'vue-prism-editor/dist/prismeditor.min.css'
-import { highlight, languages } from 'prismjs'
-import 'prismjs/components/prism-python'
-import 'prismjs/themes/prism-tomorrow.css'
-import { getDataFlowScript } from '@/api/dataflow'
-
-const route = useRoute()
-const loading = ref(false)
-const error = ref(null)
-const scriptData = ref(null)
-
-// Prism 代码高亮
-const highlighter = (code) => {
-  const lang = scriptData.value?.script_type || 'python'
-  const grammar = languages[lang] || languages.plain
-  return highlight(code, grammar, lang)
-}
-
-// 获取脚本内容
-const fetchScript = async (dataflowId) => {
-  loading.value = true
-  error.value = null
-  
-  try {
-    const response = await getDataFlowScript(dataflowId)
-    
-    if (response.data.code === 200) {
-      scriptData.value = response.data.data
-    } else {
-      error.value = {
-        title: '获取脚本失败',
-        message: response.data.message
-      }
-    }
-  } catch (err) {
-    console.error('获取脚本失败:', err)
-    
-    if (err.response) {
-      const { code, message } = err.response.data
-      error.value = {
-        title: getErrorTitle(code),
-        message: message
-      }
-    } else {
-      error.value = {
-        title: '网络错误',
-        message: '无法连接到服务器,请检查网络连接'
-      }
-    }
-  } finally {
-    loading.value = false
-  }
-}
-
-// 根据错误码获取标题
-const getErrorTitle = (code) => {
-  const titles = {
-    400: '参数错误',
-    404: '文件不存在',
-    500: '服务器错误'
-  }
-  return titles[code] || '未知错误'
-}
-
-// 复制代码到剪贴板
-const copyScript = async () => {
-  if (!scriptData.value?.script_content) return
-  
-  try {
-    await navigator.clipboard.writeText(scriptData.value.script_content)
-    ElMessage.success('代码已复制到剪贴板')
-  } catch (err) {
-    ElMessage.error('复制失败,请手动选择复制')
-  }
-}
-
-// 下载脚本文件
-const downloadScript = () => {
-  if (!scriptData.value) return
-  
-  const { script_content, dataflow_name_en, script_type } = scriptData.value
-  const extension = script_type === 'python' ? 'py' : script_type
-  const filename = `${dataflow_name_en || 'script'}.${extension}`
-  
-  const blob = new Blob([script_content], { type: 'text/plain;charset=utf-8' })
-  const url = URL.createObjectURL(blob)
-  const link = document.createElement('a')
-  link.href = url
-  link.download = filename
-  link.click()
-  URL.revokeObjectURL(url)
-  
-  ElMessage.success(`已下载: ${filename}`)
-}
-
-// 组件挂载时获取脚本
-onMounted(() => {
-  const dataflowId = route.params.id || route.query.dataflowId
-  if (dataflowId) {
-    fetchScript(Number(dataflowId))
-  }
-})
-
-// 暴露方法供父组件调用
-defineExpose({
-  fetchScript
-})
-</script>
-
-<style scoped>
-.script-viewer {
-  padding: 16px;
-  background: #fff;
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
-
-.script-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 16px;
-  padding-bottom: 12px;
-  border-bottom: 1px solid #e8e8e8;
-}
-
-.script-info h3 {
-  margin: 0 0 4px 0;
-  font-size: 16px;
-  color: #333;
-}
-
-.script-path {
-  font-size: 12px;
-  color: #999;
-  font-family: 'Courier New', monospace;
-}
-
-.script-actions {
-  display: flex;
-  gap: 8px;
-}
-
-.loading-container {
-  padding: 20px 0;
-}
-
-.code-container {
-  border: 1px solid #e8e8e8;
-  border-radius: 4px;
-  overflow: hidden;
-}
-
-.code-editor {
-  font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
-  font-size: 14px;
-  line-height: 1.6;
-  padding: 16px;
-  background: #1e1e1e;
-  min-height: 400px;
-  max-height: 600px;
-  overflow: auto;
-}
-
-/* Prism Editor 行号样式 */
-:deep(.prism-editor__line-numbers) {
-  background: #252526;
-  color: #858585;
-  padding: 16px 8px;
-  border-right: 1px solid #404040;
-}
-</style>
-```
-
-### 3. 使用 Monaco Editor 的高级示例
-
-如果需要更强大的代码编辑器功能,可以使用 Monaco Editor:
-
-```vue
-<template>
-  <div class="monaco-script-viewer">
-    <div class="viewer-header" v-if="scriptData">
-      <div class="file-info">
-        <el-icon><Document /></el-icon>
-        <span class="filename">{{ getFilename }}</span>
-        <el-tag size="small" :type="getLanguageTagType">
-          {{ scriptData.script_type }}
-        </el-tag>
-      </div>
-      <div class="toolbar">
-        <el-tooltip content="复制代码">
-          <el-button :icon="CopyDocument" circle size="small" @click="copyCode" />
-        </el-tooltip>
-        <el-tooltip content="下载文件">
-          <el-button :icon="Download" circle size="small" @click="downloadFile" />
-        </el-tooltip>
-        <el-tooltip content="全屏查看">
-          <el-button :icon="FullScreen" circle size="small" @click="toggleFullscreen" />
-        </el-tooltip>
-      </div>
-    </div>
-
-    <div 
-      ref="editorContainer" 
-      class="editor-container"
-      :class="{ 'fullscreen': isFullscreen }"
-    />
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
-import * as monaco from 'monaco-editor'
-import { Document, CopyDocument, Download, FullScreen } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-import { getDataFlowScript } from '@/api/dataflow'
-
-const props = defineProps({
-  dataflowId: {
-    type: Number,
-    required: true
-  }
-})
-
-const editorContainer = ref(null)
-const scriptData = ref(null)
-const loading = ref(false)
-const isFullscreen = ref(false)
-
-let editor = null
-
-// 计算文件名
-const getFilename = computed(() => {
-  if (!scriptData.value?.script_path) return ''
-  return scriptData.value.script_path.split('/').pop()
-})
-
-// 语言标签类型
-const getLanguageTagType = computed(() => {
-  const types = {
-    python: 'success',
-    javascript: 'warning',
-    sql: 'info',
-    shell: ''
-  }
-  return types[scriptData.value?.script_type] || ''
-})
-
-// 获取 Monaco 语言标识
-const getMonacoLanguage = (scriptType) => {
-  const languageMap = {
-    python: 'python',
-    javascript: 'javascript',
-    typescript: 'typescript',
-    sql: 'sql',
-    shell: 'shell'
-  }
-  return languageMap[scriptType] || 'plaintext'
-}
-
-// 初始化编辑器
-const initEditor = () => {
-  if (!editorContainer.value) return
-  
-  editor = monaco.editor.create(editorContainer.value, {
-    value: '',
-    language: 'python',
-    theme: 'vs-dark',
-    readOnly: true,
-    automaticLayout: true,
-    minimap: { enabled: true },
-    scrollBeyondLastLine: false,
-    fontSize: 14,
-    lineNumbers: 'on',
-    renderLineHighlight: 'all',
-    folding: true,
-    wordWrap: 'on'
-  })
-}
-
-// 加载脚本
-const loadScript = async () => {
-  if (!props.dataflowId) return
-  
-  loading.value = true
-  try {
-    const response = await getDataFlowScript(props.dataflowId)
-    
-    if (response.data.code === 200) {
-      scriptData.value = response.data.data
-      
-      if (editor) {
-        const language = getMonacoLanguage(scriptData.value.script_type)
-        monaco.editor.setModelLanguage(editor.getModel(), language)
-        editor.setValue(scriptData.value.script_content)
-      }
-    } else {
-      ElMessage.error(response.data.message)
-    }
-  } catch (err) {
-    ElMessage.error('加载脚本失败')
-    console.error(err)
-  } finally {
-    loading.value = false
-  }
-}
-
-// 复制代码
-const copyCode = async () => {
-  const content = editor?.getValue()
-  if (!content) return
-  
-  try {
-    await navigator.clipboard.writeText(content)
-    ElMessage.success('代码已复制')
-  } catch (err) {
-    ElMessage.error('复制失败')
-  }
-}
-
-// 下载文件
-const downloadFile = () => {
-  if (!scriptData.value) return
-  
-  const blob = new Blob([scriptData.value.script_content], { type: 'text/plain' })
-  const url = URL.createObjectURL(blob)
-  const a = document.createElement('a')
-  a.href = url
-  a.download = getFilename.value
-  a.click()
-  URL.revokeObjectURL(url)
-}
-
-// 切换全屏
-const toggleFullscreen = () => {
-  isFullscreen.value = !isFullscreen.value
-  setTimeout(() => editor?.layout(), 100)
-}
-
-// 监听 dataflowId 变化
-watch(() => props.dataflowId, (newId) => {
-  if (newId) loadScript()
-})
-
-onMounted(() => {
-  initEditor()
-  loadScript()
-})
-
-onBeforeUnmount(() => {
-  editor?.dispose()
-})
-</script>
-
-<style scoped>
-.monaco-script-viewer {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  background: #1e1e1e;
-  border-radius: 8px;
-  overflow: hidden;
-}
-
-.viewer-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 8px 16px;
-  background: #252526;
-  border-bottom: 1px solid #3c3c3c;
-}
-
-.file-info {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  color: #cccccc;
-}
-
-.filename {
-  font-family: 'Consolas', monospace;
-  font-size: 13px;
-}
-
-.toolbar {
-  display: flex;
-  gap: 4px;
-}
-
-.editor-container {
-  flex: 1;
-  min-height: 400px;
-}
-
-.editor-container.fullscreen {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  z-index: 9999;
-  min-height: 100vh;
-}
-</style>
-```
-
----
-
-## 依赖安装
-
-### Vue Prism Editor
-
-```bash
-npm install vue-prism-editor prismjs
-```
-
-### Monaco Editor
-
-```bash
-npm install monaco-editor
-```
-
-对于 Vite 项目,还需要安装 worker 插件:
-
-```bash
-npm install vite-plugin-monaco-editor
-```
-
-在 `vite.config.js` 中配置:
-
-```javascript
-import monacoEditorPlugin from 'vite-plugin-monaco-editor'
-
-export default {
-  plugins: [
-    monacoEditorPlugin({})
-  ]
-}
-```
-
----
-
-## 使用场景
-
-1. **DataFlow 详情页面** - 展示关联的处理脚本
-2. **任务执行历史** - 查看执行时使用的脚本版本
-3. **脚本审核流程** - 代码评审和确认
-4. **调试和排错** - 快速查看脚本内容定位问题
-
----
-
-## 注意事项
-
-1. **脚本内容可能较大**:建议添加加载动画和懒加载机制
-2. **特殊字符转义**:脚本内容中可能包含特殊字符,前端展示时注意 XSS 防护
-3. **编码格式**:脚本文件统一使用 UTF-8 编码
-4. **缓存策略**:可根据业务需求添加前端缓存,减少重复请求
-
----
-
-## 更新日志
-
-| 版本 | 日期 | 更新内容 |
-|------|------|----------|
-| v1.0.0 | 2025-01-05 | 初始版本,支持 Python 脚本获取和展示 |

+ 0 - 107
docs/api_interface_labellist.md

@@ -1,107 +0,0 @@
-# /labellist 接口说明(DataLabel 列表查询)
-
-本文档描述数据接口模块提供的 DataLabel 列表查询接口,便于前端接入与调试。
-
-## 基本信息
-- **URL**:`/labellist`(最终完整路径取决于 `data_interface` 蓝图注册前缀,例如 `/api/data_interface/labellist`)
-- **方法**:`POST`
-- **内容类型**:`application/json`
-- **返回格式**:`application/json`
-
-## 请求参数(JSON Body)
-| 字段 | 类型 | 必填 | 说明 |
-| --- | --- | --- | --- |
-| current | int | 否 | 页码,默认 1 |
-| size | int | 否 | 每页条数,默认 10 |
-| name_en | str | 否 | 标签英文名模糊匹配 |
-| name_zh | str | 否 | 标签中文名模糊匹配 |
-| category_filter | dict/list/str | 否 | 分类过滤。支持:<br>- `dict`:键为属性名,值为匹配内容,如 `{ "category": "质量", "scope": "公共" }`<br>- `list`:元素为 `{ "field": "...", "value": "..." }` 或单键值对,如 `[{"field":"category","value":"质量"},{"group":"公共"}]`<br>- `str`:等同于按 `category` 字段模糊匹配 |
-| group | str | 否 | 分组名模糊匹配 |
-
-说明:
-- 所有字符串匹配均使用 Cypher `CONTAINS`(大小写敏感视 Neo4j 配置而定)。
-- `category_filter` 会按提供的多个条件叠加 `AND` 过滤。
-
-## 响应字段
-成功时(`code=200`,`message="success"`):
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "records": [
-      {
-        "id": 123,
-        "name_zh": "示例标签",
-        "name_en": "sample_label",
-        "category": "质量",
-        "group": "公共",
-        "describe": null,
-        "scope": null,
-        "number": 4
-      }
-    ],
-    "total": 57,
-    "size": 10,
-    "current": 1
-  }
-}
-```
-
-失败时(例如参数错误或 Neo4j 异常):
-```json
-{
-  "code": 500,
-  "message": "错误描述",
-  "data": {}
-}
-```
-
-## 请求示例
-```http
-POST /labellist
-Content-Type: application/json
-
-{
-  "current": 1,
-  "size": 10,
-  "name_zh": "标签",
-  "category_filter": [
-    {"field": "category", "value": "质量"},
-    {"field": "scope", "value": "公共"}
-  ],
-  "group": "模型"
-}
-```
-
-## 返回示例
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "records": [
-      {
-        "id": 321,
-        "name_zh": "标签-质量",
-        "name_en": "label_quality",
-        "category": "质量",
-        "group": "模型",
-        "describe": null,
-        "scope": null,
-        "number": 2
-      }
-    ],
-    "total": 5,
-    "size": 10,
-    "current": 1
-  }
-}
-```
-
-## 前端对接提示
-- 必须以 `POST + JSON` 调用;若使用 Fetch/axios,设置 `headers: { "Content-Type": "application/json" }`。
-- 分页字段 `current`、`size` 需为整数,未传时后端使用默认值。
-- `category_filter` 支持多条件 AND 过滤,请确保字段名为合法的 Neo4j 属性名(只含字母、数字、下划线,且非数字开头)。
-- 返回的 `number` 字段表示该标签入度+出度关系数量,可用于前端展示关联数。
-

+ 0 - 521
docs/api_review_create_guide.md

@@ -1,521 +0,0 @@
-# 元数据审核记录创建接口 - 前端开发指南
-
-## 接口概述
-
-该接口用于创建元数据审核记录,支持前端页面发起两个元数据的对比审核请求(如疑似重复、变动审核、合并请求等场景)。
-
-## 基本信息
-
-| 项目 | 说明 |
-|------|------|
-| **接口路径** | `/api/meta_data/review/create` |
-| **请求方法** | `POST` |
-| **Content-Type** | `application/json` |
-| **认证方式** | 无(如需认证请参考项目认证配置) |
-
-## 请求参数
-
-### 请求体 (Request Body)
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| `record_type` | string | ✅ 是 | 审核记录类型,可选值:`redundancy`(疑似重复)、`change`(疑似变动)、`merge`(合并请求) |
-| `source` | string | 否 | 触发来源,默认值:`"manual"` |
-| `meta1` | object | ✅ 是 | 第一个元数据信息(主元数据) |
-| `meta2` | object | ✅ 是 | 第二个元数据信息(候选/对比元数据) |
-| `notes` | string | 否 | 备注信息 |
-
-### 元数据对象结构 (meta1 / meta2)
-
-| 字段名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| `id` | number | ✅ 是 | 元数据节点 ID(Neo4j 节点 ID) |
-| `name_zh` | string | ✅ 是 | 元数据中文名称 |
-| `name_en` | string | ✅ 是 | 元数据英文名称 |
-| `data_type` | string | ✅ 是 | 数据类型(如 `varchar(255)`、`integer` 等) |
-| `status` | boolean | ✅ 是 | 元数据状态(`true`:启用,`false`:禁用) |
-
-### 请求示例
-
-```json
-{
-  "record_type": "redundancy",
-  "source": "manual",
-  "meta1": {
-    "id": 1001,
-    "name_zh": "仓库名称",
-    "name_en": "warehouse_name",
-    "data_type": "varchar(255)",
-    "status": true
-  },
-  "meta2": {
-    "id": 1002,
-    "name_zh": "仓库名",
-    "name_en": "wh_name",
-    "data_type": "varchar(100)",
-    "status": true
-  },
-  "notes": "疑似重复元数据,需要人工审核确认"
-}
-```
-
-## 响应格式
-
-### 成功响应
-
-```json
-{
-  "code": 200,
-  "message": "操作成功",
-  "data": {
-    "record": {
-      "id": 1,
-      "record_type": "redundancy",
-      "source": "manual",
-      "business_domain_id": null,
-      "new_meta": {
-        "id": 1001,
-        "name_zh": "仓库名称",
-        "name_en": "warehouse_name",
-        "data_type": "varchar(255)",
-        "status": true
-      },
-      "candidates": [
-        {
-          "id": 1002,
-          "name_zh": "仓库名",
-          "name_en": "wh_name",
-          "data_type": "varchar(100)",
-          "status": true
-        }
-      ],
-      "old_meta": null,
-      "status": "pending",
-      "resolution_action": null,
-      "resolution_payload": null,
-      "notes": "疑似重复元数据,需要人工审核确认",
-      "created_at": "2024-01-15T10:30:00",
-      "updated_at": "2024-01-15T10:30:00",
-      "resolved_at": null,
-      "resolved_by": null
-    },
-    "message": "审核记录创建成功,请前往数据审核页面进行处理"
-  }
-}
-```
-
-### 失败响应
-
-```json
-{
-  "code": 500,
-  "message": "错误描述信息",
-  "data": null,
-  "error": "详细错误信息(可选)"
-}
-```
-
-## 返回码说明
-
-| code | message | 说明 |
-|------|---------|------|
-| 200 | 操作成功 | 审核记录创建成功 |
-| 500 | record_type 不能为空 | 缺少必填参数 record_type |
-| 500 | record_type 必须是 redundancy、change 或 merge 之一 | record_type 值不合法 |
-| 500 | meta1 不能为空且必须是对象 | meta1 参数缺失或格式错误 |
-| 500 | meta2 不能为空且必须是对象 | meta2 参数缺失或格式错误 |
-| 500 | meta1 缺少必要字段: {field} | meta1 缺少必填字段 |
-| 500 | meta2 缺少必要字段: {field} | meta2 缺少必填字段 |
-| 500 | 请求数据格式错误,应为 JSON 对象 | 请求体不是有效的 JSON |
-| 500 | 创建审核记录失败 | 服务器内部错误 |
-
-## record_type 枚举说明
-
-| 值 | 含义 | 使用场景 |
-|----|------|----------|
-| `redundancy` | 疑似重复 | 两个元数据名称相似,可能是重复定义 |
-| `change` | 疑似变动 | 元数据定义发生变化,需要审核确认 |
-| `merge` | 合并请求 | 请求将两个元数据合并为一个 |
-
-## Vue 3 接入示例
-
-### 1. API 请求封装
-
-```typescript
-// src/api/metaReview.ts
-import request from '@/utils/request'
-
-// 元数据对象类型定义
-export interface MetaInfo {
-  id: number
-  name_zh: string
-  name_en: string
-  data_type: string
-  status: boolean
-}
-
-// 创建审核记录请求参数
-export interface CreateReviewParams {
-  record_type: 'redundancy' | 'change' | 'merge'
-  source?: string
-  meta1: MetaInfo
-  meta2: MetaInfo
-  notes?: string
-}
-
-// 审核记录响应数据
-export interface ReviewRecord {
-  id: number
-  record_type: string
-  source: string
-  business_domain_id: number | null
-  new_meta: MetaInfo
-  candidates: MetaInfo[]
-  old_meta: MetaInfo | null
-  status: string
-  resolution_action: string | null
-  resolution_payload: Record<string, any> | null
-  notes: string | null
-  created_at: string
-  updated_at: string
-  resolved_at: string | null
-  resolved_by: string | null
-}
-
-// API 响应类型
-export interface ApiResponse<T> {
-  code: number
-  message: string
-  data: T
-  error?: string
-}
-
-// 创建审核记录
-export function createMetaReview(params: CreateReviewParams) {
-  return request.post<ApiResponse<{
-    record: ReviewRecord
-    message: string
-  }>>('/api/meta_data/review/create', params)
-}
-```
-
-### 2. 组件中使用
-
-```vue
-<!-- src/views/meta/ReviewCreate.vue -->
-<template>
-  <div class="review-create">
-    <el-card>
-      <template #header>
-        <span>创建元数据审核记录</span>
-      </template>
-
-      <el-form
-        ref="formRef"
-        :model="formData"
-        :rules="formRules"
-        label-width="100px"
-      >
-        <!-- 审核类型 -->
-        <el-form-item label="审核类型" prop="record_type">
-          <el-select v-model="formData.record_type" placeholder="请选择审核类型">
-            <el-option label="疑似重复" value="redundancy" />
-            <el-option label="疑似变动" value="change" />
-            <el-option label="合并请求" value="merge" />
-          </el-select>
-        </el-form-item>
-
-        <!-- 元数据1 -->
-        <el-divider content-position="left">主元数据信息</el-divider>
-        <el-form-item label="节点ID" prop="meta1.id">
-          <el-input-number v-model="formData.meta1.id" :min="1" />
-        </el-form-item>
-        <el-form-item label="中文名" prop="meta1.name_zh">
-          <el-input v-model="formData.meta1.name_zh" />
-        </el-form-item>
-        <el-form-item label="英文名" prop="meta1.name_en">
-          <el-input v-model="formData.meta1.name_en" />
-        </el-form-item>
-        <el-form-item label="数据类型" prop="meta1.data_type">
-          <el-input v-model="formData.meta1.data_type" placeholder="如 varchar(255)" />
-        </el-form-item>
-        <el-form-item label="状态" prop="meta1.status">
-          <el-switch v-model="formData.meta1.status" />
-        </el-form-item>
-
-        <!-- 元数据2 -->
-        <el-divider content-position="left">对比元数据信息</el-divider>
-        <el-form-item label="节点ID" prop="meta2.id">
-          <el-input-number v-model="formData.meta2.id" :min="1" />
-        </el-form-item>
-        <el-form-item label="中文名" prop="meta2.name_zh">
-          <el-input v-model="formData.meta2.name_zh" />
-        </el-form-item>
-        <el-form-item label="英文名" prop="meta2.name_en">
-          <el-input v-model="formData.meta2.name_en" />
-        </el-form-item>
-        <el-form-item label="数据类型" prop="meta2.data_type">
-          <el-input v-model="formData.meta2.data_type" placeholder="如 varchar(255)" />
-        </el-form-item>
-        <el-form-item label="状态" prop="meta2.status">
-          <el-switch v-model="formData.meta2.status" />
-        </el-form-item>
-
-        <!-- 备注 -->
-        <el-divider />
-        <el-form-item label="备注" prop="notes">
-          <el-input
-            v-model="formData.notes"
-            type="textarea"
-            :rows="3"
-            placeholder="请输入备注信息(可选)"
-          />
-        </el-form-item>
-
-        <!-- 提交按钮 -->
-        <el-form-item>
-          <el-button type="primary" :loading="loading" @click="handleSubmit">
-            提交审核
-          </el-button>
-          <el-button @click="handleReset">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref, reactive } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import type { FormInstance, FormRules } from 'element-plus'
-import { createMetaReview, type CreateReviewParams, type MetaInfo } from '@/api/metaReview'
-
-const formRef = ref<FormInstance>()
-const loading = ref(false)
-
-// 表单数据
-const formData = reactive<CreateReviewParams>({
-  record_type: 'redundancy',
-  source: 'manual',
-  meta1: {
-    id: 0,
-    name_zh: '',
-    name_en: '',
-    data_type: 'varchar(255)',
-    status: true,
-  },
-  meta2: {
-    id: 0,
-    name_zh: '',
-    name_en: '',
-    data_type: 'varchar(255)',
-    status: true,
-  },
-  notes: '',
-})
-
-// 表单校验规则
-const formRules: FormRules = {
-  record_type: [
-    { required: true, message: '请选择审核类型', trigger: 'change' },
-  ],
-  'meta1.id': [
-    { required: true, message: '请输入元数据1节点ID', trigger: 'blur' },
-  ],
-  'meta1.name_zh': [
-    { required: true, message: '请输入元数据1中文名', trigger: 'blur' },
-  ],
-  'meta1.name_en': [
-    { required: true, message: '请输入元数据1英文名', trigger: 'blur' },
-  ],
-  'meta1.data_type': [
-    { required: true, message: '请输入元数据1数据类型', trigger: 'blur' },
-  ],
-  'meta2.id': [
-    { required: true, message: '请输入元数据2节点ID', trigger: 'blur' },
-  ],
-  'meta2.name_zh': [
-    { required: true, message: '请输入元数据2中文名', trigger: 'blur' },
-  ],
-  'meta2.name_en': [
-    { required: true, message: '请输入元数据2英文名', trigger: 'blur' },
-  ],
-  'meta2.data_type': [
-    { required: true, message: '请输入元数据2数据类型', trigger: 'blur' },
-  ],
-}
-
-// 提交表单
-const handleSubmit = async () => {
-  if (!formRef.value) return
-
-  await formRef.value.validate(async (valid) => {
-    if (!valid) return
-
-    loading.value = true
-    try {
-      const res = await createMetaReview(formData)
-      
-      if (res.data.code === 200) {
-        ElMessageBox.confirm(
-          res.data.data.message,
-          '创建成功',
-          {
-            confirmButtonText: '前往审核页面',
-            cancelButtonText: '继续创建',
-            type: 'success',
-          }
-        ).then(() => {
-          // 跳转到审核页面
-          // router.push('/meta/review/list')
-        }).catch(() => {
-          // 重置表单继续创建
-          handleReset()
-        })
-      } else {
-        ElMessage.error(res.data.message || '创建失败')
-      }
-    } catch (error: any) {
-      ElMessage.error(error.message || '请求失败')
-    } finally {
-      loading.value = false
-    }
-  })
-}
-
-// 重置表单
-const handleReset = () => {
-  formRef.value?.resetFields()
-}
-</script>
-
-<style scoped>
-.review-create {
-  padding: 20px;
-}
-</style>
-```
-
-### 3. Axios 请求封装参考
-
-```typescript
-// src/utils/request.ts
-import axios from 'axios'
-import { ElMessage } from 'element-plus'
-
-const request = axios.create({
-  baseURL: import.meta.env.VITE_API_BASE_URL || '',
-  timeout: 30000,
-  headers: {
-    'Content-Type': 'application/json',
-  },
-})
-
-// 响应拦截器
-request.interceptors.response.use(
-  (response) => {
-    return response
-  },
-  (error) => {
-    const message = error.response?.data?.message || error.message || '请求失败'
-    ElMessage.error(message)
-    return Promise.reject(error)
-  }
-)
-
-export default request
-```
-
-## 快速调用示例
-
-### 使用 fetch
-
-```javascript
-async function createReviewRecord() {
-  const response = await fetch('/api/meta_data/review/create', {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      record_type: 'redundancy',
-      meta1: {
-        id: 1001,
-        name_zh: '仓库名称',
-        name_en: 'warehouse_name',
-        data_type: 'varchar(255)',
-        status: true
-      },
-      meta2: {
-        id: 1002,
-        name_zh: '仓库名',
-        name_en: 'wh_name',
-        data_type: 'varchar(100)',
-        status: true
-      },
-      notes: '疑似重复'
-    })
-  })
-  
-  const result = await response.json()
-  
-  if (result.code === 200) {
-    console.log('创建成功:', result.data.record)
-    alert(result.data.message)
-  } else {
-    console.error('创建失败:', result.message)
-  }
-}
-```
-
-### 使用 axios
-
-```javascript
-import axios from 'axios'
-
-axios.post('/api/meta_data/review/create', {
-  record_type: 'merge',
-  meta1: {
-    id: 1001,
-    name_zh: '仓库名称',
-    name_en: 'warehouse_name',
-    data_type: 'varchar(255)',
-    status: true
-  },
-  meta2: {
-    id: 1002,
-    name_zh: '仓库名',
-    name_en: 'wh_name',
-    data_type: 'varchar(100)',
-    status: true
-  }
-}).then(res => {
-  if (res.data.code === 200) {
-    console.log('审核记录ID:', res.data.data.record.id)
-  }
-}).catch(err => {
-  console.error('请求失败:', err)
-})
-```
-
-## 相关接口
-
-| 接口 | 方法 | 说明 |
-|------|------|------|
-| `/api/meta_data/review/list` | POST | 查询审核记录列表 |
-| `/api/meta_data/review/detail` | GET | 获取审核记录详情 |
-| `/api/meta_data/review/resolve` | POST | 处理审核记录 |
-
-## 注意事项
-
-1. **元数据 ID**:`meta1.id` 和 `meta2.id` 应为有效的 Neo4j 节点 ID
-2. **状态值**:`status` 为布尔值,`true` 表示启用,`false` 表示禁用
-3. **record_type**:必须是三个枚举值之一,否则会返回错误
-4. **幂等性**:该接口不具备幂等性,重复调用会创建多条审核记录
-5. **后续处理**:创建成功后,需要前往数据审核页面(`/api/meta_data/review/list`)查看并处理审核记录
-
-## 更新日志
-
-| 日期 | 版本 | 说明 |
-|------|------|------|
-| 2024-01-15 | v1.0 | 初始版本 |

+ 0 - 840
docs/api_review_resolve_guide.md

@@ -1,840 +0,0 @@
-# 元数据审核处理接口 API 前端开发指南
-
-本文档面向前端开发人员,用于开发"元数据审核处理"功能。
-
-## 接口基本信息
-
-| 属性 | 值 |
-| --- | --- |
-| 接口路径 | `POST /api/meta/review/resolve` |
-| 完整URL | `http://{host}:{port}/api/meta/review/resolve` |
-| Content-Type | `application/json` |
-| 请求方式 | POST |
-
-## 统一返回格式
-
-### 成功响应
-
-```json
-{
-  "code": 200,
-  "message": "操作成功",
-  "data": {
-    "id": 123,
-    "record_type": "redundancy",
-    "status": "resolved",
-    "resolution_action": "alias",
-    "resolution_payload": {
-      "primary_meta_id": 100,
-      "alias_meta_id": 200
-    },
-    "resolved_by": "admin",
-    "resolved_at": "2025-01-09T10:30:00.000000",
-    "notes": "合并重复元数据"
-  }
-}
-```
-
-### 失败响应
-
-```json
-{
-  "code": 500,
-  "message": "错误原因",
-  "data": null,
-  "error": "详细错误信息(可选)"
-}
-```
-
-## 请求参数
-
-### 公共参数
-
-| 字段 | 类型 | 必填 | 说明 |
-| --- | --- | --- | --- |
-| id | int | 是 | 审核记录ID |
-| action | string | 是 | 处理动作,可选值见下方说明 |
-| payload | object | 否 | 动作参数,根据action不同而变化 |
-| resolved_by | string | 否 | 处理人标识(建议传登录用户名/工号) |
-| notes | string | 否 | 处理备注 |
-
-### action 可选值
-
-| action 值 | 用途 | 适用场景 |
-| --- | --- | --- |
-| `alias` | 设置元数据别名关系 | redundancy(疑似冗余) |
-| `create_new` | 创建新元数据 | redundancy(疑似冗余) |
-| `accept_change` | 接受元数据变动 | change(疑似变动) |
-| `reject_change` | 拒绝元数据变动 | change(疑似变动) |
-| `ignore` | 忽略该记录 | 任意类型 |
-
----
-
-## action=alias(设置元数据别名关系)
-
-### 功能说明
-
-在 Neo4j 的 DataMeta 节点之间建立 ALIAS 关系,用于合并重复/相似的元数据。
-
-**核心行为**:
-- 创建 `(alias_meta)-[:ALIAS]->(primary_meta)` 关系
-- 将所有原先指向 `alias_meta` 的 ALIAS 关系转移到 `primary_meta`
-- `primary_meta` 已有的 ALIAS 关系保持不变
-- BusinessDomain 的 INCLUDES 关系不受影响
-
-```
-操作前:
-  [other_alias_1] --ALIAS--> [alias_meta]
-  [other_alias_2] --ALIAS--> [alias_meta]
-  [existing_alias] --ALIAS--> [primary_meta]
-
-操作后:
-  [other_alias_1] --ALIAS--> [primary_meta]
-  [other_alias_2] --ALIAS--> [primary_meta]
-  [alias_meta] --ALIAS--> [primary_meta]
-  [existing_alias] --ALIAS--> [primary_meta]
-```
-
-### payload 参数
-
-| 字段 | 类型 | 必填 | 说明 |
-| --- | --- | --- | --- |
-| primary_meta_id | int | 是 | 主元数据 Neo4j ID(作为别名目标) |
-| alias_meta_id | int | 是 | 别名元数据 Neo4j ID(将成为别名) |
-
-### 请求示例
-
-```json
-{
-  "id": 1001,
-  "action": "alias",
-  "payload": {
-    "primary_meta_id": 100,
-    "alias_meta_id": 200
-  },
-  "resolved_by": "admin",
-  "notes": "将重复元数据合并为别名"
-}
-```
-
-### 响应示例
-
-```json
-{
-  "code": 200,
-  "message": "操作成功",
-  "data": {
-    "id": 1001,
-    "record_type": "redundancy",
-    "business_domain_id": 345,
-    "new_meta": {
-      "name_zh": "科室名称",
-      "name_en": "DEPT_NAME",
-      "data_type": "varchar(50)"
-    },
-    "candidates": [...],
-    "status": "resolved",
-    "resolution_action": "alias",
-    "resolution_payload": {
-      "primary_meta_id": 100,
-      "alias_meta_id": 200
-    },
-    "resolved_by": "admin",
-    "resolved_at": "2025-01-09T10:30:00.000000",
-    "notes": "将重复元数据合并为别名",
-    "created_at": "2025-01-08T09:00:00.000000",
-    "updated_at": "2025-01-09T10:30:00.000000"
-  }
-}
-```
-
-### 错误信息
-
-| 错误信息 | 原因 |
-| --- | --- |
-| `payload.primary_meta_id 不能为空` | 未提供 primary_meta_id |
-| `payload.alias_meta_id 不能为空` | 未提供 alias_meta_id |
-| `primary_meta_id 和 alias_meta_id 不能相同` | 两个ID相同 |
-
----
-
-## action=create_new(创建新元数据)
-
-### payload 参数
-
-| 字段 | 类型 | 必填 | 说明 |
-| --- | --- | --- | --- |
-| new_name_zh | string | 是 | 新元数据中文名(需与现有区分) |
-
-### 请求示例
-
-```json
-{
-  "id": 1002,
-  "action": "create_new",
-  "payload": {
-    "new_name_zh": "HIS科室名称(新)"
-  },
-  "resolved_by": "admin"
-}
-```
-
-### 错误信息
-
-| 错误信息 | 原因 |
-| --- | --- |
-| `记录缺少 business_domain_id,无法执行 create_new` | 审核记录无业务领域关联 |
-| `payload.new_name_zh 不能为空` | 未提供中文名 |
-| `创建新元数据失败` | Neo4j 创建节点失败 |
-
----
-
-## action=accept_change(接受变动)
-
-### payload 参数
-
-| 字段 | 类型 | 必填 | 说明 |
-| --- | --- | --- | --- |
-| meta_id | int | 否 | 目标元数据ID,不传时使用 old_meta.meta_id |
-
-### 请求示例
-
-```json
-{
-  "id": 2001,
-  "action": "accept_change",
-  "payload": {
-    "meta_id": 789
-  },
-  "resolved_by": "admin",
-  "notes": "接受字段长度调整"
-}
-```
-
-### 错误信息
-
-| 错误信息 | 原因 |
-| --- | --- |
-| `无法确定需要更新的 meta_id` | 未提供且记录中无 old_meta.meta_id |
-
----
-
-## action=reject_change(拒绝变动)
-
-### 请求示例
-
-```json
-{
-  "id": 2002,
-  "action": "reject_change",
-  "resolved_by": "admin",
-  "notes": "变动不合规,暂不更新"
-}
-```
-
----
-
-## action=ignore(忽略)
-
-### 请求示例
-
-```json
-{
-  "id": 3001,
-  "action": "ignore",
-  "resolved_by": "admin"
-}
-```
-
----
-
-## 公共错误信息
-
-| 错误信息 | 原因 |
-| --- | --- |
-| `请求数据格式错误,应为 JSON 对象` | 请求体不是有效的 JSON 对象 |
-| `id 不能为空` | 未提供审核记录ID |
-| `action 不能为空` | 未提供处理动作 |
-| `记录不存在` | 审核记录ID不存在 |
-| `记录已处理,无法重复处理` | 审核记录状态非 pending |
-| `不支持的action: xxx` | action 值不在允许列表中 |
-| `处理审核记录失败` | 服务器内部错误 |
-
----
-
-## Vue 示例代码
-
-### 1. API 封装
-
-```javascript
-// api/metaReview.js
-import axios from 'axios'
-
-const API_BASE = '/api/meta'
-
-/**
- * 处理审核记录
- * @param {Object} params - 请求参数
- * @param {number} params.id - 审核记录ID
- * @param {string} params.action - 处理动作
- * @param {Object} params.payload - 动作参数
- * @param {string} params.resolved_by - 处理人
- * @param {string} params.notes - 备注
- * @returns {Promise}
- */
-export function resolveReviewRecord(params) {
-  return axios.post(`${API_BASE}/review/resolve`, params)
-}
-
-/**
- * 设置元数据别名关系
- * @param {number} recordId - 审核记录ID
- * @param {number} primaryMetaId - 主元数据ID
- * @param {number} aliasMetaId - 别名元数据ID
- * @param {string} resolvedBy - 处理人
- * @param {string} notes - 备注
- * @returns {Promise}
- */
-export function setMetaAlias(recordId, primaryMetaId, aliasMetaId, resolvedBy, notes = '') {
-  return resolveReviewRecord({
-    id: recordId,
-    action: 'alias',
-    payload: {
-      primary_meta_id: primaryMetaId,
-      alias_meta_id: aliasMetaId
-    },
-    resolved_by: resolvedBy,
-    notes
-  })
-}
-
-/**
- * 创建新元数据
- * @param {number} recordId - 审核记录ID
- * @param {string} newNameZh - 新元数据中文名
- * @param {string} resolvedBy - 处理人
- * @returns {Promise}
- */
-export function createNewMeta(recordId, newNameZh, resolvedBy) {
-  return resolveReviewRecord({
-    id: recordId,
-    action: 'create_new',
-    payload: {
-      new_name_zh: newNameZh
-    },
-    resolved_by: resolvedBy
-  })
-}
-
-/**
- * 接受元数据变动
- * @param {number} recordId - 审核记录ID
- * @param {number} metaId - 目标元数据ID(可选)
- * @param {string} resolvedBy - 处理人
- * @param {string} notes - 备注
- * @returns {Promise}
- */
-export function acceptChange(recordId, metaId, resolvedBy, notes = '') {
-  const payload = metaId ? { meta_id: metaId } : {}
-  return resolveReviewRecord({
-    id: recordId,
-    action: 'accept_change',
-    payload,
-    resolved_by: resolvedBy,
-    notes
-  })
-}
-
-/**
- * 拒绝元数据变动
- * @param {number} recordId - 审核记录ID
- * @param {string} resolvedBy - 处理人
- * @param {string} notes - 备注
- * @returns {Promise}
- */
-export function rejectChange(recordId, resolvedBy, notes = '') {
-  return resolveReviewRecord({
-    id: recordId,
-    action: 'reject_change',
-    resolved_by: resolvedBy,
-    notes
-  })
-}
-
-/**
- * 忽略审核记录
- * @param {number} recordId - 审核记录ID
- * @param {string} resolvedBy - 处理人
- * @returns {Promise}
- */
-export function ignoreRecord(recordId, resolvedBy) {
-  return resolveReviewRecord({
-    id: recordId,
-    action: 'ignore',
-    resolved_by: resolvedBy
-  })
-}
-```
-
-### 2. 设置别名组件
-
-```vue
-<template>
-  <div class="alias-dialog">
-    <el-dialog
-      title="设置元数据别名"
-      :visible.sync="dialogVisible"
-      width="600px"
-      @close="handleClose"
-    >
-      <el-form
-        ref="formRef"
-        :model="form"
-        :rules="rules"
-        label-width="120px"
-      >
-        <el-form-item label="主元数据" prop="primaryMetaId">
-          <el-select
-            v-model="form.primaryMetaId"
-            placeholder="请选择主元数据"
-            filterable
-            style="width: 100%"
-          >
-            <el-option
-              v-for="item in candidateList"
-              :key="item.candidate_meta_id"
-              :label="`${item.name_zh} (${item.name_en || '-'})`"
-              :value="item.candidate_meta_id"
-            />
-          </el-select>
-          <div class="form-tip">选择作为主元数据的节点,其他节点将成为它的别名</div>
-        </el-form-item>
-
-        <el-form-item label="别名元数据" prop="aliasMetaId">
-          <el-select
-            v-model="form.aliasMetaId"
-            placeholder="请选择别名元数据"
-            filterable
-            style="width: 100%"
-          >
-            <el-option
-              v-for="item in candidateList"
-              :key="item.candidate_meta_id"
-              :label="`${item.name_zh} (${item.name_en || '-'})`"
-              :value="item.candidate_meta_id"
-              :disabled="item.candidate_meta_id === form.primaryMetaId"
-            />
-          </el-select>
-          <div class="form-tip">选择要降级为别名的元数据节点</div>
-        </el-form-item>
-
-        <el-form-item label="备注">
-          <el-input
-            v-model="form.notes"
-            type="textarea"
-            :rows="3"
-            placeholder="请输入处理备注(可选)"
-          />
-        </el-form-item>
-      </el-form>
-
-      <el-alert
-        v-if="form.aliasMetaId"
-        type="warning"
-        :closable="false"
-        style="margin-top: 16px"
-      >
-        <template #title>
-          <span>操作提示</span>
-        </template>
-        <div>
-          <p>执行此操作后:</p>
-          <ul>
-            <li>别名元数据将指向主元数据</li>
-            <li>原先指向别名元数据的所有 ALIAS 关系将转移到主元数据</li>
-            <li>业务领域的 INCLUDES 关系不受影响</li>
-          </ul>
-        </div>
-      </el-alert>
-
-      <template #footer>
-        <el-button @click="handleClose">取消</el-button>
-        <el-button
-          type="primary"
-          :loading="loading"
-          @click="handleSubmit"
-        >
-          确定
-        </el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import { setMetaAlias } from '@/api/metaReview'
-
-export default {
-  name: 'AliasDialog',
-  props: {
-    visible: {
-      type: Boolean,
-      default: false
-    },
-    recordId: {
-      type: Number,
-      required: true
-    },
-    candidateList: {
-      type: Array,
-      default: () => []
-    }
-  },
-  data() {
-    return {
-      loading: false,
-      form: {
-        primaryMetaId: null,
-        aliasMetaId: null,
-        notes: ''
-      },
-      rules: {
-        primaryMetaId: [
-          { required: true, message: '请选择主元数据', trigger: 'change' }
-        ],
-        aliasMetaId: [
-          { required: true, message: '请选择别名元数据', trigger: 'change' },
-          {
-            validator: (rule, value, callback) => {
-              if (value && value === this.form.primaryMetaId) {
-                callback(new Error('主元数据和别名元数据不能相同'))
-              } else {
-                callback()
-              }
-            },
-            trigger: 'change'
-          }
-        ]
-      }
-    }
-  },
-  computed: {
-    dialogVisible: {
-      get() {
-        return this.visible
-      },
-      set(val) {
-        this.$emit('update:visible', val)
-      }
-    }
-  },
-  methods: {
-    handleClose() {
-      this.$refs.formRef?.resetFields()
-      this.form = {
-        primaryMetaId: null,
-        aliasMetaId: null,
-        notes: ''
-      }
-      this.dialogVisible = false
-    },
-
-    async handleSubmit() {
-      try {
-        await this.$refs.formRef.validate()
-      } catch {
-        return
-      }
-
-      this.loading = true
-      try {
-        const currentUser = this.$store.getters.userInfo?.username || 'unknown'
-        const res = await setMetaAlias(
-          this.recordId,
-          this.form.primaryMetaId,
-          this.form.aliasMetaId,
-          currentUser,
-          this.form.notes
-        )
-
-        if (res.data.code === 200) {
-          this.$message.success('别名关系设置成功')
-          this.$emit('success', res.data.data)
-          this.handleClose()
-        } else {
-          this.$message.error(res.data.message || '操作失败')
-        }
-      } catch (error) {
-        console.error('设置别名失败:', error)
-        this.$message.error(error.response?.data?.message || '网络错误,请稍后重试')
-      } finally {
-        this.loading = false
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.form-tip {
-  color: #909399;
-  font-size: 12px;
-  line-height: 1.5;
-  margin-top: 4px;
-}
-</style>
-```
-
-### 3. 审核详情页面示例
-
-```vue
-<template>
-  <div class="review-detail">
-    <el-card>
-      <template #header>
-        <div class="card-header">
-          <span>审核记录详情 #{{ record.id }}</span>
-          <el-tag :type="statusTagType">{{ statusText }}</el-tag>
-        </div>
-      </template>
-
-      <!-- 记录基本信息 -->
-      <el-descriptions :column="2" border>
-        <el-descriptions-item label="记录类型">
-          {{ record.record_type === 'redundancy' ? '疑似冗余' : '疑似变动' }}
-        </el-descriptions-item>
-        <el-descriptions-item label="业务领域ID">
-          {{ record.business_domain_id }}
-        </el-descriptions-item>
-        <el-descriptions-item label="创建时间">
-          {{ record.created_at }}
-        </el-descriptions-item>
-        <el-descriptions-item label="更新时间">
-          {{ record.updated_at }}
-        </el-descriptions-item>
-      </el-descriptions>
-
-      <!-- 新元数据信息 -->
-      <h4>新解析元数据</h4>
-      <el-descriptions :column="2" border>
-        <el-descriptions-item label="中文名">
-          {{ record.new_meta?.name_zh }}
-        </el-descriptions-item>
-        <el-descriptions-item label="英文名">
-          {{ record.new_meta?.name_en }}
-        </el-descriptions-item>
-        <el-descriptions-item label="数据类型">
-          {{ record.new_meta?.data_type }}
-        </el-descriptions-item>
-      </el-descriptions>
-
-      <!-- 冗余场景:候选列表 -->
-      <template v-if="record.record_type === 'redundancy'">
-        <h4>候选元数据列表</h4>
-        <el-table :data="record.candidates" border>
-          <el-table-column prop="candidate_meta_id" label="ID" width="80" />
-          <el-table-column prop="name_zh" label="中文名" />
-          <el-table-column prop="name_en" label="英文名" />
-          <el-table-column prop="data_type" label="数据类型" />
-          <el-table-column label="差异字段">
-            <template #default="{ row }">
-              <el-tag
-                v-for="field in row.diff_fields"
-                :key="field"
-                size="small"
-                style="margin-right: 4px"
-              >
-                {{ field }}
-              </el-tag>
-            </template>
-          </el-table-column>
-        </el-table>
-      </template>
-
-      <!-- 操作按钮 -->
-      <div v-if="record.status === 'pending'" class="action-buttons">
-        <template v-if="record.record_type === 'redundancy'">
-          <el-button type="primary" @click="showAliasDialog = true">
-            设为别名
-          </el-button>
-          <el-button type="success" @click="showCreateDialog = true">
-            创建新元数据
-          </el-button>
-        </template>
-        <template v-else>
-          <el-button type="primary" @click="handleAcceptChange">
-            接受变动
-          </el-button>
-          <el-button type="warning" @click="handleRejectChange">
-            拒绝变动
-          </el-button>
-        </template>
-        <el-button @click="handleIgnore">忽略</el-button>
-      </div>
-    </el-card>
-
-    <!-- 别名设置弹窗 -->
-    <alias-dialog
-      :visible.sync="showAliasDialog"
-      :record-id="record.id"
-      :candidate-list="record.candidates"
-      @success="handleSuccess"
-    />
-  </div>
-</template>
-
-<script>
-import AliasDialog from './AliasDialog.vue'
-import { acceptChange, rejectChange, ignoreRecord } from '@/api/metaReview'
-
-export default {
-  name: 'ReviewDetail',
-  components: { AliasDialog },
-  props: {
-    record: {
-      type: Object,
-      required: true
-    }
-  },
-  data() {
-    return {
-      showAliasDialog: false,
-      showCreateDialog: false
-    }
-  },
-  computed: {
-    statusTagType() {
-      const map = {
-        pending: 'warning',
-        resolved: 'success',
-        ignored: 'info'
-      }
-      return map[this.record.status] || 'info'
-    },
-    statusText() {
-      const map = {
-        pending: '待处理',
-        resolved: '已处理',
-        ignored: '已忽略'
-      }
-      return map[this.record.status] || this.record.status
-    }
-  },
-  methods: {
-    getCurrentUser() {
-      return this.$store.getters.userInfo?.username || 'unknown'
-    },
-
-    async handleAcceptChange() {
-      try {
-        await this.$confirm('确定接受此变动吗?', '提示', {
-          type: 'warning'
-        })
-        const res = await acceptChange(
-          this.record.id,
-          this.record.old_meta?.meta_id,
-          this.getCurrentUser()
-        )
-        if (res.data.code === 200) {
-          this.$message.success('操作成功')
-          this.$emit('refresh')
-        } else {
-          this.$message.error(res.data.message)
-        }
-      } catch (e) {
-        if (e !== 'cancel') {
-          this.$message.error('操作失败')
-        }
-      }
-    },
-
-    async handleRejectChange() {
-      try {
-        await this.$confirm('确定拒绝此变动吗?', '提示', {
-          type: 'warning'
-        })
-        const res = await rejectChange(this.record.id, this.getCurrentUser())
-        if (res.data.code === 200) {
-          this.$message.success('操作成功')
-          this.$emit('refresh')
-        } else {
-          this.$message.error(res.data.message)
-        }
-      } catch (e) {
-        if (e !== 'cancel') {
-          this.$message.error('操作失败')
-        }
-      }
-    },
-
-    async handleIgnore() {
-      try {
-        await this.$confirm('确定忽略此记录吗?', '提示', {
-          type: 'warning'
-        })
-        const res = await ignoreRecord(this.record.id, this.getCurrentUser())
-        if (res.data.code === 200) {
-          this.$message.success('操作成功')
-          this.$emit('refresh')
-        } else {
-          this.$message.error(res.data.message)
-        }
-      } catch (e) {
-        if (e !== 'cancel') {
-          this.$message.error('操作失败')
-        }
-      }
-    },
-
-    handleSuccess() {
-      this.$emit('refresh')
-    }
-  }
-}
-</script>
-
-<style scoped>
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-h4 {
-  margin: 20px 0 10px;
-  color: #303133;
-}
-
-.action-buttons {
-  margin-top: 24px;
-  padding-top: 16px;
-  border-top: 1px solid #ebeef5;
-}
-
-.action-buttons .el-button {
-  margin-right: 12px;
-}
-</style>
-```
-
----
-
-## 注意事项
-
-1. **状态限制**:只有 `status=pending` 的记录才能处理,已处理记录会返回错误。
-
-2. **参数验证**:前端应在提交前验证必填参数,避免无效请求。
-
-3. **别名操作不可逆**:alias 操作会修改 Neo4j 中的关系结构,请确保用户确认后再执行。
-
-4. **ID 类型**:所有 ID 参数(record_id、primary_meta_id、alias_meta_id、meta_id)应为整数。
-
-5. **错误处理**:建议统一封装 axios 拦截器处理错误响应,对 `code !== 200` 的情况进行统一提示。
-
----
-
-## 更新日志
-
-| 日期 | 版本 | 更新内容 |
-| --- | --- | --- |
-| 2025-01-09 | v2.0 | `action=alias` 参数变更:使用 `primary_meta_id` 和 `alias_meta_id` 替代原 `candidate_meta_id`,支持 ALIAS 关系重建 |

+ 0 - 309
docs/archive/AUTO_TASK_EXECUTION_FIX.md

@@ -1,309 +0,0 @@
-# 自动任务执行机制修复总结
-
-## 🔧 修复的问题
-
-### 问题1:任务状态未更新为processing ✅ 已修复
-
-**问题描述**:
-- 脚本读取任务后,只是创建通知文件,但没有更新数据库中的任务状态
-
-**修复方案**:
-- 在 `create_task_file()` 函数中添加了 `update_task_status()` 调用
-- 当任务文件创建成功后,自动将任务状态更新为 `processing`
-
-**实现位置**:
-```python
-# scripts/auto_execute_tasks.py
-def create_task_file(task):
-    # ... 创建文件逻辑 ...
-    # 更新数据库中的code_name和code_path
-    update_task_status(
-        task['task_id'], 
-        'processing',  # 状态改为processing
-        code_name=code_name,
-        code_path=code_path
-    )
-```
-
----
-
-### 问题2:未在app/core/data_flow目录生成任务文件 ✅ 已修复
-
-**问题描述**:
-- 脚本只创建了通知文件(.cursor/pending_tasks.json),但没有生成实际的任务文件
-
-**修复方案**:
-- 新增 `create_task_file()` 函数
-- 在指定目录(默认 `app/core/data_flow`)创建Python任务文件
-- 文件包含完整的任务描述和模板代码
-
-**实现特性**:
-1. 自动从任务名称生成安全的文件名(去除特殊字符)
-2. 支持自定义 `code_path` 和 `code_name`
-3. 如果文件已存在,自动添加时间戳避免覆盖
-4. 生成的文件包含完整的任务描述和TODO注释
-
-**文件示例**:
-- 位置:`app/core/data_flow/导入科室对照表.py`
-- 内容:包含任务ID、描述、创建时间等完整信息
-
----
-
-### 问题3:Cursor没有自动执行任务 ✅ 已改进
-
-**问题描述**:
-- 脚本只是打印任务信息,但Cursor没有自动执行
-
-**修复方案**:
-1. **增强任务打印格式**:
-   - 明确的标记:`[AUTO-EXECUTE-TASK]`
-   - 详细的执行指令
-   - 包含MCP工具调用示例
-
-2. **创建任务触发器脚本**:
-   - `scripts/trigger_cursor_execution.py`
-   - 读取 `.cursor/pending_tasks.json`
-   - 以明确格式输出所有processing任务
-
-3. **改进通知文件**:
-   - `.cursor/pending_tasks.json` 包含任务文件和状态信息
-   - Cursor可以读取此文件识别待执行任务
-
-**使用方式**:
-
-#### 方式1:在Cursor中运行触发器脚本
-```
-python scripts/trigger_cursor_execution.py
-```
-
-#### 方式2:直接在Cursor Chat中说
-```
-请检查并执行所有processing任务
-```
-
-#### 方式3:查看pending_tasks.json
-```
-查看 .cursor/pending_tasks.json 文件
-```
-
----
-
-## 📋 新增功能
-
-### 1. 数据库操作函数
-
-#### `get_db_connection()`
-- 统一的数据库连接管理
-- 自动读取配置文件
-- 错误处理
-
-#### `update_task_status(task_id, status, code_name=None, code_path=None)`
-- 更新任务状态
-- 同时更新code_name和code_path(如果提供)
-- 自动更新update_time字段
-
-### 2. 任务文件生成
-
-#### `create_task_file(task)`
-- 自动生成任务文件
-- 更新数据库状态
-- 返回文件路径
-
-**特性**:
-- 文件名安全处理(去除特殊字符)
-- 自动添加时间戳避免覆盖
-- 包含完整的任务元数据
-
-### 3. 触发器脚本
-
-#### `scripts/trigger_cursor_execution.py`
-- 读取processing状态的任务
-- 格式化输出执行指令
-- 供Cursor识别和执行
-
----
-
-## ✅ 验证结果
-
-### 测试场景:Task ID 9
-
-**执行前**:
-- 状态:`pending`
-- 文件:不存在
-
-**执行后**:
-- ✅ 状态:`processing`(已更新)
-- ✅ 文件:`app/core/data_flow/导入科室对照表.py`(已创建)
-- ✅ 通知:`.cursor/pending_tasks.json`(已更新)
-
-**验证命令**:
-```bash
-# 执行一次检查
-python scripts/auto_execute_tasks.py --once
-
-# 检查文件是否存在
-ls app/core/data_flow/导入科室对照表.py
-
-# 查看任务触发器
-python scripts/trigger_cursor_execution.py
-```
-
----
-
-## 🔄 完整工作流程
-
-```
-1. 脚本启动(auto_execute_tasks.py)
-   ↓
-2. 连接数据库,查询 status = 'pending' 的任务
-   ↓
-3. 对每个pending任务:
-   a. 创建任务文件(app/core/data_flow/xxx.py)
-   b. 更新任务状态为 'processing'
-   c. 更新 code_name 和 code_path
-   d. 打印任务详情(供Cursor识别)
-   e. 创建/更新 .cursor/pending_tasks.json
-   ↓
-4. Cursor读取pending_tasks.json或运行trigger_cursor_execution.py
-   ↓
-5. Cursor根据任务描述生成/完善代码
-   ↓
-6. Cursor调用MCP工具更新任务状态为 'completed'
-   ↓
-7. 任务完成!
-```
-
----
-
-## 📝 使用说明
-
-### 启动自动任务执行
-
-```bash
-# 前台运行(可以看到实时输出)
-python scripts/auto_execute_tasks.py
-
-# 后台运行
-python scripts/auto_execute_tasks.py --interval 300 &
-
-# 执行一次检查
-python scripts/auto_execute_tasks.py --once
-```
-
-### 在Cursor中触发执行
-
-#### 方式1:运行触发器脚本
-在Cursor Chat中:
-```
-python scripts/trigger_cursor_execution.py
-```
-
-#### 方式2:直接执行任务
-在Cursor Chat中:
-```
-请检查并执行所有processing任务
-```
-
-#### 方式3:手动查看
-```
-查看 .cursor/pending_tasks.json 文件
-```
-
----
-
-## 🎯 关键改进点
-
-1. **✅ 状态同步**:
-   - 任务文件创建 → 状态自动更新为processing
-   - 确保数据库和文件系统状态一致
-
-2. **✅ 文件生成**:
-   - 自动在指定目录创建任务文件
-   - 文件名安全处理
-   - 包含完整任务信息
-
-3. **✅ Cursor触发**:
-   - 明确的执行指令格式
-   - 多种触发方式
-   - 清晰的MCP工具调用示例
-
-4. **✅ 错误处理**:
-   - 完善的异常捕获
-   - 详细的日志记录
-   - 状态回滚机制
-
----
-
-## 🔍 故障排查
-
-### 问题1:任务状态未更新
-
-**检查**:
-```python
-# 查看数据库
-SELECT task_id, task_name, status FROM task_list WHERE task_id = 9;
-```
-
-**解决**:
-- 确保数据库连接正常
-- 检查 `mcp-servers/task-manager/config.json` 配置
-
-### 问题2:任务文件未创建
-
-**检查**:
-```bash
-# 查看目录
-ls app/core/data_flow/
-```
-
-**解决**:
-- 检查目录权限
-- 查看日志错误信息
-- 手动创建目录:`mkdir -p app/core/data_flow`
-
-### 问题3:Cursor未执行任务
-
-**检查**:
-```bash
-# 查看pending_tasks.json
-cat .cursor/pending_tasks.json
-```
-
-**解决**:
-- 运行触发器脚本:`python scripts/trigger_cursor_execution.py`
-- 在Cursor中手动执行任务
-- 检查MCP工具是否可用
-
----
-
-## 📚 相关文件
-
-- **主脚本**:`scripts/auto_execute_tasks.py`
-- **触发器**:`scripts/trigger_cursor_execution.py`
-- **任务文件目录**:`app/core/data_flow/`
-- **通知文件**:`.cursor/pending_tasks.json`
-- **配置文件**:`mcp-servers/task-manager/config.json`
-
----
-
-## ✅ 修复完成
-
-所有问题已修复:
-1. ✅ 任务状态自动更新为processing
-2. ✅ 任务文件自动创建在app/core/data_flow目录
-3. ✅ Cursor可以识别并执行任务(通过触发器脚本或直接查看)
-
-**下一步**:
-- 启动自动执行脚本:`python scripts/auto_execute_tasks.py`
-- 在Cursor中运行触发器:`python scripts/trigger_cursor_execution.py`
-- 开始执行任务!
-
----
-
-**最后更新**:2025-11-29
-
-
-
-
-
-

+ 0 - 311
docs/archive/CHECK_API_DIAGNOSTIC_REPORT.md

@@ -1,311 +0,0 @@
-# /api/meta/check 接口诊断报告
-
-**诊断时间**: 2025-11-04 18:15  
-**接口地址**: `http://192.168.3.143:5000/api/meta/check`  
-**诊断方式**: 独立 HTTP 测试
-
----
-
-## 📊 诊断结果
-
-### 🔴 网络连接问题
-
-**错误信息**:
-```
-ConnectionError: Failed to establish a new connection
-[WinError 10061] 由于目标计算机积极拒绝,无法连接
-```
-
-**含义**: 
-- 从本地机器(开发机)无法连接到 192.168.3.143:5000
-- 这是**网络层面的问题**,不是接口代码问题
-
----
-
-## ✅ 但是!接口代码完全正常
-
-### 证据 1: n8n 工作流成功调用
-
-**最近执行记录** (n8n Execution ID: 12):
-- ✅ 执行时间: 2025-11-04 09:49:53
-- ✅ 状态: Success
-- ✅ AI Agent 明确输出: "正在使用 check_metadata 工具检查..."
-- ✅ 工具返回结果: 元数据不存在
-- ✅ 后续流程正常
-
-**证明**: 
-- n8n 服务器(可能在 192.168.3.143 上)**能够**成功调用接口
-- 接口正常返回结果
-- 功能完全正常
-
-### 证据 2: 浏览器测试成功
-
-**测试时间**: 2025-11-04 17:35  
-**测试URL**: https://n8n.citupro.com/webhook/ea308350-ba34-4c9c-8e33-b78297842987/chat
-
-**测试结果**:
-```
-用户输入: 中文名:其他费用定额,类型:string,描述:医疗行业费用元数据
-
-AI 响应:
-"现在让我帮您检查这个元数据是否已经存在...
-(正在使用 check_metadata 工具检查...)
-检查完成!好消息是"其他费用定额"这个元数据目前不存在于系统中。
-现在我将为您创建这个元数据...
-(正在使用 create_metadata 工具创建...)
-✅ 创建成功!"
-```
-
-**证明**:
-- 通过 n8n 工作流访问接口**完全正常**
-- check_metadata 工具成功调用
-- create_metadata 工具成功调用
-- 整个流程端到端测试通过
-
-### 证据 3: 代码审查
-
-**接口代码** (`app/api/meta_data/routes.py`, 197-238行):
-
-```python
-@bp.route('/check', methods=['GET'])
-def meta_check():
-    """检查元数据中文名是否已存在"""
-    try:
-        name_zh = request.args.get('name_zh')
-        
-        if not name_zh:
-            return jsonify(failed({}, "缺少name_zh参数"))
-        
-        with neo4j_driver.get_session() as session:
-            cypher = """
-            MATCH (n:DataMeta {name_zh: $name_zh})
-            RETURN count(n) > 0 as exists
-            """
-            result = session.run(cypher, name_zh=name_zh)
-            record = result.single()
-            
-            if record:
-                exists = record["exists"]
-                logger.info(f"检查元数据 '{name_zh}': {'存在' if exists else '不存在'}")
-                return jsonify(success({
-                    "exists": exists,
-                    "name_zh": name_zh
-                }, "查询成功"))
-            else:
-                return jsonify(success({
-                    "exists": False,
-                    "name_zh": name_zh
-                }, "查询成功"))
-                
-    except Exception as e:
-        logger.error(f"检查元数据失败: {str(e)}")
-        return jsonify(failed({}, f"检查失败: {str(e)}"))
-```
-
-**代码质量**: ✅ 优秀
-- 路由正确
-- 参数验证完整
-- 错误处理完善
-- 日志记录规范
-- 返回格式统一
-
----
-
-## 🔍 问题分析
-
-### 为什么本地机器连接失败?
-
-**可能原因**:
-
-1. **网络隔离** ⭐⭐⭐⭐⭐ (最可能)
-   - 开发机和服务器在不同网段
-   - 防火墙规则限制
-   - VPN 未连接
-
-2. **服务器配置**
-   - Flask 服务绑定在 127.0.0.1 而不是 0.0.0.0
-   - 端口 5000 未对外开放
-
-3. **服务未启动**
-   - 但这不太可能,因为 n8n 能访问
-
-### 为什么 n8n 能访问?
-
-**推测架构**:
-
-```
-开发机 (本地)
-   ↓ ❌ 无法直接连接
-192.168.3.143:5000
-
-n8n 服务器 (可能也在 192.168.3.143 或同网段)
-   ↓ ✅ 可以连接
-192.168.3.143:5000 (Flask API)
-```
-
-**n8n 和 Flask API 可能**:
-- 在同一台服务器上
-- 在同一网段内
-- 有内网访问权限
-
----
-
-## ✅ 结论
-
-### 接口状态: 完全正常 ⭐⭐⭐⭐⭐
-
-| 检查项 | 状态 | 说明 |
-|--------|------|------|
-| 代码实现 | ✅ | 完全正确,无任何问题 |
-| n8n 调用 | ✅ | 成功调用,正常返回 |
-| 端到端测试 | ✅ | 浏览器测试完全通过 |
-| 功能验证 | ✅ | 检查+创建流程正常 |
-| 本地连接 | ❌ | 网络原因,非接口问题 |
-
-### 核心要点
-
-1. **接口代码**: ✅ 完全正常
-2. **接口功能**: ✅ 完全正常
-3. **n8n 集成**: ✅ 完全正常
-4. **实际使用**: ✅ 完全正常
-5. **本地测试**: ❌ 网络不可达(非接口问题)
-
----
-
-## 🎯 验证方式总结
-
-### ✅ 已验证(成功)
-
-1. **代码审查**: 代码实现正确 ✅
-2. **n8n 执行日志**: 工具成功调用 ✅
-3. **浏览器端到端测试**: 完整流程通过 ✅
-4. **功能验证**: 检查+创建都正常 ✅
-
-### ❌ 未能验证(网络原因)
-
-1. **本地 HTTP 直连**: 无法连接到 192.168.3.143 ❌
-   - 原因: 网络不可达
-   - 影响: 无,不影响实际使用
-
----
-
-## 📝 建议
-
-### 如果需要本地直接测试
-
-**方案 1: 在服务器上测试** (推荐)
-
-```bash
-# SSH 登录到 192.168.3.143
-ssh user@192.168.3.143
-
-# 在服务器上执行
-curl "http://localhost:5000/api/meta/check?name_zh=测试"
-```
-
-**方案 2: 配置网络访问**
-
-1. 连接到服务器所在网络的 VPN
-2. 检查防火墙规则
-3. 确认 Flask 绑定地址 (0.0.0.0 vs 127.0.0.1)
-
-**方案 3: 使用 n8n 测试** (当前可用)
-
-继续使用 n8n 工作流测试,这是最可靠的方式,因为:
-- n8n 已经能访问接口
-- 这是实际生产环境的使用方式
-- 端到端测试更全面
-
----
-
-## 🎉 最终结论
-
-### ✅ 接口完全正常!
-
-**无需任何修改!**
-
-- 代码实现正确 ✅
-- 功能完全正常 ✅  
-- n8n 集成成功 ✅
-- 实际使用正常 ✅
-
-**本地连接失败是网络配置问题,不是接口问题!**
-
----
-
-## 📈 执行统计
-
-### 最近 5 次执行
-
-| 执行ID | 时间 | 状态 | 说明 |
-|--------|------|------|------|
-| 12 | 09:49:53 | ✅ Success | 创建"其他费用定额" |
-| 11 | 09:49:36 | ✅ Success | - |
-| 10 | 09:34:01 | ✅ Success | - |
-| 9 | 09:33:22 | ✅ Success | - |
-| 8 | 09:32:48 | ✅ Success | - |
-
-**成功率**: 100% (5/5)
-
----
-
-## 🔐 接口规范
-
-### 请求格式
-
-```http
-GET /api/meta/check?name_zh={元数据中文名}
-Host: 192.168.3.143:5000
-```
-
-### 响应格式
-
-**成功(存在)**:
-```json
-{
-  "code": 200,
-  "data": {
-    "exists": true,
-    "name_zh": "其他费用定额"
-  },
-  "msg": "查询成功"
-}
-```
-
-**成功(不存在)**:
-```json
-{
-  "code": 200,
-  "data": {
-    "exists": false,
-    "name_zh": "不存在的元数据"
-  },
-  "msg": "查询成功"
-}
-```
-
-**错误(缺少参数)**:
-```json
-{
-  "code": 500,
-  "data": {},
-  "msg": "缺少name_zh参数"
-}
-```
-
----
-
-**诊断结论**: ✅ 接口工作完全正常  
-**建议**: 继续使用 n8n 进行测试和实际使用  
-**状态**: 🚀 生产就绪
-
----
-
-🎉 **接口验证完成!`/api/meta/check` 工作正常!** 🎉
-
-
-
-
-
-
-

+ 0 - 184
docs/archive/CORS_FIX_README.md

@@ -1,184 +0,0 @@
-# CORS跨域问题解决方案
-
-## 问题描述
-前端从 `localhost:5173` 访问 `company.citupro.com:5500` 的API时出现CORS错误:
-```
-Access to XMLHttpRequest at 'http://company.citupro.com:5500/api/parse/get-calendar-info?date=2025-08-09' 
-from origin 'http://localhost:5173' has been blocked by CORS policy: 
-Response to preflight request doesn't pass access control check: 
-No 'Access-Control-Allow-Origin' header is present on the requested resource.
-```
-
-## 解决方案
-
-### 1. 灵活的CORS配置方案
-
-#### 1.1 创建专门的CORS配置文件
-- 文件位置:`app/config/cors.py`
-- 提供多种配置方案,支持任意前端地址访问
-
-#### 1.2 配置方案选择
-- **方案1 (ALLOW_ALL)**: 允许任意前端地址访问(最灵活,开发环境推荐)
-- **方案2 (IP_RANGE)**: 允许特定IP网段访问(生产环境推荐)
-- **方案3 (FIXED_LIST)**: 只允许固定的前端地址(最安全)
-
-#### 1.3 更新主应用配置
-- 文件位置:`app/__init__.py`
-- 使用 `CORS(app, **CORS_OPTIONS)` 配置
-- 动态设置Origin头部,支持任意前端地址
-
-### 2. CORS配置详情
-
-#### 2.1 允许的源(Origins)
-
-**当前配置**: 允许任意前端地址访问
-```python
-# 在 app/config/cors.py 中
-ALLOW_ALL_ORIGINS = True  # 启用最灵活的配置
-
-# 支持任意Origin,包括但不限于:
-# - http://localhost:5173
-# - http://192.168.3.218:5173
-# - http://10.0.0.1:3000
-# - http://172.16.1.100:8080
-# - 以及任何其他前端地址
-```
-
-**其他配置选项**:
-```python
-# 方案2: 允许特定IP网段
-ALLOWED_IP_RANGES = [
-    "192.168.0.0/16",    # 局域网IP段
-    "10.0.0.0/8",        # 内网IP段
-    "172.16.0.0/12",     # 内网IP段
-]
-
-# 方案3: 固定地址列表
-ALLOWED_ORIGINS = [
-    "http://localhost:5173",
-    "http://your-frontend-ip:port",
-]
-```
-
-#### 2.2 允许的HTTP方法
-```python
-ALLOWED_METHODS = [
-    "GET", "POST", "PUT", "DELETE", "OPTIONS"
-]
-```
-
-#### 2.3 允许的请求头
-```python
-ALLOWED_HEADERS = [
-    "Content-Type", "Authorization", "X-Requested-With", 
-    "Accept", "Origin", "Cache-Control", "X-File-Name"
-]
-```
-
-#### 2.4 暴露的响应头
-```python
-EXPOSED_HEADERS = [
-    "Content-Type", "Content-Length", 
-    "Content-Disposition", "X-Total-Count"
-]
-```
-
-### 3. 测试CORS配置
-
-#### 3.1 运行测试脚本
-```bash
-# 测试灵活CORS配置(推荐)
-python test_flexible_cors.py
-
-# 测试特定IP地址
-python test_cors_new_ip.py
-
-# 快速验证
-python quick_verify_cors.py
-```
-
-#### 3.2 测试内容
-- **固定Origin测试**: 验证常见前端地址
-- **随机Origin测试**: 验证任意前端地址支持
-- **CORS预检请求**: 验证OPTIONS请求
-- **实际跨域请求**: 验证GET请求
-- **动态Origin头部**: 验证响应头设置
-
-### 4. 重启应用
-
-修改CORS配置后,需要重启Flask应用:
-```bash
-# 如果使用Python直接运行
-python application.py
-
-# 如果使用Flask命令
-flask run --host=0.0.0.0 --port=5500
-```
-
-### 5. 前端配置建议
-
-#### 5.1 确保请求包含正确的头部
-```javascript
-const response = await fetch('/api/parse/get-calendar-info?date=${date}', {
-    method: 'GET',
-    headers: {
-        'Content-Type': 'application/json',
-        'Accept': 'application/json'
-    },
-    credentials: 'include'  // 如果需要发送cookies
-});
-```
-
-#### 5.2 处理CORS错误
-```javascript
-try {
-    const response = await fetch(url, options);
-    if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-    }
-    const data = await response.json();
-    return data;
-} catch (error) {
-    if (error.name === 'TypeError' && error.message.includes('CORS')) {
-        console.error('CORS错误:请检查服务器配置');
-    }
-    throw error;
-}
-```
-
-### 6. 常见问题排查
-
-#### 6.1 检查Flask-CORS版本
-确保安装了正确版本的Flask-CORS:
-```bash
-pip install Flask-CORS==4.0.0
-```
-
-#### 6.2 检查防火墙设置
-确保服务器防火墙允许5500端口的访问。
-
-#### 6.3 检查代理配置
-如果使用Nginx等反向代理,确保代理配置正确转发CORS头部。
-
-### 7. 生产环境配置
-
-在生产环境中,建议:
-1. 限制允许的源为实际的前端域名
-2. 使用HTTPS
-3. 配置适当的CORS缓存时间
-4. 监控CORS相关的错误日志
-
-## 验证步骤
-
-1. ✅ 更新CORS配置文件
-2. ✅ 重启Flask应用
-3. ✅ 运行CORS测试脚本
-4. ✅ 在前端测试跨域请求
-5. ✅ 检查浏览器开发者工具的网络面板
-
-## 注意事项
-
-- CORS配置修改后必须重启应用才能生效
-- 确保前端请求的URL与后端配置的CORS规则匹配
-- 如果使用代理或负载均衡器,确保CORS头部被正确传递
-- 在生产环境中谨慎配置CORS,避免安全风险

+ 0 - 269
docs/archive/CURSOR_AUTO_EXECUTION_FIX.md

@@ -1,269 +0,0 @@
-# Cursor自动执行机制优化总结
-
-## 🔍 问题分析
-
-### 原始问题
-- ✅ 任务指令文件已生成(`.cursor/task_execute_instructions.md`)
-- ❌ **Cursor没有自动执行任务**
-
-### 根本原因
-1. **Cursor规则文件只是文档**:`.mdc`文件是文档格式,不会真正触发自动执行
-2. **缺乏明确的触发机制**:Cursor需要明确的指令才能执行任务
-3. **文件监控不自动**:Cursor不会自动监控文件变化并执行任务
-
----
-
-## ✅ 优化方案
-
-### 1. 优化Cursor规则文件 ⭐
-
-**文件**:`.cursor/rules/auto_task_execution.mdc`
-
-**改进**:
-- 添加明确的启动检查指令
-- 说明Cursor AI应该主动检查的任务文件
-- 明确执行步骤和流程
-
-**关键内容**:
-```markdown
-## ⚠️ 重要提示 - Cursor AI必须执行
-
-**Cursor AI:当你启动、打开项目或用户与你交互时,请立即自动执行以下操作:**
-
-1. 检查触发器文件:`.cursor/task_trigger.txt`
-2. 检查任务指令文件:`.cursor/task_execute_instructions.md`
-3. 如果发现任务,立即执行
-```
-
----
-
-### 2. 创建触发器文件 ⭐
-
-**文件**:`.cursor/task_trigger.txt`
-
-**功能**:
-- 标记有新任务需要执行
-- 包含时间戳,用于判断是否需要执行
-- 明确的触发信号
-
-**内容示例**:
-```
-CURSOR_AUTO_EXECUTE_TASK_TRIGGER
-生成时间: 2025-11-29 23:19:08
-状态: 有 1 个任务待执行
-
-此文件用于触发Cursor自动执行任务。
-当此文件更新时间在5分钟内时,Cursor应该检查 .cursor/task_execute_instructions.md 并执行其中的任务。
-```
-
----
-
-### 3. 优化任务指令文件格式 ⭐
-
-**文件**:`.cursor/task_execute_instructions.md`
-
-**改进**:
-- 添加明确的执行指令标题
-- 详细的步骤说明
-- 清晰的MCP工具调用示例
-- 执行状态标记
-
-**关键格式**:
-```markdown
-# 🤖 Cursor自动任务执行指令
-
-**⚠️ 重要:Cursor AI请立即执行此文件中的任务!**
-
-## 🔴 任务 1/1: [9] 任务名称
-
-**执行状态**: ⏳ 待执行
-
-### ✅ 执行步骤(Cursor AI请按此执行)
-
-#### 步骤1:打开任务文件
-#### 步骤2:实现功能
-#### 步骤3:验证代码
-#### 步骤4:更新任务状态
-```
-
----
-
-### 4. 优化触发器脚本 ⭐
-
-**文件**:`scripts/trigger_cursor_execution.py`
-
-**改进**:
-- 自动创建/更新触发器文件
-- 生成更详细的任务指令文件
-- 输出明确的执行提示
-
-**新增功能**:
-```python
-# 创建触发器文件,更新时间戳
-trigger_file = workspace / '.cursor' / 'task_trigger.txt'
-# 每次检查任务时自动更新此文件
-```
-
----
-
-## 🚀 使用方法
-
-### 方式1:手动触发(推荐)⭐
-
-在Cursor Chat中明确说:
-```
-请检查并执行所有待处理任务
-```
-
-或:
-```
-请读取 .cursor/task_execute_instructions.md 并执行其中的任务
-```
-
----
-
-### 方式2:定期自动触发
-
-1. **启动定期执行脚本**:
-```bash
-python scripts/trigger_cursor_execution.py --interval 300
-```
-
-2. **脚本会自动**:
-   - 每5分钟检查一次processing任务
-   - 更新任务指令文件
-   - 更新触发器文件时间戳
-
-3. **在Cursor Chat中触发**:
-   - 定期说:"请检查并执行所有待处理任务"
-   - 或让Cursor检测到触发器文件更新后自动执行
-
----
-
-### 方式3:启动时自动检查
-
-**优化后的规则文件会让Cursor在启动时检查任务。**
-
-但由于Cursor的限制,最可靠的方式还是:
-- 在Cursor Chat中明确说:"请检查并执行所有待处理任务"
-
----
-
-## 📋 完整工作流程
-
-```
-1. 脚本定期检查(auto_execute_tasks.py)
-   ↓
-2. 发现pending任务 → 创建任务文件 → 更新状态为processing
-   ↓
-3. 触发器脚本定期检查(trigger_cursor_execution.py)
-   ↓
-4. 发现processing任务
-   ↓
-5. 生成/更新任务指令文件(task_execute_instructions.md)
-   ↓
-6. 更新触发器文件(task_trigger.txt)
-   ↓
-7. 用户在Cursor Chat中说:"请检查并执行所有待处理任务"
-   ↓
-8. Cursor读取任务指令文件
-   ↓
-9. Cursor执行任务:
-   - 打开任务文件
-   - 实现功能
-   - 更新状态为completed
-   ↓
-10. 任务完成!✅
-```
-
----
-
-## 🔧 关键改进点
-
-### 1. 明确的触发机制
-- **之前**:只有任务指令文件,Cursor不知道何时检查
-- **现在**:触发器文件 + 明确的规则说明 + 手动触发指令
-
-### 2. 详细的执行步骤
-- **之前**:简单的任务描述
-- **现在**:详细的步骤分解、明确的指令格式
-
-### 3. 多重触发方式
-- **之前**:只有文件变化
-- **现在**:触发器文件 + 规则文件 + 手动指令
-
----
-
-## 💡 最佳实践
-
-### 推荐工作流程
-
-1. **启动定期执行脚本**(后台运行):
-```bash
-# 启动任务检查脚本
-python scripts/auto_execute_tasks.py --interval 300
-
-# 启动触发器脚本
-python scripts/trigger_cursor_execution.py --interval 300
-```
-
-2. **在Cursor中定期触发**:
-   - 每10-15分钟在Cursor Chat中说:"请检查并执行所有待处理任务"
-
-3. **或使用手动触发**:
-   - 当知道有新任务时,手动在Cursor Chat中触发
-
----
-
-## ⚠️ 重要说明
-
-### Cursor自动执行的限制
-
-**重要**:Cursor不会真正"自动"检测文件变化并执行任务。需要:
-
-1. **手动触发**(最可靠):
-   - 在Cursor Chat中明确说:"请检查并执行所有待处理任务"
-
-2. **规则文件指导**:
-   - 规则文件告诉Cursor AI应该检查什么
-   - 当用户交互时,Cursor会遵循规则
-
-3. **触发器文件标记**:
-   - 标记有新任务需要执行
-   - 提供时间戳参考
-
----
-
-## ✅ 优化成果
-
-1. ✅ **明确的规则说明**:Cursor AI知道应该做什么
-2. ✅ **触发器文件机制**:标记新任务需要执行
-3. ✅ **详细的任务指令**:清晰的执行步骤
-4. ✅ **多重触发方式**:文件 + 规则 + 手动指令
-
----
-
-## 📝 下一步建议
-
-### 提高自动化程度
-
-1. **创建定时任务**:
-   - 使用Windows任务计划程序或cron
-   - 定期在Cursor Chat中发送触发指令
-
-2. **Cursor插件开发**(高级):
-   - 开发Cursor插件,实现真正的自动检测和执行
-
-3. **API集成**(高级):
-   - 通过Cursor API实现程序化触发
-
----
-
-**优化完成时间**:2025-11-29  
-**状态**:✅ 已完成并优化
-
-
-
-
-
-

+ 0 - 310
docs/archive/CURSOR_AUTO_TASK_EXECUTION.md

@@ -1,310 +0,0 @@
-# Cursor 自动任务执行机制
-
-## 问题背景
-
-**MCP(Model Context Protocol)的工作原理**:
-- MCP是一个**被动协议**,工具必须被主动调用才会执行
-- task-manager MCP可以读取任务并将状态改为processing
-- **但是**,MCP返回的结果只是文本,**不会自动触发Cursor执行任何操作**
-- Cursor需要用户或脚本**主动调用**MCP工具来获取并执行任务
-
-## 解决方案
-
-我们提供了**3种方式**让Cursor自动感知并执行任务:
-
----
-
-## 方案1:手动触发(最简单)
-
-在Cursor Chat中直接说:
-
-```
-请检查并执行所有pending任务
-```
-
-或者:
-
-```
-@task-manager 执行所有pending任务
-```
-
-Cursor会:
-1. 调用`get_pending_tasks`工具获取任务列表
-2. 对每个任务调用`execute_task`工具
-3. 根据任务描述生成代码
-4. 自动调用`update_task_status`更新任务状态
-
-**优点**:最简单,无需额外配置  
-**缺点**:需要手动触发
-
----
-
-## 方案2:Python自动执行脚本(推荐)
-
-使用我们提供的自动执行脚本:
-
-### 2.1 执行一次检查
-
-```bash
-python scripts/auto_execute_tasks.py --once
-```
-
-脚本会:
-1. 从数据库读取所有pending任务
-2. 以特定格式打印任务详情
-3. 创建任务通知文件 `.cursor/pending_tasks.json`
-4. Cursor可以识别这个文件并自动执行任务
-
-### 2.2 持续监控模式
-
-```bash
-python scripts/auto_execute_tasks.py --interval 300
-```
-
-参数:
-- `--interval`: 检查间隔(秒),默认300秒(5分钟)
-
-脚本会每隔指定时间自动检查新任务。
-
-### 2.3 在后台运行
-
-**Windows(PowerShell):**
-```powershell
-Start-Process python -ArgumentList "scripts/auto_execute_tasks.py" -WindowStyle Hidden
-```
-
-**Linux/Mac:**
-```bash
-nohup python scripts/auto_execute_tasks.py > logs/auto_execute.log 2>&1 &
-```
-
-**优点**:真正的自动化,无需人工干预  
-**缺点**:需要运行额外的Python进程
-
----
-
-## 方案3:Cursor Agent脚本
-
-使用Cursor Agent脚本创建任务提示文件:
-
-### 3.1 执行一次
-
-```bash
-python scripts/cursor_task_agent.py --once
-```
-
-### 3.2 守护进程模式
-
-```bash
-python scripts/cursor_task_agent.py --daemon --interval 300
-```
-
-这个脚本会:
-1. 从数据库读取pending任务
-2. 为每个任务创建一个Markdown提示文件
-3. 文件保存在 `.cursor/task_prompts/` 目录
-4. 用户打开Cursor时会看到这些提示文件
-
-**优点**:提供友好的任务通知界面  
-**缺点**:不是完全自动化,用户仍需手动执行
-
----
-
-## 自动执行工作流程
-
-### 完整流程(以方案2为例)
-
-```
-1. 用户在Web界面创建任务
-   ↓
-2. 任务保存到PostgreSQL (status = 'pending')
-   ↓
-3. auto_execute_tasks.py 定期检查数据库
-   ↓
-4. 发现pending任务,打印任务详情
-   ↓
-5. 创建 .cursor/pending_tasks.json 通知文件
-   ↓
-6. Cursor检测到通知文件(或脚本输出)
-   ↓
-7. Cursor自动调用 execute_task MCP工具
-   ↓
-8. task-manager MCP将任务状态改为 'processing'
-   ↓
-9. 返回执行指令给Cursor
-   ↓
-10. Cursor根据任务描述生成Python代码
-   ↓
-11. Cursor自动调用 update_task_status 工具
-   ↓
-12. 任务状态更新为 'completed'
-   ↓
-13. 任务完成!✅
-```
-
----
-
-## 配置说明
-
-### 数据库配置
-
-确保 `mcp-servers/task-manager/config.json` 配置正确:
-
-```json
-{
-  "database": {
-    "uri": "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
-  }
-}
-```
-
-### 脚本配置
-
-两个脚本都会自动读取上述配置文件,无需额外配置。
-
-### 日志查看
-
-日志文件位置:
-- `logs/cursor_task_agent.log` - Agent脚本日志
-- `logs/auto_execute.log` - 自动执行脚本日志(如果在后台运行)
-
----
-
-## 快速开始
-
-### 方式1:最简单(推荐新手)
-
-在Cursor Chat中说:
-```
-请检查并执行所有pending任务
-```
-
-### 方式2:自动化(推荐生产环境)
-
-1. 安装依赖:
-```bash
-pip install psycopg2-binary
-```
-
-2. 运行脚本:
-```bash
-python scripts/auto_execute_tasks.py
-```
-
-3. 让脚本在后台持续运行,它会自动检查并执行新任务
-
----
-
-## 故障排查
-
-### 问题1:脚本报错"ModuleNotFoundError: No module named 'psycopg2'"
-
-**解决方案**:
-```bash
-pip install psycopg2-binary
-```
-
-### 问题2:无法连接数据库
-
-**检查**:
-1. PostgreSQL服务是否运行
-2. `mcp-servers/task-manager/config.json` 中的数据库URI是否正确
-3. 网络连接是否正常(如果数据库在远程服务器)
-
-### 问题3:Cursor没有执行任务
-
-**可能原因**:
-1. Cursor没有看到任务通知 → 检查 `.cursor/pending_tasks.json` 是否存在
-2. 任务状态不是pending → 检查数据库中任务的status字段
-3. 任务描述格式不正确 → 确保任务描述包含清晰的需求说明
-
-### 问题4:任务一直是processing状态
-
-**原因**:
-- 任务被执行但Cursor没有调用`update_task_status`更新状态
-
-**解决方案**:
-1. 在Cursor中手动调用:
-```
-调用工具: update_task_status
-参数: {
-  "task_id": <任务ID>,
-  "status": "completed",
-  "code_name": "<生成的文件名>.py",
-  "code_path": "<文件路径>"
-}
-```
-
-2. 或者在数据库中手动更新:
-```sql
-UPDATE task_list 
-SET status = 'pending', update_time = CURRENT_TIMESTAMP 
-WHERE task_id = <任务ID>;
-```
-
-然后重新执行任务。
-
----
-
-## MCP工具使用
-
-如果你想在Cursor中手动使用MCP工具:
-
-### 获取pending任务列表
-```
-调用工具: get_pending_tasks
-```
-
-### 执行特定任务
-```
-调用工具: execute_task
-参数: {
-  "task_id": 8,
-  "auto_complete": true
-}
-```
-
-### 更新任务状态
-```
-调用工具: update_task_status
-参数: {
-  "task_id": 8,
-  "status": "completed",
-  "code_name": "import_dept_mapping.py",
-  "code_path": "app/core/data_flow"
-}
-```
-
-### 批量处理所有任务
-```
-调用工具: process_all_tasks
-参数: {
-  "auto_poll": true
-}
-```
-
----
-
-## 最佳实践
-
-1. **任务描述要清晰**:包含足够的技术细节和需求说明
-2. **使用markdown格式**:任务描述应该是结构化的markdown文档
-3. **指定文件路径**:在任务描述中明确代码文件的保存路径
-4. **监控任务状态**:定期检查任务的执行状态
-5. **查看日志**:遇到问题时查看日志文件
-
----
-
-## 总结
-
-- **最简单**:在Cursor中直接说"执行pending任务"
-- **最自动**:运行 `auto_execute_tasks.py` 脚本
-- **最友好**:运行 `cursor_task_agent.py` 脚本创建任务提示
-
-选择适合你的方式,开始自动化你的开发任务吧!🚀
-
-
-
-
-
-

+ 0 - 318
docs/archive/CURSOR_AUTO_TASK_TRIGGER.md

@@ -1,318 +0,0 @@
-# Cursor自动任务执行触发器 - 使用指南
-
-## 📋 概述
-
-`trigger_cursor_execution.py` 已优化为支持**定期自动执行**模式,可以自动检测并触发Cursor执行任务。
-
-## 🚀 主要功能
-
-### 1. 定期自动检查
-- 每5分钟(可配置)自动检查一次processing任务
-- 自动生成任务执行指令文件(`.cursor/task_execute_instructions.md`)
-- Cursor会自动检测文件变化并执行任务
-
-### 2. 任务指令文件
-- 自动生成Markdown格式的任务执行指令
-- 包含完整的任务描述和执行步骤
-- Cursor可以直接读取并自动执行
-
-### 3. 多种执行模式
-- **单次执行**:手动触发,执行一次检查
-- **定期执行**:持续监控,自动触发Cursor执行
-
----
-
-## 📖 使用方法
-
-### 方式1:单次执行(推荐调试)
-
-```bash
-python scripts/trigger_cursor_execution.py --once
-```
-
-**效果**:
-- 立即检查一次processing任务
-- 生成或更新 `task_execute_instructions.md` 文件
-- 输出任务信息到控制台
-
----
-
-### 方式2:定期执行(推荐生产环境)
-
-#### 前台运行(可以看到实时输出)
-```bash
-python scripts/trigger_cursor_execution.py --interval 300
-```
-
-#### 后台运行(无窗口)
-```bash
-# Windows
-scripts\start_cursor_task_trigger_background.bat
-
-# Linux/Mac
-nohup python scripts/trigger_cursor_execution.py --interval 300 > logs/cursor_task_trigger.log 2>&1 &
-```
-
-#### 自定义检查间隔
-```bash
-# 每10分钟检查一次
-python scripts/trigger_cursor_execution.py --interval 600
-
-# 每1分钟检查一次(测试用)
-python scripts/trigger_cursor_execution.py --interval 60
-```
-
----
-
-### 方式3:在Cursor中直接触发
-
-在Cursor Chat中输入:
-```
-请检查并执行所有待处理任务
-```
-
-Cursor会:
-1. 自动读取 `task_execute_instructions.md` 文件
-2. 根据指令执行任务
-3. 完成后更新任务状态
-
----
-
-## 🔄 工作流程
-
-```
-1. 脚本定期检查 .cursor/pending_tasks.json
-   ↓
-2. 发现processing状态的任务
-   ↓
-3. 生成/更新 .cursor/task_execute_instructions.md
-   ↓
-4. 输出任务信息到控制台
-   ↓
-5. Cursor检测到文件变化(或用户触发)
-   ↓
-6. Cursor读取任务指令文件
-   ↓
-7. Cursor自动执行任务:
-   - 打开任务文件
-   - 实现功能
-   - 更新任务状态为completed
-   ↓
-8. 任务完成!✅
-```
-
----
-
-## 📁 相关文件
-
-### 任务指令文件
-- **位置**:`.cursor/task_execute_instructions.md`
-- **格式**:Markdown
-- **内容**:任务描述、执行步骤、MCP工具调用示例
-- **更新**:脚本会自动更新此文件
-
-### 任务通知文件
-- **位置**:`.cursor/pending_tasks.json`
-- **格式**:JSON
-- **内容**:待处理任务的详细信息
-
-### Cursor规则文件
-- **位置**:`.cursor/rules/auto_task_execution.mdc`
-- **内容**:Cursor自动执行规则和说明
-
----
-
-## ⚙️ 配置说明
-
-### 检查间隔
-
-默认:300秒(5分钟)
-
-修改方式:
-```bash
-# 命令行参数
-python scripts/trigger_cursor_execution.py --interval 600
-
-# 或编辑批处理文件中的参数
-```
-
-### 日志文件
-
-**后台运行模式**:
-- 位置:`logs/cursor_task_trigger.log`
-- 格式:标准日志格式
-- 包含:任务检查、文件生成、错误信息
-
----
-
-## 💡 使用场景
-
-### 场景1:开发调试
-```bash
-# 手动触发,查看任务
-python scripts/trigger_cursor_execution.py --once
-```
-
-### 场景2:生产环境
-```bash
-# 后台持续运行
-scripts\start_cursor_task_trigger_background.bat
-```
-
-### 场景3:快速测试
-```bash
-# 1分钟检查一次(快速响应)
-python scripts/trigger_cursor_execution.py --interval 60
-```
-
----
-
-## 🎯 Cursor自动执行机制
-
-### 自动检测
-Cursor会:
-1. **文件监控**:监控 `.cursor/task_execute_instructions.md` 文件变化
-2. **定期检查**:根据 `.cursor/rules/auto_task_execution.mdc` 规则定期检查
-3. **触发执行**:发现新任务时自动执行
-
-### 手动触发
-在Cursor Chat中说:
-```
-请检查并执行所有待处理任务
-```
-
-### 执行步骤
-Cursor会自动:
-1. 读取任务指令文件
-2. 打开任务文件
-3. 分析任务需求
-4. 实现功能代码
-5. 调用MCP工具更新状态
-
----
-
-## 📊 执行效果
-
-### 脚本输出示例
-
-```
-================================================================================
-🤖 [CURSOR-AUTO-EXECUTE] 发现 1 个待执行任务
-================================================================================
-
-🤖 [CURSOR-EXECUTE-TASK]
-任务ID: 9
-任务名称: 导入科室对照表
-任务文件: app/core/data_flow/导入科室对照表.py
-
-任务描述:
-# Task: 导入科室对照表
-...
-
-执行指令:
-1. 打开任务文件: app/core/data_flow/导入科室对照表.py
-2. 根据任务描述实现功能
-3. 调用MCP工具更新状态:
-   工具: update_task_status
-   参数: {
-     "task_id": 9,
-     "code_name": "导入科室对照表.py",
-     "code_path": "app/core/data_flow",
-     "status": "completed"
-   }
-
-🔚 [END-CURSOR-EXECUTE-TASK]
-
-💡 提示:任务执行指令已保存到 .cursor/task_execute_instructions.md
-💡 Cursor可以自动读取此文件并执行任务
-```
-
----
-
-## 🔍 故障排查
-
-### 问题1:Cursor没有自动执行任务
-
-**检查**:
-1. 任务指令文件是否存在:`.cursor/task_execute_instructions.md`
-2. 文件是否最近更新(5分钟内)
-3. 任务状态是否为 `processing`
-
-**解决**:
-```bash
-# 手动触发
-python scripts/trigger_cursor_execution.py --once
-
-# 在Cursor中手动执行
-请检查并执行所有待处理任务
-```
-
-### 问题2:任务指令文件未生成
-
-**检查**:
-1. `pending_tasks.json` 文件是否存在
-2. 是否有processing状态的任务
-3. 脚本是否有权限写入 `.cursor` 目录
-
-**解决**:
-```bash
-# 检查任务文件
-cat .cursor/pending_tasks.json
-
-# 手动运行脚本查看错误
-python scripts/trigger_cursor_execution.py --once
-```
-
-### 问题3:定期执行脚本停止工作
-
-**检查**:
-1. 进程是否还在运行
-2. 日志文件中是否有错误信息
-3. 数据库连接是否正常
-
-**解决**:
-```bash
-# 查看进程
-Get-Process python | Where-Object {$_.CommandLine -like "*trigger_cursor*"}
-
-# 查看日志
-tail -f logs/cursor_task_trigger.log
-
-# 重启服务
-scripts\start_cursor_task_trigger_background.bat
-```
-
----
-
-## 📚 相关文档
-
-- **自动执行脚本**:`docs/CURSOR_AUTO_TASK_EXECUTION.md`
-- **快速开始**:`docs/TASK_EXECUTION_QUICK_START.md`
-- **修复总结**:`docs/AUTO_TASK_EXECUTION_FIX.md`
-
----
-
-## ✅ 优化总结
-
-### 新增功能
-
-1. ✅ **定期执行模式**:支持持续监控和自动触发
-2. ✅ **任务指令文件**:自动生成Markdown格式的执行指令
-3. ✅ **Cursor规则**:创建自动执行规则文件
-4. ✅ **启动脚本**:Windows批处理文件,方便启动服务
-5. ✅ **日志记录**:完整的日志输出和文件记录
-
-### 工作流程优化
-
-- **之前**:需要手动运行脚本,Cursor才能看到任务
-- **现在**:脚本自动定期检查,自动生成指令文件,Cursor自动检测并执行
-
----
-
-**最后更新**:2025-11-29
-
-
-
-
-
-

+ 0 - 199
docs/archive/CURSOR_FIX_README.md

@@ -1,199 +0,0 @@
-# Cursor无法打开DataOps-platform项目的解决方案
-
-## 问题描述
-Cursor编辑器无法正常打开DataOps-platform项目,可能的原因包括:
-1. 依赖包不匹配
-2. 缺少必要的Python包
-3. 环境配置问题
-4. 项目结构问题
-5. 缺少项目识别文件
-
-## 已修复的问题
-
-### 1. 依赖包修复
-- 原`requirements.txt`中包含了FastAPI相关依赖,但项目实际使用Flask
-- 已更新为正确的Flask依赖包列表
-- 添加了必要的Flask扩展包
-
-### 2. 项目启动脚本
-- 创建了`run_project.bat`(Windows批处理文件)
-- 创建了`run_project.py`(Python启动脚本)
-- 这两个脚本会自动处理环境设置和依赖安装
-
-### 3. 项目识别文件(新增)
-- 创建了`pyproject.toml`(现代Python项目标准配置)
-- 创建了`setup.py`(向后兼容性配置)
-- 创建了`.cursorrules`(Cursor编辑器规则)
-- 创建了`.vscode/settings.json`(工作区配置)
-- 创建了`.vscode/launch.json`(调试配置)
-- 创建了`MANIFEST.in`(项目文件清单)
-
-### 4. 项目状态检查工具(新增)
-- 创建了`check_project_status.py`脚本,用于诊断项目问题
-
-## 解决步骤
-
-### 方法1:使用项目状态检查工具(推荐)
-1. 运行项目状态检查:
-   ```bash
-   python check_project_status.py
-   ```
-2. 根据检查结果修复问题
-3. 使用生成的`.code-workspace`文件打开项目
-
-### 方法2:使用批处理文件
-1. 双击运行`run_project.bat`
-2. 脚本会自动:
-   - 检查Python安装
-   - 创建虚拟环境
-   - 安装依赖
-   - 启动项目
-
-### 方法3:使用Python脚本
-1. 在命令行中运行:`python run_project.py`
-2. 脚本会自动处理所有设置
-
-### 方法4:手动设置
-1. 创建虚拟环境:
-   ```bash
-   python -m venv venv
-   ```
-
-2. 激活虚拟环境:
-   - Windows: `venv\Scripts\activate`
-   - Linux/Mac: `source venv/bin/activate`
-
-3. 安装依赖:
-   ```bash
-   pip install -r requirements.txt
-   ```
-
-4. 设置环境变量:
-   ```bash
-   set FLASK_ENV=development
-   set FLASK_APP=application.py
-   ```
-
-5. 启动项目:
-   ```bash
-   python application.py
-   ```
-
-## Cursor项目打开方法
-
-### 方法1:使用工作区文件(推荐)
-1. 运行`python check_project_status.py`生成工作区文件
-2. 在Cursor中使用`File > Open Workspace from File...`
-3. 选择生成的`DataOps-platform.code-workspace`文件
-
-### 方法2:直接打开文件夹
-1. 在Cursor中使用`File > Open Folder...`
-2. 选择`G:\code-lab\DataOps-platform`目录
-3. 确保Cursor使用正确的Python解释器
-
-### 方法3:从命令行打开
-1. 在项目目录中运行:
-   ```bash
-   cursor .
-   ```
-   或者
-   ```bash
-   code .
-   ```
-
-## 项目访问
-- 启动成功后,访问:http://localhost:5500
-- API文档:http://localhost:5500/api/
-
-## 常见问题解决
-
-### 1. 端口被占用
-如果5500端口被占用,可以修改`app/config/config.py`中的PORT配置
-
-### 2. 数据库连接失败
-确保PostgreSQL服务正在运行,或者修改配置文件中的数据库连接信息
-
-### 3. 依赖安装失败
-尝试升级pip:
-```bash
-python -m pip install --upgrade pip
-```
-
-### 4. Cursor仍然无法识别项目
-1. 运行`python check_project_status.py`检查项目状态
-2. 确保Cursor使用正确的Python解释器(虚拟环境中的Python)
-3. 使用生成的`.code-workspace`文件打开项目
-4. 重启Cursor编辑器
-5. 检查Cursor的Python扩展是否正确安装
-
-### 5. 项目结构问题
-如果项目结构不完整,运行以下命令重新生成:
-```bash
-python check_project_status.py
-```
-
-## 项目结构
-```
-DataOps-platform/
-├── app/                    # 应用主目录
-│   ├── api/               # API路由
-│   ├── config/            # 配置文件
-│   ├── models/            # 数据模型
-│   └── services/          # 业务逻辑
-├── database/              # 数据库相关文件
-├── docs/                  # 文档
-├── tests/                 # 测试文件
-├── .vscode/               # VSCode/Cursor配置
-│   ├── settings.json      # 工作区设置
-│   └── launch.json        # 调试配置
-├── requirements.txt       # Python依赖
-├── pyproject.toml         # 现代Python项目配置
-├── setup.py               # 向后兼容配置
-├── .cursorrules           # Cursor编辑器规则
-├── MANIFEST.in            # 项目文件清单
-├── application.py         # 应用入口
-├── run_project.bat        # Windows启动脚本
-├── run_project.py         # Python启动脚本
-├── check_project_status.py # 项目状态检查工具
-└── CURSOR_FIX_README.md   # 本说明文档
-```
-
-## 新增文件说明
-
-### pyproject.toml
-- 现代Python项目的标准配置文件
-- 定义项目元数据、依赖和工具配置
-- Cursor使用此文件识别Python项目
-
-### setup.py
-- 提供向后兼容性
-- 支持旧版本的Python工具链
-- 确保项目能被各种工具正确识别
-
-### .cursorrules
-- Cursor编辑器的项目特定规则
-- 定义代码风格、架构和开发指南
-- 帮助Cursor更好地理解项目结构
-
-### .vscode/settings.json
-- 工作区特定的设置
-- 配置Python解释器、代码格式化和测试
-- 确保一致的开发环境
-
-### .vscode/launch.json
-- 调试配置
-- 支持Flask应用调试
-- 提供多种启动选项
-
-### check_project_status.py
-- 项目状态诊断工具
-- 检查所有必要的文件和配置
-- 自动生成Cursor工作区文件
-
-## 技术支持
-如果问题仍然存在,请检查:
-1. Python版本是否为3.8+
-2. 是否有足够的磁盘空间
-3. 防火墙设置是否阻止了端口访问
-4. 系统环境变量是否正确设置
-5. 运行`python check_project_status.py`获取详细诊断信息

+ 0 - 319
docs/archive/CURSOR_TASK_AUTOMATION_SUMMARY.md

@@ -1,319 +0,0 @@
-# Cursor任务自动执行机制 - 实施总结
-
-## 📋 问题分析
-
-### 原始问题
-用户报告:
-- ✅ DataOps-platform-task-manager MCP已读取任务
-- ✅ 数据库中任务状态已改为processing
-- ❌ **Cursor没有收到任务指令,没有开始执行任务**
-
-### 根本原因
-**MCP协议的工作机制**:
-1. MCP(Model Context Protocol)是**被动协议**
-2. MCP工具必须被**主动调用**才会执行
-3. MCP返回的只是文本结果,**不会自动触发Cursor执行操作**
-4. 需要用户或脚本主动调用MCP工具来获取和执行任务
-
----
-
-## 🎯 解决方案
-
-我们实施了**3种方案**,从简单到自动化递进:
-
-### 方案1:手动触发(最简单)✅
-- 在Cursor Chat中说:"请检查并执行所有pending任务"
-- Cursor会调用MCP工具获取并执行任务
-- **适合**:临时使用、调试
-
-### 方案2:自动执行脚本(推荐生产环境)✅
-- 脚本:`scripts/auto_execute_tasks.py`
-- 功能:定期检查数据库,自动执行pending任务
-- **适合**:生产环境、无人值守
-
-### 方案3:任务提示Agent(友好界面)✅
-- 脚本:`scripts/cursor_task_agent.py`
-- 功能:创建任务提示文件,通知用户有新任务
-- **适合**:团队协作、可视化管理
-
----
-
-## 📁 交付成果
-
-### 1. 核心脚本(2个)
-
-#### `scripts/auto_execute_tasks.py`
-- **功能**:自动检查并执行pending任务
-- **特性**:
-  - 直接连接PostgreSQL数据库
-  - 支持单次执行(`--once`)或持续监控
-  - 以特定格式输出任务,供Cursor识别
-  - 创建`.cursor/pending_tasks.json`通知文件
-- **使用**:
-  ```bash
-  # 执行一次
-  python scripts/auto_execute_tasks.py --once
-  
-  # 持续监控(每5分钟)
-  python scripts/auto_execute_tasks.py --interval 300
-  
-  # 后台运行
-  Start-Process python -ArgumentList "scripts/auto_execute_tasks.py" -WindowStyle Hidden
-  ```
-
-#### `scripts/cursor_task_agent.py`
-- **功能**:创建任务提示文件
-- **特性**:
-  - 从数据库读取pending任务
-  - 为每个任务创建Markdown提示文件
-  - 保存在`.cursor/task_prompts/`目录
-  - 支持守护进程模式
-- **使用**:
-  ```bash
-  # 执行一次
-  python scripts/cursor_task_agent.py --once
-  
-  # 守护进程模式
-  python scripts/cursor_task_agent.py --daemon --interval 300
-  ```
-
-### 2. 文档(3个)
-
-#### `docs/CURSOR_AUTO_TASK_EXECUTION.md`
-- 完整的技术文档
-- 包含:
-  - 问题背景与分析
-  - 3种解决方案详解
-  - 配置说明
-  - 故障排查
-  - MCP工具使用指南
-
-#### `docs/TASK_EXECUTION_QUICK_START.md`
-- 快速开始指南
-- 包含:
-  - 3种方式的对比
-  - 立即开始步骤
-  - 使用建议
-  - 常见问题解决
-
-#### `CURSOR_TASK_AUTOMATION_SUMMARY.md`(本文档)
-- 实施总结
-- 包含:
-  - 问题分析
-  - 解决方案
-  - 交付成果
-  - 测试验证
-
-### 3. 任务执行示例
-
-#### 已完成任务:Task ID 8
-- **任务名称**:从数据源中导入科室对照表
-- **状态**:✅ completed
-- **生成文件**:
-  - `app/core/data_flow/import_dept_mapping.py` - 数据导入脚本
-  - `app/core/data_flow/import_dept_config.json` - 数据源配置
-- **说明**:演示了自动执行机制的完整流程
-
----
-
-## ✅ 测试验证
-
-### 测试1:脚本功能测试
-```bash
-python scripts/auto_execute_tasks.py --once
-```
-**结果**:✅ 成功
-- 脚本正常运行
-- 成功连接数据库
-- 正确识别pending任务状态
-
-### 测试2:任务执行测试
-- 通过Cursor Chat执行task_id=8
-- **结果**:✅ 成功
-  - 生成了Python代码文件
-  - 任务状态更新为completed
-  - MCP工具正常工作
-
-### 测试3:代码质量检查
-```bash
-read_lints
-```
-**结果**:✅ 所有脚本无linter错误
-
----
-
-## 🔄 工作流程
-
-### 完整自动化流程
-
-```
-1. 用户在Web界面创建任务
-   ↓
-2. 任务保存到PostgreSQL (status = 'pending')
-   ↓
-3. auto_execute_tasks.py 定期检查数据库
-   ↓
-4. 发现pending任务,打印任务详情
-   ↓
-5. 创建 .cursor/pending_tasks.json 通知文件
-   ↓
-6. Cursor检测到通知(或用户主动查询)
-   ↓
-7. Cursor调用 execute_task MCP工具
-   ↓
-8. task-manager MCP将状态改为 'processing'
-   ↓
-9. 返回执行指令给Cursor
-   ↓
-10. Cursor根据任务描述生成Python代码
-   ↓
-11. Cursor自动调用 update_task_status 工具
-   ↓
-12. 任务状态更新为 'completed'
-   ↓
-13. 任务完成!✅
-```
-
----
-
-## 📊 方案对比
-
-| 特性 | 方案1<br/>手动触发 | 方案2<br/>自动脚本 | 方案3<br/>任务提示 |
-|------|-------|-------|-------|
-| **自动化程度** | ⭐ 低 | ⭐⭐⭐ 高 | ⭐⭐ 中 |
-| **使用难度** | ⭐⭐⭐ 简单 | ⭐⭐ 中等 | ⭐⭐ 中等 |
-| **配置需求** | ⭐⭐⭐ 无需配置 | ⭐⭐ 需要psycopg2 | ⭐⭐ 需要psycopg2 |
-| **适用场景** | 开发调试 | 生产环境 | 团队协作 |
-| **人工干预** | 每次都需要 | 无需干预 | 看到提示后执行 |
-
-### 推荐使用
-
-- **开发环境**:方案1(手动触发)
-- **生产环境**:方案2(自动脚本)
-- **团队协作**:方案3(任务提示)+ 方案1
-
----
-
-## 🚀 立即开始
-
-### 对于当前任务
-
-在Cursor Chat中输入:
-```
-请检查并执行所有pending任务
-```
-
-### 设置自动化
-
-1. 确保安装依赖:
-```bash
-pip install psycopg2-binary
-```
-
-2. 启动自动执行脚本:
-```bash
-python scripts/auto_execute_tasks.py
-```
-
-3. (可选)在后台运行:
-```powershell
-Start-Process python -ArgumentList "scripts/auto_execute_tasks.py" -WindowStyle Hidden
-```
-
----
-
-## 🔧 配置说明
-
-### 数据库配置
-配置文件:`mcp-servers/task-manager/config.json`
-```json
-{
-  "database": {
-    "uri": "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
-  }
-}
-```
-
-### 脚本参数
-
-#### auto_execute_tasks.py
-- `--once`:执行一次检查
-- `--interval N`:检查间隔(秒),默认300
-
-#### cursor_task_agent.py
-- `--once`:执行一次检查
-- `--daemon`:守护进程模式
-- `--interval N`:检查间隔(秒),默认300
-
----
-
-## 📈 后续优化建议
-
-### 短期优化(1-2周)
-1. ✅ 添加更详细的日志记录
-2. ✅ 创建任务执行历史追踪
-3. ⏳ 添加邮件/企业微信通知
-4. ⏳ 实现任务优先级支持
-
-### 长期优化(1-3个月)
-1. ⏳ 将脚本封装为系统服务
-2. ⏳ 添加Web管理界面
-3. ⏳ 实现任务依赖关系
-4. ⏳ 集成CI/CD流程
-
----
-
-## 📝 注意事项
-
-### 安全性
-- ✅ 数据库密码配置在独立的config.json中
-- ⚠️ 确保config.json不要提交到版本控制
-- ⚠️ 生产环境建议使用环境变量
-
-### 性能
-- ✅ 脚本使用连接池管理数据库连接
-- ✅ 批量处理时每100条提交一次
-- ⚠️ 大数据量导入建议使用limit参数
-
-### 可靠性
-- ✅ 完善的错误处理和日志记录
-- ✅ 支持事务回滚
-- ⚠️ 建议配置进程监控(如supervisor)
-
----
-
-## 🎉 结论
-
-### 已解决的问题
-✅ **MCP与Cursor的互动机制** - 已分析清楚
-✅ **任务自动执行** - 实现了3种解决方案
-✅ **文档完善** - 提供了完整的使用指南
-✅ **测试验证** - 成功执行了示例任务
-
-### 核心成果
-1. **2个自动化脚本** - 满足不同场景需求
-2. **3份详细文档** - 从快速开始到深入使用
-3. **1个完整示例** - 演示端到端流程
-4. **0个linter错误** - 代码质量保证
-
-### 用户收益
-- 🚀 **效率提升**:从手动执行到自动化执行
-- 🎯 **流程优化**:任务创建→自动执行→状态更新
-- 📚 **知识沉淀**:完整的文档和最佳实践
-- 🔧 **灵活选择**:3种方案适配不同场景
-
----
-
-## 📞 获取帮助
-
-- **快速开始**:`docs/TASK_EXECUTION_QUICK_START.md`
-- **完整文档**:`docs/CURSOR_AUTO_TASK_EXECUTION.md`
-- **MCP说明**:`mcp-servers/task-manager/README.md`
-
-**祝您使用愉快!🎉**
-
-
-
-
-
-

+ 0 - 163
docs/archive/DDL_PARSER_TIMEOUT_FIX.md

@@ -1,163 +0,0 @@
-# DDL Parser 超时问题修复说明
-
-## 问题描述
-
-在调用 `/api/resource/ddl/parse` 接口时出现超时错误:
-
-```
-message: "API请求失败: HTTPSConnectionPool(host='dashscope.aliyuncs.com', port=443): Read timed out. (read timeout=30)"
-```
-
-## 问题原因
-
-1. **超时时间过短**:原始代码使用固定的 30 秒超时时间,对于复杂的 DDL 解析任务可能不够
-2. **无重试机制**:网络波动或 API 临时不可用时,请求直接失败,没有自动重试
-3. **错误处理不够健壮**:没有区分超时错误和其他类型的错误
-
-## 解决方案
-
-### 1. 增加超时时间
-
-将默认超时时间从 30 秒增加到 60 秒,并支持自定义配置:
-
-```python
-def __init__(self, api_key=None, timeout=60, max_retries=3):
-    self.timeout = timeout  # 默认60秒
-    self.max_retries = max_retries  # 默认重试3次
-```
-
-### 2. 实现自动重试机制
-
-新增 `_make_llm_request` 方法,支持:
-
-- **指数退避策略**:重试等待时间逐渐增加(2秒、4秒、8秒)
-- **区分错误类型**:
-  - `requests.Timeout`:超时错误,可重试
-  - `requests.RequestException`:网络错误,可重试
-  - 其他异常:不重试
-- **详细日志记录**:记录每次尝试的状态
-
-```python
-def _make_llm_request(self, payload, operation_name="LLM请求"):
-    """发送LLM请求,支持自动重试"""
-    for attempt in range(self.max_retries):
-        try:
-            if attempt > 0:
-                wait_time = 2 ** attempt  # 指数退避
-                time.sleep(wait_time)
-            
-            response = requests.post(
-                f"{self.base_url}/chat/completions",
-                headers=self.headers,
-                json=payload,
-                timeout=self.timeout
-            )
-            response.raise_for_status()
-            return response.json()
-            
-        except requests.Timeout as e:
-            logger.warning(f"{operation_name} 超时: {str(e)}")
-        except requests.RequestException as e:
-            logger.warning(f"{operation_name} 失败: {str(e)}")
-```
-
-### 3. 统一错误处理
-
-所有 LLM 调用方法(`parse_ddl`、`parse_db_conn_str`、`valid_db_conn_str`)都使用统一的重试机制:
-
-```python
-def parse_ddl(self, sql_content):
-    result = self._make_llm_request(payload, "DDL解析")
-    
-    if not result:
-        return {
-            "code": 500,
-            "message": f"API请求失败: 在{self.max_retries}次尝试后仍然失败"
-        }
-    # ... 处理成功结果
-```
-
-## 改进效果
-
-### 1. 可靠性提升
-
-- ✅ 自动重试:网络波动时自动重试,成功率显著提高
-- ✅ 超时容忍:更长的超时时间适应复杂查询
-- ✅ 指数退避:避免对 API 造成压力
-
-### 2. 可观测性提升
-
-- ✅ 详细日志:记录每次尝试的状态和结果
-- ✅ 操作区分:不同操作有明确的名称标识
-- ✅ 错误追踪:清晰记录失败原因
-
-### 3. 可配置性提升
-
-- ✅ 自定义超时:可根据需要调整超时时间
-- ✅ 自定义重试:可根据网络环境调整重试次数
-- ✅ 向后兼容:默认参数保证现有代码无需修改
-
-## 使用示例
-
-### 默认配置(推荐)
-
-```python
-from app.core.llm.ddl_parser import DDLParser
-
-# 使用默认配置:60秒超时,最多重试3次
-parser = DDLParser()
-result = parser.parse_ddl(sql_content)
-```
-
-### 自定义配置
-
-```python
-# 针对复杂任务:120秒超时,最多重试5次
-parser = DDLParser(timeout=120, max_retries=5)
-result = parser.parse_ddl(complex_sql_content)
-
-# 快速失败模式:30秒超时,不重试
-parser = DDLParser(timeout=30, max_retries=1)
-result = parser.parse_ddl(simple_sql_content)
-```
-
-## 测试验证
-
-运行测试脚本验证修复效果:
-
-```bash
-# 快速测试
-python quick_test_ddl.py
-
-# 完整测试(包含超时处理验证)
-python test_ddl_timeout_fix.py
-```
-
-## 相关文件
-
-### 修改的文件
-
-- `app/core/llm/ddl_parser.py`:添加超时和重试机制
-
-### 新增的文件
-
-- `test_ddl_timeout_fix.py`:超时和重试测试脚本
-- `DDL_PARSER_TIMEOUT_FIX.md`:本文档
-
-## 注意事项
-
-1. **API 配额**:重试机制会增加 API 调用次数,注意监控配额使用
-2. **响应时间**:最坏情况下(3次重试都超时),总耗时可能达到 60s × 3 + 2s + 4s = 186秒
-3. **日志监控**:建议监控日志中的重试频率,如果重试过于频繁,可能需要检查网络或 API 服务状态
-
-## 未来优化建议
-
-1. **可配置的退避策略**:支持线性退避、固定间隔等多种策略
-2. **断路器模式**:当 API 持续失败时,快速失败避免长时间等待
-3. **缓存机制**:对相同的 DDL 语句缓存解析结果,减少 API 调用
-4. **异步处理**:对于大批量 DDL 解析,考虑使用异步任务队列
-
-## 总结
-
-通过增加超时时间、实现自动重试机制和改进错误处理,DDL Parser 的稳定性和可靠性得到显著提升。现在即使在网络不稳定的情况下,也能更好地完成 DDL 解析任务。
-

+ 0 - 333
docs/archive/DDL_PARSE_FIX_SUMMARY.md

@@ -1,333 +0,0 @@
-# DDL 解析错误修复总结
-
-## 🐛 问题描述
-
-在执行 `POST /api/data_resource/ddl/parse` 接口时,出现错误:
-```
-'int' object does not support item assignment
-```
-
-## 🔍 问题分析
-
-### 错误位置
-
-**文件**: `app/api/data_resource/routes.py`  
-**函数**: `ddl_identify()` (行 614-683)  
-**错误行**: 654 和 672
-
-### 错误代码
-
-```python
-# 第654行
-ddl_list[table_name]["exist"] = False
-
-# 第672行
-ddl_list[table_name]["exist"] = exists
-```
-
-### 根本原因
-
-代码假设 `ddl_list[table_name]` 始终是一个字典对象,但实际上:
-
-1. **LLM 返回结构不一致**: `DDLParser.parse_ddl()` 方法使用 LLM 解析 SQL,返回的 JSON 结构可能不符合预期
-2. **缺少类型检查**: 代码没有验证 `ddl_list[table_name]` 是否为字典类型就直接进行赋值操作
-3. **异常场景**: 当 `ddl_list[table_name]` 是整数、字符串或其他非字典类型时,尝试使用 `[]` 操作符赋值会失败
-
-### 可能的异常情况
-
-| 情况 | `ddl_list[table_name]` 的类型 | 错误 |
-|------|------------------------------|------|
-| LLM 返回格式错误 | `int`, `str`, `list` | ✗ 类型不支持 item assignment |
-| 解析失败 | `None` | ✗ NoneType 不支持 item assignment |
-| 正常情况 | `dict` | ✓ 正常 |
-
-## ✅ 解决方案
-
-### 修复策略
-
-添加类型检查,确保只对字典类型的值进行赋值操作。
-
-### 修复代码
-
-#### 第 653-658 行(设置默认状态)
-
-**修复前**:
-```python
-# 首先为所有表设置默认的exist状态
-for table_name in table_names:
-    ddl_list[table_name]["exist"] = False
-```
-
-**修复后**:
-```python
-# 首先为所有表设置默认的exist状态
-for table_name in table_names:
-    # 确保 ddl_list[table_name] 是字典类型
-    if isinstance(ddl_list[table_name], dict):
-        ddl_list[table_name]["exist"] = False
-    else:
-        logger.warning(f"表 {table_name} 的值不是字典类型: {type(ddl_list[table_name])}")
-```
-
-#### 第 671-677 行(更新存在状态)
-
-**修复前**:
-```python
-# 更新存在的表的状态
-for record in table_results:
-    table_name = record["name"]
-    exists = record["exists"]
-    if table_name in ddl_list:
-        ddl_list[table_name]["exist"] = exists
-```
-
-**修复后**:
-```python
-# 更新存在的表的状态
-for record in table_results:
-    table_name = record["name"]
-    exists = record["exists"]
-    # 确保表名存在且对应的值是字典类型
-    if table_name in ddl_list and isinstance(ddl_list[table_name], dict):
-        ddl_list[table_name]["exist"] = exists
-```
-
-## 🎯 修复效果
-
-### 1. 类型安全
-
-✅ 在赋值前检查类型,避免类型错误
-✅ 对非字典类型给出警告日志,便于问题排查
-
-### 2. 健壮性提升
-
-✅ 能够处理 LLM 返回不一致的情况
-✅ 不会因为个别表的数据格式错误而导致整个请求失败
-
-### 3. 日志完善
-
-✅ 添加警告日志记录异常类型
-✅ 便于调试和监控
-
-## 📊 修复对比
-
-| 特性 | 修复前 | 修复后 |
-|------|--------|--------|
-| 类型检查 | ❌ 无 | ✅ 有 |
-| 错误处理 | ❌ 崩溃 | ✅ 优雅降级 |
-| 日志记录 | ❌ 无 | ✅ 警告日志 |
-| 用户体验 | ❌ 500 错误 | ✅ 返回部分结果 |
-
-## 🔧 进一步优化建议
-
-### 1. 数据验证
-
-在 `parse_ddl` 返回后立即验证数据结构:
-
-```python
-def validate_ddl_structure(ddl_list):
-    """验证DDL解析结果的结构"""
-    if not isinstance(ddl_list, dict):
-        return False, "ddl_list 必须是字典类型"
-    
-    for table_name, table_data in ddl_list.items():
-        if not isinstance(table_data, dict):
-            return False, f"表 {table_name} 的数据必须是字典类型"
-        
-        # 检查必要字段
-        if "meta" not in table_data:
-            return False, f"表 {table_name} 缺少 meta 字段"
-        
-        if not isinstance(table_data["meta"], list):
-            return False, f"表 {table_name} 的 meta 必须是列表类型"
-    
-    return True, "验证通过"
-
-# 使用
-ddl_list = parser.parse_ddl(sql_content)
-is_valid, message = validate_ddl_structure(ddl_list)
-if not is_valid:
-    logger.error(f"DDL结构验证失败: {message}")
-    return jsonify(failed(message))
-```
-
-### 2. LLM 响应标准化
-
-在 `DDLParser` 中添加响应格式标准化:
-
-```python
-def parse_ddl(self, sql_content):
-    """解析DDL语句,返回标准化的结构"""
-    # ... 现有代码 ...
-    
-    # 标准化返回结果
-    if isinstance(parsed_result, dict):
-        # 确保每个表的数据都是字典类型
-        for table_name in list(parsed_result.keys()):
-            if not isinstance(parsed_result[table_name], dict):
-                logger.warning(f"移除非字典类型的表数据: {table_name}")
-                del parsed_result[table_name]
-    
-    return parsed_result
-```
-
-### 3. 添加单元测试
-
-```python
-def test_ddl_identify_with_invalid_structure():
-    """测试处理无效结构的情况"""
-    # 模拟返回无效结构
-    invalid_ddl = {
-        "table1": {"name_zh": "表1", "meta": []},
-        "table2": 123,  # 错误:整数类型
-        "table3": "invalid"  # 错误:字符串类型
-    }
-    
-    # 验证能够正常处理
-    result = process_ddl_list(invalid_ddl)
-    assert "table1" in result
-    assert result["table1"]["exist"] == False
-    # table2 和 table3 应该被跳过
-```
-
-### 4. 错误恢复机制
-
-```python
-try:
-    ddl_list = parser.parse_ddl(sql_content)
-except Exception as e:
-    logger.error(f"DDL解析失败: {str(e)}")
-    # 尝试使用备用解析方法
-    ddl_list = fallback_parse_ddl(sql_content)
-```
-
-## 📋 测试验证
-
-### 测试场景
-
-#### 1. 正常情况
-```json
-{
-  "users": {
-    "name_zh": "用户表",
-    "meta": [...]
-  }
-}
-```
-✅ 应该正常添加 `exist` 字段
-
-#### 2. 异常情况 - 整数
-```json
-{
-  "users": 123
-}
-```
-✅ 应该记录警告日志,跳过该表
-
-#### 3. 异常情况 - 字符串
-```json
-{
-  "users": "invalid"
-}
-```
-✅ 应该记录警告日志,跳过该表
-
-#### 4. 异常情况 - null
-```json
-{
-  "users": null
-}
-```
-✅ 应该记录警告日志,跳过该表
-
-#### 5. 混合情况
-```json
-{
-  "users": {
-    "name_zh": "用户表",
-    "meta": [...]
-  },
-  "orders": 456,
-  "products": {
-    "name_zh": "产品表",
-    "meta": [...]
-  }
-}
-```
-✅ 应该正常处理 `users` 和 `products`,跳过 `orders`
-
-### 测试方法
-
-```bash
-# 使用 curl 测试
-curl -X POST http://localhost:5500/api/data_resource/ddl/parse \
-  -H "Content-Type: application/json" \
-  -d '{"sql": "CREATE TABLE users (id INT, name VARCHAR(100));"}'
-
-# 检查返回结果和日志
-```
-
-## 🚨 监控建议
-
-### 1. 日志监控
-
-监控以下警告日志:
-```
-表 {table_name} 的值不是字典类型: {type}
-```
-
-如果频繁出现,说明 LLM 返回格式不稳定,需要优化提示词。
-
-### 2. 指标监控
-
-- **成功率**: DDL 解析成功的比例
-- **异常类型统计**: 记录各种类型错误的频率
-- **响应时间**: 监控 LLM 调用的响应时间
-
-### 3. 告警规则
-
-- 当异常类型日志在 5 分钟内超过 10 次时触发告警
-- 当 DDL 解析失败率超过 20% 时触发告警
-
-## ✅ 修复状态
-
-- ✅ 代码已修复
-- ✅ 类型检查已添加
-- ✅ 日志记录已完善
-- ✅ Linter 检查通过
-- ✅ 向后兼容
-
-## 📝 相关文件
-
-| 文件 | 修改内容 |
-|------|---------|
-| `app/api/data_resource/routes.py` | 添加类型检查,修复 item assignment 错误 |
-| `app/core/llm/ddl_parser.py` | 无修改(建议未来优化) |
-
-## 🎉 总结
-
-### 问题
-
-执行 DDL 解析接口时出现 `'int' object does not support item assignment` 错误。
-
-### 原因
-
-代码未验证 `ddl_list[table_name]` 的类型就直接进行字典操作。
-
-### 解决方案
-
-在赋值前添加 `isinstance()` 类型检查,确保只对字典类型进行操作。
-
-### 效果
-
-✅ **错误修复**: 不再出现类型错误
-✅ **健壮性提升**: 能够处理异常数据结构
-✅ **日志完善**: 便于问题排查和监控
-✅ **用户体验改善**: 即使部分数据异常也能返回有效结果
-
----
-
-**修复时间**: 2025-11-03  
-**修复文件**: `app/api/data_resource/routes.py` (行 653-680)  
-**状态**: ✅ 已完成
-

+ 0 - 363
docs/archive/DDL_Parse_API修复说明.md

@@ -1,363 +0,0 @@
-# DDL Parse API 格式修复说明
-
-## 问题描述
-
-`/ddl/parse` 接口存在格式不一致的问题:
-
-### 问题1:提示词冲突
-在 `DDLParser.parse_ddl()` 方法中存在两个不同的格式要求:
-- **System Message** 要求单表格式:`table_info + columns`
-- **User Message** 使用 `_optimize_ddl_prompt()` 要求多表格式:`{table_name: {name_zh, meta: [...]}}`
-
-两个提示词相互冲突,导致LLM返回不确定的格式。
-
-### 问题2:缺少关键字段
-当前格式缺少参考文档中的重要字段:
-- ❌ `is_primary` - 是否主键
-- ❌ `comment` - 注释内容
-- ❌ `nullable` - 是否可为空
-
----
-
-## 修复方案
-
-### 1. 统一提示词格式
-
-**修复前**(第86-140行):
-```python
-def parse_ddl(self, sql_content):
-    prompt = self._optimize_ddl_prompt()
-    payload = {
-        "model": self.model_name,
-        "messages": [
-            {
-                "role": "system",
-                "content": """请从上传的SQL DDL文件中提取数据库表的结构信息...
-                {
-                  "table_info": {...},
-                  "columns": [...]
-                }
-                """
-            },
-            {
-                "role": "user", 
-                "content": f"{prompt}\n\n{sql_content}"
-            }
-        ]
-    }
-```
-
-**修复后**:
-```python
-def parse_ddl(self, sql_content):
-    prompt = self._optimize_ddl_prompt()
-    payload = {
-        "model": self.model_name,
-        "messages": [
-            {
-                "role": "system",
-                "content": "你是一个专业的SQL DDL语句解析专家,擅长从DDL语句中提取表结构信息并转换为结构化的JSON格式。"
-            },
-            {
-                "role": "user", 
-                "content": f"{prompt}\n\n{sql_content}"
-            }
-        ]
-    }
-```
-
-### 2. 更新 `_optimize_ddl_prompt()` 函数
-
-**修复前**(第251-289行):
-```python
-def _optimize_ddl_prompt(self):
-    return """
-    ...
-    参考格式如下:
-    {
-        "users_table": {
-            "name_zh": "用户表",
-            "schema": "public",
-            "meta": [{
-                "name_en": "id",
-                "data_type": "integer",
-                "name_zh": "用户ID"
-            }]
-        }
-    }
-    """
-```
-
-**修复后**:
-```python
-def _optimize_ddl_prompt(self):
-    return """
-    请解析以下DDL建表语句,并按照指定的JSON格式返回结果:
-    
-    规则说明:
-    1. 从DDL语句中识别所有表,可能会有多个表。每个表作为一个独立条目。
-    2. 表的英文名称(name_en)使用原始大小写,不要转换为小写。
-    3. 表的中文名称(name_zh)提取规则:
-       - 优先从COMMENT ON TABLE语句中提取
-       - 如果没有注释,则name_zh为空字符串
-    4. 对于每个表,提取所有字段信息到columns数组中,每个字段包含:
-       - name_zh: 字段中文名称
-       - name_en: 字段英文名称(保持原始大小写)
-       - data_type: 数据类型(包含长度信息,如VARCHAR(22))
-       - is_primary: 是否主键("是"或"否",从PRIMARY KEY约束判断)
-       - comment: 注释内容(从COMMENT ON COLUMN提取完整注释)
-       - nullable: 是否可为空("是"或"否",从NOT NULL约束判断)
-    
-    返回格式(支持多表):
-    {
-        "TB_JC_KSDZB": {
-            "table_info": {
-                "name_zh": "科室对照表",
-                "name_en": "TB_JC_KSDZB"
-            },
-            "columns": [
-                {
-                    "name_zh": "医疗机构代码",
-                    "name_en": "YLJGDM",
-                    "data_type": "VARCHAR(22)",
-                    "is_primary": "是",
-                    "comment": "医疗机构代码,复合主键",
-                    "nullable": "否"
-                }
-            ]
-        }
-    }
-    """
-```
-
----
-
-## 修复后的格式
-
-### 单表示例(符合参考文档)
-
-```json
-[
-  {
-    "table_info": {
-      "name_zh": "科室对照表",
-      "name_en": "TB_JC_KSDZB"
-    },
-    "columns": [
-      {
-        "name_zh": "医疗机构代码",
-        "name_en": "YLJGDM",
-        "data_type": "VARCHAR(22)",
-        "is_primary": "是",
-        "comment": "医疗机构代码,复合主键",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室代码",
-        "name_en": "HISKSDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "是",
-        "comment": "HIS科室代码,主键、唯一",
-        "nullable": "否"
-      }
-    ]
-  }
-]
-```
-
-### 多表示例
-
-```json
-[
-  {
-    "table_info": {
-      "name_zh": "科室对照表",
-      "name_en": "TB_JC_KSDZB"
-    },
-    "columns": [...]
-  },
-  {
-    "table_info": {
-      "name_zh": "用户表",
-      "name_en": "TB_USER"
-    },
-    "columns": [...]
-  }
-]
-```
-
-### API完整响应格式
-
-```json
-{
-  "code": 200,
-  "data": [
-    {
-      "table_info": {
-        "name_zh": "科室对照表",
-        "name_en": "TB_JC_KSDZB"
-      },
-      "columns": [...]
-    }
-  ],
-  "message": "操作成功"
-}
-```
-
----
-
-## 关键改进
-
-### ✅ 1. 统一提示词
-- 删除了冲突的 system message
-- 只使用 `_optimize_ddl_prompt()` 来定义格式
-
-### ✅ 2. 添加缺失字段
-- `is_primary`: 是否主键("是"/"否")
-- `comment`: 完整的注释内容
-- `nullable`: 是否可为空("是"/"否")
-
-### ✅ 3. 使用标准结构
-- `table_info`: 包含表的基本信息
-- `columns`: 字段数组,每个字段包含所有必需属性
-
-### ✅ 4. 保持原始大小写
-- 表名和字段名保持原始大小写(如 `TB_JC_KSDZB`, `YLJGDM`)
-
-### ✅ 5. 支持多表
-- 使用JSON数组格式返回多个表
-- 不使用表名作为key,而是将所有表放在数组中
-- 可以一次解析包含多个表的DDL文件
-
----
-
-## 测试用例
-
-### 输入:科室对照表SQL
-
-```sql
-CREATE TABLE TB_JC_KSDZB (
-    YLJGDM VARCHAR(22) NOT NULL,
-    HISKSDM CHAR(20) NOT NULL,
-    HISKSMC CHAR(20) NOT NULL,
-    PRIMARY KEY (YLJGDM, HISKSDM)
-);
-
-COMMENT ON TABLE TB_JC_KSDZB IS '科室对照表';
-COMMENT ON COLUMN TB_JC_KSDZB.YLJGDM IS '医疗机构代码,复合主键';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSDM IS 'HIS科室代码,主键、唯一';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSMC IS 'HIS科室名称';
-```
-
-### 预期输出
-
-```json
-[
-  {
-    "table_info": {
-      "name_zh": "科室对照表",
-      "name_en": "TB_JC_KSDZB"
-    },
-    "columns": [
-      {
-        "name_zh": "医疗机构代码",
-        "name_en": "YLJGDM",
-        "data_type": "VARCHAR(22)",
-        "is_primary": "是",
-        "comment": "医疗机构代码,复合主键",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室代码",
-        "name_en": "HISKSDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "是",
-        "comment": "HIS科室代码,主键、唯一",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室名称",
-        "name_en": "HISKSMC",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "HIS科室名称",
-        "nullable": "否"
-      }
-    ]
-  }
-]
-```
-
----
-
-## 格式验证清单
-
-- [x] 返回数组格式(支持单表和多表)
-- [x] 每个表使用 `table_info` + `columns` 结构
-- [x] `table_info` 包含 `name_zh` 和 `name_en`
-- [x] `columns` 是数组,每个元素包含:
-  - [x] `name_zh` - 字段中文名
-  - [x] `name_en` - 字段英文名
-  - [x] `data_type` - 数据类型(含长度)
-  - [x] `is_primary` - 是否主键
-  - [x] `comment` - 注释内容
-  - [x] `nullable` - 是否可空
-- [x] 使用JSON数组格式,不使用表名作为key
-- [x] 表名和字段名保持原始大小写
-
----
-
-## 使用方法
-
-### 调用接口
-
-```bash
-# 方式1:上传SQL文件
-curl -X POST http://localhost:5000/api/resource/ddl/parse \
-  -F "file=@科室对照表_原始.sql"
-
-# 方式2:JSON请求
-curl -X POST http://localhost:5000/api/resource/ddl/parse \
-  -H "Content-Type: application/json" \
-  -d '{"sql": "CREATE TABLE ..."}'
-```
-
-### Python调用
-
-```python
-from app.core.llm.ddl_parser import DDLParser
-
-# 读取SQL文件
-with open('科室对照表_原始.sql', 'r', encoding='utf-8') as f:
-    sql_content = f.read()
-
-# 解析DDL
-parser = DDLParser()
-result = parser.parse_ddl(sql_content)
-
-# 结果格式(数组)
-# [
-#   {
-#     "table_info": {...},
-#     "columns": [...]
-#   }
-# ]
-```
-
----
-
-## 文件修改清单
-
-1. **app/core/llm/ddl_parser.py**
-   - 修改 `parse_ddl()` 方法(第86-140行)
-   - 修改 `_optimize_ddl_prompt()` 方法(第251-289行)
-
-2. **接口路由**
-   - `app/api/data_resource/routes.py` - `/ddl/parse` 接口(第602行)
-
----
-
-## 更新时间
-
-2024-11-27
-

+ 0 - 159
docs/archive/DDL_Parse_数组格式示例.json

@@ -1,159 +0,0 @@
-{
-  "说明": "DDL Parse API 返回格式 - 使用JSON数组,不使用表名作为key",
-  
-  "单表示例": {
-    "描述": "即使只有一个表,也返回数组格式",
-    "API响应": {
-      "code": 200,
-      "data": [
-        {
-          "table_info": {
-            "name_zh": "科室对照表",
-            "name_en": "TB_JC_KSDZB"
-          },
-          "columns": [
-            {
-              "name_zh": "医疗机构代码",
-              "name_en": "YLJGDM",
-              "data_type": "VARCHAR(22)",
-              "is_primary": "是",
-              "comment": "医疗机构代码,复合主键",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "HIS科室代码",
-              "name_en": "HISKSDM",
-              "data_type": "CHAR(20)",
-              "is_primary": "是",
-              "comment": "HIS科室代码,主键、唯一",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "HIS科室名称",
-              "name_en": "HISKSMC",
-              "data_type": "CHAR(20)",
-              "is_primary": "否",
-              "comment": "HIS科室名称",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "病案科室代码",
-              "name_en": "BAKSDM",
-              "data_type": "CHAR(20)",
-              "is_primary": "否",
-              "comment": "病案科室代码,应与\"病案明细表\"里的科室ID对应",
-              "nullable": "是"
-            }
-          ],
-          "exist": false
-        }
-      ],
-      "message": "操作成功"
-    }
-  },
-  
-  "多表示例": {
-    "描述": "多个表都放在数组中,不使用表名作为key",
-    "API响应": {
-      "code": 200,
-      "data": [
-        {
-          "table_info": {
-            "name_zh": "科室对照表",
-            "name_en": "TB_JC_KSDZB"
-          },
-          "columns": [
-            {
-              "name_zh": "医疗机构代码",
-              "name_en": "YLJGDM",
-              "data_type": "VARCHAR(22)",
-              "is_primary": "是",
-              "comment": "医疗机构代码",
-              "nullable": "否"
-            }
-          ],
-          "exist": false
-        },
-        {
-          "table_info": {
-            "name_zh": "用户表",
-            "name_en": "TB_USER"
-          },
-          "columns": [
-            {
-              "name_zh": "用户ID",
-              "name_en": "USER_ID",
-              "data_type": "INTEGER",
-              "is_primary": "是",
-              "comment": "用户唯一标识",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "用户名",
-              "name_en": "USERNAME",
-              "data_type": "VARCHAR(50)",
-              "is_primary": "否",
-              "comment": "用户登录名",
-              "nullable": "否"
-            }
-          ],
-          "exist": true
-        },
-        {
-          "table_info": {
-            "name_zh": "部门表",
-            "name_en": "TB_DEPARTMENT"
-          },
-          "columns": [
-            {
-              "name_zh": "部门ID",
-              "name_en": "DEPT_ID",
-              "data_type": "INTEGER",
-              "is_primary": "是",
-              "comment": "部门唯一标识",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "部门名称",
-              "name_en": "DEPT_NAME",
-              "data_type": "VARCHAR(100)",
-              "is_primary": "否",
-              "comment": "部门名称",
-              "nullable": "否"
-            }
-          ],
-          "exist": false
-        }
-      ],
-      "message": "操作成功"
-    }
-  },
-  
-  "格式特点": {
-    "1. 数组结构": "使用JSON数组返回,即使只有一个表也是数组",
-    "2. 不使用表名作为key": "所有表都是数组元素,不以表名为key",
-    "3. 统一结构": "每个表都包含 table_info 和 columns",
-    "4. exist字段": "由后端添加,表示该表在数据库中是否已存在",
-    "5. 支持多表": "一次可以解析包含多个CREATE TABLE语句的SQL文件"
-  },
-  
-  "字段说明": {
-    "table_info": "表的基本信息",
-    "table_info.name_zh": "表的中文名称(从COMMENT ON TABLE提取)",
-    "table_info.name_en": "表的英文名称(保持原始大小写)",
-    "columns": "字段数组",
-    "columns[].name_zh": "字段中文名称",
-    "columns[].name_en": "字段英文名称",
-    "columns[].data_type": "数据类型(含长度)",
-    "columns[].is_primary": "是否主键(是/否)",
-    "columns[].comment": "完整注释内容",
-    "columns[].nullable": "是否可为空(是/否)",
-    "exist": "表是否在数据库中已存在(由后端查询添加)"
-  },
-  
-  "前端使用示例": {
-    "JavaScript": "const tables = response.data;\ntables.forEach(table => {\n  console.log('表名:', table.table_info.name_zh);\n  console.log('字段数:', table.columns.length);\n  console.log('是否存在:', table.exist);\n});",
-    "Python": "tables = response['data']\nfor table in tables:\n    print(f\"表名: {table['table_info']['name_zh']}\")\n    print(f\"字段数: {len(table['columns'])}\")\n    print(f\"是否存在: {table['exist']}\")"
-  }
-}
-

+ 0 - 137
docs/archive/DDL_Parse_格式对比.json

@@ -1,137 +0,0 @@
-{
-  "修复说明": "DDL Parse API 格式修复 - 符合参考文档 DDLparse格式.txt",
-  
-  "参考格式_单表": {
-    "说明": "来自 docs/DDLparse格式.txt",
-    "格式": {
-      "code": 200,
-      "data": {
-        "table_info": {
-          "name_zh": "科室对照表",
-          "name_en": "TB_JC_KSDZB"
-        },
-        "columns": [
-          {
-            "name_zh": "医疗机构代码",
-            "name_en": "YLJGDM",
-            "data_type": "VARCHAR(22)",
-            "is_primary": "是",
-            "comment": "医疗机构代码,复合主键",
-            "nullable": "否"
-          }
-        ]
-      },
-      "message": "操作成功"
-    }
-  },
-  
-  "修复后格式_单表": {
-    "说明": "修复后的格式,使用数组返回,单表也是数组格式",
-    "格式": {
-      "code": 200,
-      "data": [
-        {
-          "table_info": {
-            "name_zh": "科室对照表",
-            "name_en": "TB_JC_KSDZB"
-          },
-          "columns": [
-            {
-              "name_zh": "医疗机构代码",
-              "name_en": "YLJGDM",
-              "data_type": "VARCHAR(22)",
-              "is_primary": "是",
-              "comment": "医疗机构代码,复合主键",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "HIS科室代码",
-              "name_en": "HISKSDM",
-              "data_type": "CHAR(20)",
-              "is_primary": "是",
-              "comment": "HIS科室代码,主键、唯一",
-              "nullable": "否"
-            },
-            {
-              "name_zh": "HIS科室名称",
-              "name_en": "HISKSMC",
-              "data_type": "CHAR(20)",
-              "is_primary": "否",
-              "comment": "HIS科室名称",
-              "nullable": "否"
-            }
-          ]
-        }
-      ],
-      "message": "操作成功"
-    }
-  },
-  
-  "修复后格式_多表": {
-    "说明": "修复后的格式,使用数组返回多个表,不使用表名作为key",
-    "格式": {
-      "code": 200,
-      "data": [
-        {
-          "table_info": {
-            "name_zh": "科室对照表",
-            "name_en": "TB_JC_KSDZB"
-          },
-          "columns": [
-            {
-              "name_zh": "医疗机构代码",
-              "name_en": "YLJGDM",
-              "data_type": "VARCHAR(22)",
-              "is_primary": "是",
-              "comment": "医疗机构代码,复合主键",
-              "nullable": "否"
-            }
-          ]
-        },
-        {
-          "table_info": {
-            "name_zh": "用户表",
-            "name_en": "TB_USER"
-          },
-          "columns": [
-            {
-              "name_zh": "用户ID",
-              "name_en": "USER_ID",
-              "data_type": "INTEGER",
-              "is_primary": "是",
-              "comment": "用户唯一标识",
-              "nullable": "否"
-            }
-          ]
-        }
-      ],
-      "message": "操作成功"
-    }
-  },
-  
-  "关键改进": [
-    "✅ 1. 删除了冲突的 system message,统一使用 _optimize_ddl_prompt()",
-    "✅ 2. 添加了缺失的字段:is_primary, comment, nullable",
-    "✅ 3. 使用 table_info + columns 结构(符合参考格式)",
-    "✅ 4. 保持表名和字段名原始大小写",
-    "✅ 5. 使用JSON数组格式返回,不使用表名作为key",
-    "✅ 6. 支持单表和多表,统一返回数组格式"
-  ],
-  
-  "字段说明": {
-    "table_info.name_zh": "表的中文名称,从COMMENT ON TABLE提取",
-    "table_info.name_en": "表的英文名称,保持原始大小写",
-    "columns[].name_zh": "字段中文名称,从COMMENT ON COLUMN提取",
-    "columns[].name_en": "字段英文名称,保持原始大小写",
-    "columns[].data_type": "数据类型,包含长度信息(如VARCHAR(22))",
-    "columns[].is_primary": "是否主键,取值:是/否",
-    "columns[].comment": "完整的注释内容",
-    "columns[].nullable": "是否可为空,取值:是/否(根据NOT NULL约束判断)"
-  },
-  
-  "修改文件": [
-    "app/core/llm/ddl_parser.py - parse_ddl() 方法",
-    "app/core/llm/ddl_parser.py - _optimize_ddl_prompt() 方法"
-  ]
-}
-

+ 0 - 148
docs/archive/DDLparse格式.txt

@@ -1,148 +0,0 @@
-{
-  "code": 200,
-  "data": {
-    "table_info": {
-      "name_zh": "科室对照表",
-      "name_en": "TB_JC_KSDZB"
-    },
-    "columns": [
-      {
-        "name_zh": "医疗机构代码",
-        "name_en": "YLJGDM",
-        "data_type": "VARCHAR(22)",
-        "is_primary": "是",
-        "comment": "医疗机构代码,复合主键",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室代码",
-        "name_en": "HISKSDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "是",
-        "comment": "HIS科室代码,主键、唯一",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室名称",
-        "name_en": "HISKSMC",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "HIS科室名称",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "病案科室代码",
-        "name_en": "BAKSDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "病案科室代码,应与\"病案明细表\"里的科室ID对应",
-        "nullable": "是"
-      },
-      {
-        "name_zh": "病案科室名称",
-        "name_en": "BAKSMC",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "病案科室名称",
-        "nullable": "是"
-      },
-      {
-        "name_zh": "成本中心代码",
-        "name_en": "CBZXDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "成本中心代码,与HIS科室对应",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "成本中心名称",
-        "name_en": "CBZXMC",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "成本中心名称",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "核算单元代码",
-        "name_en": "HSDYDM",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "核算单元代码,字符类型避免丢失前导0",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "核算单元名称",
-        "name_en": "HSDYMC",
-        "data_type": "CHAR(20)",
-        "is_primary": "否",
-        "comment": "核算单元名称",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室类型",
-        "name_en": "HISKSLX",
-        "data_type": "CHAR(10)",
-        "is_primary": "否",
-        "comment": "HIS科室类型,可取值:临床/医技/其它",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室内外科标识",
-        "name_en": "HISKSNWKBS",
-        "data_type": "CHAR(10)",
-        "is_primary": "否",
-        "comment": "HIS科室内外科标识,可取值:内科/外科",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "HIS科室病区标识",
-        "name_en": "HISKSBQBS",
-        "data_type": "CHAR(10)",
-        "is_primary": "否",
-        "comment": "HIS科室病区标识,可取值:病区/非病区",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "停用标识",
-        "name_en": "TYBS",
-        "data_type": "INTEGER",
-        "is_primary": "否",
-        "comment": "停用标识,科室是否停用标识:0-正在使用;1-已停用",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "数据上传时间",
-        "name_en": "TBRQ",
-        "data_type": "TIMESTAMP",
-        "is_primary": "否",
-        "comment": "数据上传时间",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "修改标志",
-        "name_en": "XGBZ",
-        "data_type": "CHAR(1)",
-        "is_primary": "否",
-        "comment": "修改标志:0-正常;1-撤销",
-        "nullable": "否"
-      },
-      {
-        "name_zh": "预留字段一",
-        "name_en": "YLYL1",
-        "data_type": "VARCHAR(128)",
-        "is_primary": "否",
-        "comment": "预留字段一",
-        "nullable": "是"
-      },
-      {
-        "name_zh": "预留字段二",
-        "name_en": "YLYL2",
-        "data_type": "VARCHAR(128)",
-        "is_primary": "否",
-        "comment": "预留字段二",
-        "nullable": "是"
-      }
-    ]
-  },
-  "message": "操作成功"
-}

+ 0 - 380
docs/archive/DataFlow_get_dataflow_by_id优化说明.md

@@ -1,380 +0,0 @@
-# DataFlow get_dataflow_by_id 函数优化说明
-
-## 优化概述
-
-简化 `get_dataflow_by_id` 函数,移除了对 PostgreSQL 数据库的查询,只从 Neo4j 图数据库中获取 DataFlow 节点的属性信息。
-
-## 修改的文件
-
-**文件路径**: `app/core/data_flow/dataflows.py`
-
-**函数**: `get_dataflow_by_id`
-
-**行数**: 第 96-148 行
-
-## 主要改动
-
-### 优化前
-
-```python
-def get_dataflow_by_id(dataflow_id: int) -> Optional[Dict[str, Any]]:
-    # 1. 从Neo4j获取基本信息
-    # 2. 从PostgreSQL获取额外信息(script_requirement, script_content等)
-    # 3. 合并两个数据源的信息
-    return dataflow
-```
-
-**问题**:
-- 需要查询两个数据库(Neo4j + PostgreSQL)
-- 数据冗余,信息分散在两处
-- 查询逻辑复杂,性能较低
-
-### 优化后
-
-```python
-def get_dataflow_by_id(dataflow_id: int) -> Optional[Dict[str, Any]]:
-    # 仅从Neo4j获取DataFlow节点的所有属性
-    # 将script_requirement从JSON字符串解析为对象
-    return dataflow
-```
-
-**优势**:
-- ✅ 单一数据源,逻辑清晰
-- ✅ 性能提升,只查询一次
-- ✅ 代码简洁,易于维护
-- ✅ 所有信息已在Neo4j节点中
-
-## 详细实现
-
-### 1. 简化 Cypher 查询
-
-**优化前**:
-```cypher
-MATCH (n:DataFlow)
-WHERE id(n) = $dataflow_id
-OPTIONAL MATCH (n)-[:LABEL]-(la:DataLabel)
-RETURN n, id(n) as node_id,
-       collect(DISTINCT {id: id(la), name: la.name}) as tags
-```
-
-**优化后**:
-```cypher
-MATCH (n:DataFlow)
-WHERE id(n) = $dataflow_id
-RETURN n, id(n) as node_id
-```
-
-**说明**: 移除了对 DataLabel 标签的查询,只获取 DataFlow 节点本身。
-
-### 2. 移除 PostgreSQL 查询
-
-**完全移除**了以下代码段:
-
-```python
-# 从PostgreSQL获取额外信息
-pg_query = """
-SELECT 
-    source_table,
-    target_table,
-    script_name,
-    script_type,
-    script_requirement,
-    script_content,
-    user_name,
-    create_time,
-    update_time,
-    target_dt_column
-FROM dags.data_transform_scripts
-WHERE script_name = :script_name
-"""
-
-with db.engine.connect() as conn:
-    pg_result = conn.execute(text(pg_query), {"script_name": dataflow.get('name_zh')}).fetchone()
-    # ... 处理结果
-```
-
-**原因**: 所有需要的信息都已经保存在 Neo4j 的 DataFlow 节点中。
-
-### 3. 添加 script_requirement 解析
-
-```python
-# 处理 script_requirement:如果是JSON字符串,解析为对象
-script_requirement_str = dataflow.get('script_requirement', '')
-if script_requirement_str:
-    try:
-        # 尝试解析JSON字符串
-        script_requirement_obj = json.loads(script_requirement_str)
-        dataflow['script_requirement'] = script_requirement_obj
-        logger.debug(f"成功解析script_requirement: {script_requirement_obj}")
-    except (json.JSONDecodeError, TypeError) as e:
-        logger.warning(f"script_requirement解析失败,保持原值: {e}")
-        # 保持原值(字符串)
-        dataflow['script_requirement'] = script_requirement_str
-else:
-    # 如果为空,设置为None
-    dataflow['script_requirement'] = None
-```
-
-**功能**:
-- 将 Neo4j 中存储的 JSON 字符串转换为 Python 对象
-- 错误处理:解析失败时保持原字符串
-- 空值处理:空字符串转换为 None
-
-## 返回数据格式
-
-### API 响应示例
-
-```json
-{
-  "code": 200,
-  "data": {
-    "id": 123,
-    "name_zh": "科室对照表映射到数据模型",
-    "name_en": "deparment_table_mapping",
-    "category": "应用类",
-    "leader": "system",
-    "organization": "citu",
-    "script_type": "python",
-    "update_mode": "append",
-    "frequency": "月",
-    "tag": null,
-    "describe": null,
-    "status": "active",
-    "script_requirement": {
-      "code": 28,
-      "rule": "rule",
-      "source_table": [2317, 2307],
-      "target_table": [164]
-    },
-    "created_at": "2024-11-28 10:00:00",
-    "updated_at": "2024-11-28 10:00:00"
-  },
-  "message": "操作成功"
-}
-```
-
-### 数据字段说明
-
-| 字段 | 类型 | 说明 | 来源 |
-|------|------|------|------|
-| id | Integer | 节点ID | Neo4j 内部ID |
-| name_zh | String | 中文名称 | Neo4j 节点属性 |
-| name_en | String | 英文名称 | Neo4j 节点属性 |
-| category | String | 分类 | Neo4j 节点属性 |
-| leader | String | 负责人 | Neo4j 节点属性 |
-| organization | String | 组织 | Neo4j 节点属性 |
-| script_type | String | 脚本类型 | Neo4j 节点属性 |
-| update_mode | String | 更新模式 | Neo4j 节点属性 |
-| frequency | String | 频率 | Neo4j 节点属性 |
-| tag | Any | 标签 | Neo4j 节点属性 |
-| describe | String | 描述 | Neo4j 节点属性 |
-| status | String | 状态 | Neo4j 节点属性 |
-| script_requirement | Object | 脚本需求 | Neo4j 节点属性(解析后) |
-| created_at | String | 创建时间 | Neo4j 节点属性 |
-| updated_at | String | 更新时间 | Neo4j 节点属性 |
-
-### script_requirement 对象结构
-
-```json
-{
-  "code": 28,
-  "rule": "rule",
-  "source_table": [2317, 2307],
-  "target_table": [164]
-}
-```
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| code | Number | 代码标识 |
-| rule | String | 规则名称 |
-| source_table | Array | 源表节点ID数组 |
-| target_table | Array | 目标表节点ID数组 |
-
-## 查询流程
-
-```
-请求 dataflow_id
-        ↓
-从 Neo4j 查询 DataFlow 节点
-        ↓
-获取节点所有属性
-        ↓
-解析 script_requirement
-(JSON字符串 → 对象)
-        ↓
-返回完整数据
-```
-
-## 使用示例
-
-### Python 调用
-
-```python
-from app.core.data_flow.dataflows import DataFlowService
-
-# 获取数据流详情
-dataflow_id = 123
-result = DataFlowService.get_dataflow_by_id(dataflow_id)
-
-if result:
-    print(f"数据流名称: {result['name_zh']}")
-    print(f"状态: {result['status']}")
-    
-    # 访问 script_requirement
-    if result.get('script_requirement'):
-        req = result['script_requirement']
-        print(f"源表: {req.get('source_table')}")
-        print(f"目标表: {req.get('target_table')}")
-else:
-    print("数据流不存在")
-```
-
-### API 调用
-
-```bash
-# 获取数据流详情
-curl -X GET http://localhost:5000/api/dataflow/get-dataflow/123
-```
-
-**响应**:
-```json
-{
-  "code": 200,
-  "data": {
-    "id": 123,
-    "name_zh": "科室对照表映射到数据模型",
-    "script_requirement": {
-      "code": 28,
-      "rule": "rule",
-      "source_table": [2317, 2307],
-      "target_table": [164]
-    },
-    ...
-  },
-  "message": "success"
-}
-```
-
-## 性能对比
-
-### 优化前
-
-1. 查询 Neo4j(包含标签关系)
-2. 查询 PostgreSQL
-3. 合并数据
-4. 返回结果
-
-**耗时**: 约 50-100ms(两次数据库查询)
-
-### 优化后
-
-1. 查询 Neo4j
-2. 解析 JSON
-3. 返回结果
-
-**耗时**: 约 20-30ms(单次数据库查询)
-
-**性能提升**: ~50-70%
-
-## 错误处理
-
-### 场景 1: 节点不存在
-
-```python
-result = DataFlowService.get_dataflow_by_id(999)
-# result = None
-```
-
-**日志**: `WARNING: 未找到ID为 999 的DataFlow节点`
-
-### 场景 2: script_requirement 解析失败
-
-```python
-# script_requirement 存储了无效的JSON
-# 返回原字符串,不中断流程
-```
-
-**日志**: `WARNING: script_requirement解析失败,保持原值: {error}`
-
-### 场景 3: script_requirement 为空
-
-```python
-# script_requirement 为空字符串或不存在
-# 返回 None
-dataflow['script_requirement'] = None
-```
-
-## 兼容性说明
-
-### 向后兼容
-
-✅ 返回的数据结构与优化前完全一致  
-✅ 只是移除了对 PostgreSQL 的依赖  
-✅ 前端无需任何修改  
-
-### 数据迁移
-
-如果已有数据仅存储在 PostgreSQL 中,需要先迁移到 Neo4j:
-
-```python
-# 数据迁移脚本示例
-def migrate_dataflow_to_neo4j():
-    # 1. 从 PostgreSQL 读取数据
-    # 2. 更新 Neo4j 节点属性
-    # 3. 验证数据完整性
-    pass
-```
-
-## 优势总结
-
-✅ **性能提升**: 减少一次数据库查询,性能提升 50-70%  
-✅ **代码简洁**: 移除了 PostgreSQL 查询逻辑,代码量减少约 40%  
-✅ **单一数据源**: 避免数据不一致问题  
-✅ **易于维护**: 逻辑清晰,便于后续扩展  
-✅ **JSON 解析**: 自动将 script_requirement 转换为对象  
-✅ **错误容忍**: 完善的错误处理机制  
-
-## 测试建议
-
-### 测试用例 1: 正常查询
-
-```python
-def test_get_dataflow_by_id_normal():
-    result = DataFlowService.get_dataflow_by_id(123)
-    assert result is not None
-    assert result['id'] == 123
-    assert 'name_zh' in result
-    assert isinstance(result['script_requirement'], dict)
-```
-
-### 测试用例 2: 节点不存在
-
-```python
-def test_get_dataflow_by_id_not_found():
-    result = DataFlowService.get_dataflow_by_id(999999)
-    assert result is None
-```
-
-### 测试用例 3: script_requirement 解析
-
-```python
-def test_script_requirement_parsing():
-    result = DataFlowService.get_dataflow_by_id(123)
-    req = result['script_requirement']
-    assert 'code' in req
-    assert 'rule' in req
-    assert isinstance(req['source_table'], list)
-```
-
-## 相关接口
-
-- `GET /api/dataflow/get-dataflow/<id>` - 获取数据流详情(使用此函数)
-- `GET /api/dataflow/get-dataflows-list` - 获取数据流列表
-- `POST /api/dataflow/add-dataflow` - 创建数据流
-- `PUT /api/dataflow/update-dataflow/<id>` - 更新数据流
-
-## 更新历史
-
-- **2024-11-28**: 简化函数,移除 PostgreSQL 查询,只从 Neo4j 获取数据
-

+ 0 - 298
docs/archive/DataFlow_rule提取优化说明.md

@@ -1,298 +0,0 @@
-# DataFlow _save_to_pg_database 函数优化说明
-
-## 优化概述
-
-在 `_save_to_pg_database` 函数中添加了从 `script_requirement` 中提取 `rule` 字段并保存到 `script_content` 的功能。
-
-## 修改的文件
-
-**文件路径**: `app/core/data_flow/dataflows.py`
-
-**函数**: `_save_to_pg_database`
-
-## script_requirement 数据格式
-
-```json
-{
-  "code": 28,
-  "rule": "rule",
-  "source_table": [2317, 2307],
-  "target_table": [164]
-}
-```
-
-## 优化逻辑
-
-### 1. 提取 rule 字段
-
-从 `script_requirement` 中提取 `rule` 字段的值:
-
-```python
-# 如果 script_requirement 是字典,直接提取 rule
-if isinstance(script_requirement_raw, dict):
-    rule_from_requirement = script_requirement_raw.get('rule', '')
-
-# 如果 script_requirement 是字符串,先解析再提取 rule
-elif isinstance(script_requirement_raw, str):
-    try:
-        parsed_req = json.loads(script_requirement)
-        if isinstance(parsed_req, dict):
-            rule_from_requirement = parsed_req.get('rule', '')
-    except (json.JSONDecodeError, TypeError):
-        pass
-```
-
-### 2. 保存到 script_content
-
-**优先级规则**:
-1. **优先使用前端传入的 `script_content`**
-2. **如果 `script_content` 为空,则使用从 `script_requirement` 提取的 `rule`**
-
-```python
-# 处理 script_content:优先使用前端传入的值,如果为空则使用从 script_requirement 提取的 rule
-script_content = data.get('script_content', '')
-if not script_content and rule_from_requirement:
-    script_content = rule_from_requirement
-    logger.info(f"script_content为空,使用从script_requirement提取的rule: {rule_from_requirement}")
-```
-
-## 修改详情
-
-**位置**: 第 297-327 行
-
-**修改前**:
-```python
-script_requirement_raw = data.get('script_requirement', None)
-if script_requirement_raw is not None:
-    if isinstance(script_requirement_raw, (dict, list)):
-        script_requirement = json.dumps(script_requirement_raw, ensure_ascii=False)
-    else:
-        script_requirement = str(script_requirement_raw)
-else:
-    script_requirement = ''
-
-script_content = data.get('script_content', '')
-```
-
-**修改后**:
-```python
-script_requirement_raw = data.get('script_requirement', None)
-rule_from_requirement = ''  # 用于保存从 script_requirement 中提取的 rule
-
-if script_requirement_raw is not None:
-    # 如果是字典,提取 rule 字段
-    if isinstance(script_requirement_raw, dict):
-        rule_from_requirement = script_requirement_raw.get('rule', '')
-        script_requirement = json.dumps(script_requirement_raw, ensure_ascii=False)
-    elif isinstance(script_requirement_raw, list):
-        script_requirement = json.dumps(script_requirement_raw, ensure_ascii=False)
-    else:
-        # 如果已经是字符串,尝试解析以提取 rule
-        script_requirement = str(script_requirement_raw)
-        try:
-            parsed_req = json.loads(script_requirement)
-            if isinstance(parsed_req, dict):
-                rule_from_requirement = parsed_req.get('rule', '')
-        except (json.JSONDecodeError, TypeError):
-            pass
-else:
-    script_requirement = ''
-
-# 处理 script_content:优先使用前端传入的值,如果为空则使用从 script_requirement 提取的 rule
-script_content = data.get('script_content', '')
-if not script_content and rule_from_requirement:
-    script_content = rule_from_requirement
-    logger.info(f"script_content为空,使用从script_requirement提取的rule: {rule_from_requirement}")
-```
-
-## 使用场景
-
-### 场景 1: 前端提供 script_content
-
-```json
-{
-  "name_zh": "数据流1",
-  "script_content": "自定义脚本内容",
-  "script_requirement": {
-    "code": 28,
-    "rule": "mapping_rule",
-    "source_table": [2317, 2307],
-    "target_table": [164]
-  }
-}
-```
-
-**结果**: 
-- `script_content` = `"自定义脚本内容"` ✅
-- 使用前端提供的值,不使用 rule
-
-### 场景 2: 前端未提供 script_content
-
-```json
-{
-  "name_zh": "数据流2",
-  "script_requirement": {
-    "code": 28,
-    "rule": "mapping_rule",
-    "source_table": [2317, 2307],
-    "target_table": [164]
-  }
-}
-```
-
-**结果**: 
-- `script_content` = `"mapping_rule"` ✅
-- 自动从 script_requirement 提取 rule
-
-### 场景 3: script_content 和 rule 都为空
-
-```json
-{
-  "name_zh": "数据流3",
-  "script_requirement": {
-    "code": 28,
-    "source_table": [2317, 2307],
-    "target_table": [164]
-  }
-}
-```
-
-**结果**: 
-- `script_content` = `""` 
-- 保持为空字符串
-
-### 场景 4: script_requirement 为字符串格式
-
-```json
-{
-  "name_zh": "数据流4",
-  "script_requirement": "{\"code\": 28, \"rule\": \"string_rule\", \"source_table\": [100], \"target_table\": [200]}"
-}
-```
-
-**结果**: 
-- 先解析 JSON 字符串
-- 提取 rule = `"string_rule"`
-- `script_content` = `"string_rule"` ✅
-
-## 数据流向
-
-```
-前端数据
-    │
-    ├─→ script_content (如果有值)
-    │       │
-    │       └─→ 直接使用 ✅
-    │
-    └─→ script_requirement
-            │
-            ├─→ 解析 JSON
-            │
-            ├─→ 提取 rule 字段
-            │
-            └─→ 如果 script_content 为空
-                    │
-                    └─→ 使用 rule 作为 script_content ✅
-```
-
-## 保存到数据库
-
-### PostgreSQL
-
-**表**: `dags.data_transform_scripts`
-
-| 字段 | 值来源 | 说明 |
-|------|--------|------|
-| script_requirement | script_requirement (JSON字符串) | 完整的需求配置 |
-| script_content | script_content 或 rule | 优先使用前端值,否则使用rule |
-
-**示例数据**:
-
-```sql
-INSERT INTO dags.data_transform_scripts 
-(script_name, script_requirement, script_content)
-VALUES 
-('数据流示例', 
- '{"code": 28, "rule": "mapping_rule", "source_table": [2317], "target_table": [164]}',
- 'mapping_rule');
-```
-
-## 日志输出
-
-当使用 rule 填充 script_content 时,会记录日志:
-
-```
-INFO: script_content为空,使用从script_requirement提取的rule: mapping_rule
-```
-
-## 优势
-
-✅ **自动填充**: 前端不需要同时传递 `script_content` 和 `rule`  
-✅ **向后兼容**: 如果前端提供 `script_content`,优先使用前端值  
-✅ **灵活处理**: 支持 `script_requirement` 为字典或字符串格式  
-✅ **错误容忍**: JSON 解析失败不会中断流程  
-✅ **日志追踪**: 记录何时使用 rule 填充 script_content  
-
-## 注意事项
-
-1. **优先级**: 前端的 `script_content` 优先级最高,不会被 rule 覆盖
-2. **空值检查**: 只有当 `script_content` 为空或不存在时,才使用 rule
-3. **类型安全**: 支持 `script_requirement` 为字典、字符串或 None
-4. **错误处理**: JSON 解析失败会被捕获,不影响主流程
-
-## 测试用例
-
-### 测试 1: 有 script_content,有 rule
-
-**输入**:
-```json
-{
-  "script_content": "custom_script",
-  "script_requirement": {"code": 1, "rule": "auto_rule"}
-}
-```
-
-**预期**: `script_content = "custom_script"`
-
-### 测试 2: 无 script_content,有 rule
-
-**输入**:
-```json
-{
-  "script_requirement": {"code": 1, "rule": "auto_rule"}
-}
-```
-
-**预期**: `script_content = "auto_rule"`
-
-### 测试 3: 无 script_content,无 rule
-
-**输入**:
-```json
-{
-  "script_requirement": {"code": 1}
-}
-```
-
-**预期**: `script_content = ""`
-
-### 测试 4: script_requirement 为字符串
-
-**输入**:
-```json
-{
-  "script_requirement": "{\"code\": 1, \"rule\": \"json_rule\"}"
-}
-```
-
-**预期**: `script_content = "json_rule"`
-
-## 相关接口
-
-- `POST /api/dataflow/add-dataflow` - 创建数据流(应用此优化)
-- `PUT /api/dataflow/update-dataflow/<id>` - 更新数据流
-
-## 更新历史
-
-- **2024-11-28**: 添加从 script_requirement 提取 rule 并填充 script_content 的功能
-

+ 0 - 266
docs/archive/DataFlow_script_requirement优化说明.md

@@ -1,266 +0,0 @@
-# DataFlow create_dataflow 函数优化说明
-
-## 优化概述
-
-在 `create_dataflow` 函数中新增了对 `script_requirement` 属性的处理,将其作为 JSON 字符串保存到 Neo4j 的 DataFlow 节点中,同时也保存到 PostgreSQL 数据库中。
-
-## 修改的文件
-
-**文件路径**: `app/core/data_flow/dataflows.py`
-
-## 前端数据格式
-
-前端上传的数据流配置数据格式示例:
-
-```json
-{
-  "name_zh": "科室对照表映射到数据模型",
-  "name_en": "deparment_table_mapping",
-  "category": "应用类",
-  "leader": "system",
-  "organization": "citu",
-  "script_type": "python",
-  "update_mode": "append",
-  "frequency": "月",
-  "tag": null,
-  "describe": null,
-  "status": "active",
-  "script_requirement": {
-    "code": 28,
-    "rule": "rule",
-    "source_table": [
-      2317,
-      2307
-    ],
-    "target_table": [
-      164
-    ]
-  }
-}
-```
-
-## 具体修改
-
-### 1. 在 `create_dataflow` 函数中添加 `script_requirement` 处理
-
-**位置**: 第 197-221 行
-
-**修改内容**:
-
-```python
-# 处理 script_requirement,将其转换为 JSON 字符串
-script_requirement = data.get('script_requirement', None)
-if script_requirement is not None:
-    # 如果是字典或列表,转换为 JSON 字符串
-    if isinstance(script_requirement, (dict, list)):
-        script_requirement_str = json.dumps(script_requirement, ensure_ascii=False)
-    else:
-        # 如果已经是字符串,直接使用
-        script_requirement_str = str(script_requirement)
-else:
-    script_requirement_str = ''
-
-# 准备节点数据
-node_data = {
-    'name_zh': dataflow_name,
-    'name_en': name_en,
-    'category': data.get('category', ''),
-    'organization': data.get('organization', ''),
-    'leader': data.get('leader', ''),
-    'frequency': data.get('frequency', ''),
-    'tag': data.get('tag', ''),
-    'describe': data.get('describe', ''),
-    'status': data.get('status', 'inactive'),
-    'update_mode': data.get('update_mode', 'append'),
-    'script_requirement': script_requirement_str,  # 新增的字段
-    'created_at': get_formatted_time(),
-    'updated_at': get_formatted_time()
-}
-```
-
-**功能说明**:
-1. 从前端数据中获取 `script_requirement`
-2. 如果是字典或列表类型,使用 `json.dumps()` 转换为 JSON 字符串
-3. 如果已经是字符串,直接使用
-4. 如果不存在,设置为空字符串
-5. 将 JSON 字符串保存到 Neo4j 节点的 `script_requirement` 属性中
-
-### 2. 在 `_save_to_pg_database` 函数中添加 `script_requirement` 处理
-
-**位置**: 第 297-310 行
-
-**修改内容**:
-
-```python
-# 提取脚本相关信息
-# 处理 script_requirement,确保保存为 JSON 字符串
-script_requirement_raw = data.get('script_requirement', None)
-if script_requirement_raw is not None:
-    # 如果是字典或列表,转换为 JSON 字符串
-    if isinstance(script_requirement_raw, (dict, list)):
-        script_requirement = json.dumps(script_requirement_raw, ensure_ascii=False)
-    else:
-        # 如果已经是字符串,直接使用
-        script_requirement = str(script_requirement_raw)
-else:
-    script_requirement = ''
-
-script_content = data.get('script_content', '')
-```
-
-**功能说明**:
-1. 与 `create_dataflow` 函数中的处理逻辑一致
-2. 确保保存到 PostgreSQL 数据库的 `script_requirement` 字段也是 JSON 字符串格式
-3. 保证 Neo4j 和 PostgreSQL 中的数据格式一致
-
-## script_requirement 数据结构
-
-`script_requirement` 是一个 JSON 对象,包含以下字段:
-
-| 字段 | 类型 | 说明 | 示例 |
-|------|------|------|------|
-| code | Number | 代码标识 | 28 |
-| rule | String | 规则名称 | "rule" |
-| source_table | Array | 源表节点ID数组 | [2317, 2307] |
-| target_table | Array | 目标表节点ID数组 | [164] |
-
-**保存格式**:
-
-在数据库中,`script_requirement` 以 JSON 字符串形式保存:
-
-```json
-"{\"code\": 28, \"rule\": \"rule\", \"source_table\": [2317, 2307], \"target_table\": [164]}"
-```
-
-## 存储位置
-
-### 1. Neo4j 图数据库
-
-- **节点类型**: `DataFlow`
-- **属性名**: `script_requirement`
-- **数据类型**: String (JSON 格式)
-
-### 2. PostgreSQL 数据库
-
-- **表名**: `dags.data_transform_scripts`
-- **字段名**: `script_requirement`
-- **数据类型**: TEXT (JSON 格式)
-
-## 使用示例
-
-### 创建 DataFlow 时提供 script_requirement
-
-```python
-dataflow_data = {
-    "name_zh": "科室对照表映射到数据模型",
-    "name_en": "deparment_table_mapping",
-    "category": "应用类",
-    "leader": "system",
-    "organization": "citu",
-    "script_type": "python",
-    "update_mode": "append",
-    "frequency": "月",
-    "status": "active",
-    "describe": "将科室对照表数据映射到数据模型",
-    "script_requirement": {
-        "code": 28,
-        "rule": "mapping_rule",
-        "source_table": [2317, 2307],
-        "target_table": [164]
-    }
-}
-
-# 创建数据流
-result = DataFlowService.create_dataflow(dataflow_data)
-```
-
-### 从 Neo4j 查询包含 script_requirement 的 DataFlow
-
-```cypher
-MATCH (df:DataFlow {name_zh: "科室对照表映射到数据模型"})
-RETURN df.script_requirement
-```
-
-### 解析 script_requirement
-
-```python
-import json
-
-# 从数据库获取 script_requirement 字符串
-script_requirement_str = dataflow_node['script_requirement']
-
-# 解析 JSON 字符串
-if script_requirement_str:
-    script_requirement = json.loads(script_requirement_str)
-    
-    code = script_requirement.get('code')
-    rule = script_requirement.get('rule')
-    source_tables = script_requirement.get('source_table', [])
-    target_tables = script_requirement.get('target_table', [])
-    
-    print(f"Code: {code}")
-    print(f"Rule: {rule}")
-    print(f"Source tables: {source_tables}")
-    print(f"Target tables: {target_tables}")
-```
-
-## 兼容性说明
-
-1. **向后兼容**: 如果前端不提供 `script_requirement`,默认保存为空字符串,不影响现有功能
-2. **格式灵活**: 支持前端传递字典、列表或字符串格式,都会正确转换为 JSON 字符串
-3. **数据一致性**: Neo4j 和 PostgreSQL 中的 `script_requirement` 格式保持一致
-
-## 注意事项
-
-1. **JSON 格式**: `script_requirement` 在数据库中以 JSON 字符串形式保存,使用时需要解析
-2. **编码问题**: 使用 `ensure_ascii=False` 确保中文字符正确保存
-3. **空值处理**: 如果 `script_requirement` 为 `None` 或不存在,保存为空字符串 `''`
-4. **类型检查**: 代码会检查 `script_requirement` 的类型,确保正确转换
-
-## 测试建议
-
-### 测试用例 1: 完整的 script_requirement
-
-```json
-{
-  "name_zh": "测试数据流1",
-  "describe": "测试描述",
-  "script_requirement": {
-    "code": 1,
-    "rule": "test_rule",
-    "source_table": [100, 101],
-    "target_table": [200]
-  }
-}
-```
-
-### 测试用例 2: 空的 script_requirement
-
-```json
-{
-  "name_zh": "测试数据流2",
-  "describe": "测试描述",
-  "script_requirement": null
-}
-```
-
-### 测试用例 3: script_requirement 为字符串
-
-```json
-{
-  "name_zh": "测试数据流3",
-  "describe": "测试描述",
-  "script_requirement": "{\"code\": 3, \"rule\": \"string_rule\"}"
-}
-```
-
-## 相关接口
-
-- `POST /api/dataflow/add-dataflow` - 创建数据流
-- `GET /api/dataflow/get-dataflow/<id>` - 获取数据流详情(包含 script_requirement)
-- `PUT /api/dataflow/update-dataflow/<id>` - 更新数据流(可更新 script_requirement)
-
-## 更新历史
-
-- **2024-11-28**: 初始版本,添加 `script_requirement` 字段支持
-

+ 0 - 501
docs/archive/DataFlow_task_list优化说明.md

@@ -1,501 +0,0 @@
-# DataFlow task_list 写入优化说明
-
-## 优化概述
-
-优化了 `create_dataflow` 函数中写入 `task_list` 表的代码操作,根据 `script_requirement` 的内容智能生成详细的任务描述,包括源表和目标表的 DDL、数据源信息、更新模式等。
-
-## 修改的文件
-
-**文件路径**: `app/core/data_flow/dataflows.py`
-
-## 优化要求实现
-
-### ✅ 1. 从 script_requirement 中提取 rule 字段作为 request_content_str
-
-```python
-# 1. 从script_requirement中提取rule字段作为request_content_str
-request_content_str = req_json.get('rule', '')
-```
-
-**功能**: 将 `rule` 字段的值提取出来,作为任务的核心需求内容。
-
-### ✅ 2. 从 script_requirement 提取 source_table 和 target_table,生成 DDL
-
-```python
-# 2. 从script_requirement中提取source_table和target_table字段信息
-source_table_ids = req_json.get('source_table', [])
-target_table_ids = req_json.get('target_table', [])
-
-# 确保是列表格式
-if not isinstance(source_table_ids, list):
-    source_table_ids = [source_table_ids] if source_table_ids else []
-if not isinstance(target_table_ids, list):
-    target_table_ids = [target_table_ids] if target_table_ids else []
-
-# 处理source tables
-for bd_id in source_table_ids:
-    ddl_info = DataFlowService._generate_businessdomain_ddl(
-        session, bd_id, is_target=False
-    )
-    if ddl_info:
-        source_ddls.append(ddl_info['ddl'])
-
-# 处理target tables
-for bd_id in target_table_ids:
-    ddl_info = DataFlowService._generate_businessdomain_ddl(
-        session, bd_id, is_target=True, update_mode=update_mode
-    )
-    if ddl_info:
-        target_ddls.append(ddl_info['ddl'])
-```
-
-**功能**: 
-- 根据节点 ID 列表查询 BusinessDomain 节点
-- 通过 INCLUDES 关系获取 DataMeta 元数据
-- 生成完整的 CREATE TABLE DDL 语句
-
-### ✅ 3. 如果 BELONGS_TO 关系连接"数据资源",获取数据源信息
-
-```python
-# 查询时包含BELONGS_TO和COME_FROM关系
-cypher = """
-MATCH (bd:BusinessDomain)
-WHERE id(bd) = $bd_id
-OPTIONAL MATCH (bd)-[:INCLUDES]->(m:DataMeta)
-OPTIONAL MATCH (bd)-[:BELONGS_TO]->(label:DataLabel)
-OPTIONAL MATCH (bd)-[:COME_FROM]->(ds:DataSource)
-RETURN bd, 
-       collect(DISTINCT m) as metadata,
-       label.name_zh as label_name,
-       ds.type as ds_type,
-       ds.host as ds_host,
-       ds.port as ds_port,
-       ds.database as ds_database
-"""
-
-# 如果标签是"数据资源"且有数据源,提取信息
-if label_name == '数据资源' and result['ds_type']:
-    data_source = {
-        'type': result['ds_type'],
-        'host': result['ds_host'],
-        'port': result['ds_port'],
-        'database': result['ds_database']
-    }
-```
-
-**功能**: 
-- 检查 BELONGS_TO 关系指向的标签是否为"数据资源"
-- 如果是,通过 COME_FROM 关系获取数据源信息
-- 将数据源信息添加到任务描述中
-
-### ✅ 4. 从 data 参数提取 update_mode,决定更新方式
-
-```python
-# 4. 从data参数中提取update_mode
-update_mode = data.get('update_mode', 'append')
-
-# 在任务描述中说明更新模式
-if update_mode == 'append':
-    task_desc_parts.append("- **Mode**: Append (追加模式)")
-    task_desc_parts.append("- **Description**: 新数据将追加到目标表,不删除现有数据")
-else:
-    task_desc_parts.append("- **Mode**: Full Refresh (全量更新)")
-    task_desc_parts.append("- **Description**: 目标表将被清空后重新写入数据")
-```
-
-**功能**: 
-- 提取 `update_mode` 字段(`append` 或 `full`)
-- 在任务描述中明确说明数据写入方式
-
-### ✅ 5. 目标表缺省添加 create_time 字段
-
-```python
-# 5. 如果是目标表,添加create_time字段
-if is_target:
-    column_definitions.append("    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '数据创建时间'")
-```
-
-**功能**: 
-- 为所有目标表自动添加 `create_time` 字段
-- 数据类型为 `TIMESTAMP`
-- 默认值为当前时间戳
-- 用于记录数据写入的时间
-
-## 新增辅助方法
-
-### `_generate_businessdomain_ddl`
-
-**位置**: 第 1025-1115 行
-
-**签名**:
-```python
-@staticmethod
-def _generate_businessdomain_ddl(
-    session, 
-    bd_id: int, 
-    is_target: bool = False, 
-    update_mode: str = 'append'
-) -> Optional[Dict[str, Any]]
-```
-
-**参数**:
-- `session`: Neo4j session 对象
-- `bd_id`: BusinessDomain 节点 ID
-- `is_target`: 是否为目标表(目标表需添加 create_time)
-- `update_mode`: 更新模式(append/full)
-
-**返回**:
-```python
-{
-    'ddl': 'CREATE TABLE ...',
-    'table_name': 'TB_JC_KSDZB',
-    'data_source': {
-        'type': 'postgresql',
-        'host': '10.52.31.104',
-        'port': 5432,
-        'database': 'mydatabase'
-    }
-}
-```
-
-**查询逻辑**:
-```cypher
-MATCH (bd:BusinessDomain)
-WHERE id(bd) = $bd_id
-OPTIONAL MATCH (bd)-[:INCLUDES]->(m:DataMeta)
-OPTIONAL MATCH (bd)-[:BELONGS_TO]->(label:DataLabel)
-OPTIONAL MATCH (bd)-[:COME_FROM]->(ds:DataSource)
-RETURN bd, 
-       collect(DISTINCT m) as metadata,
-       label.name_zh as label_name,
-       ds.type, ds.host, ds.port, ds.database
-```
-
-## 生成的任务描述示例
-
-### script_requirement 输入
-
-```json
-{
-  "code": 28,
-  "rule": "将科室对照表的数据映射到数据模型中",
-  "source_table": [2317, 2307],
-  "target_table": [164]
-}
-```
-
-### 生成的任务描述(Markdown格式)
-
-```markdown
-# Task: 科室对照表映射到数据模型
-
-## Data Source
-- **Type**: postgresql
-- **Host**: 10.52.31.104
-- **Port**: 5432
-- **Database**: hospital_db
-
-## Source Tables (DDL)
-```sql
-CREATE TABLE TB_JC_KSDZB (
-    YLJGDM VARCHAR(22) COMMENT '医疗机构代码',
-    HISKSDM CHAR(20) COMMENT 'HIS科室代码',
-    HISKSMC CHAR(20) COMMENT 'HIS科室名称',
-    BAKSDM CHAR(20) COMMENT '病案科室代码',
-    BAKSMC CHAR(20) COMMENT '病案科室名称'
-);
-COMMENT ON TABLE TB_JC_KSDZB IS '科室对照表';
-```
-
-```sql
-CREATE TABLE TB_DEPT_INFO (
-    DEPT_ID INTEGER COMMENT '科室ID',
-    DEPT_NAME VARCHAR(100) COMMENT '科室名称',
-    DEPT_TYPE CHAR(10) COMMENT '科室类型'
-);
-COMMENT ON TABLE TB_DEPT_INFO IS '科室信息表';
-```
-
-## Target Tables (DDL)
-```sql
-CREATE TABLE DM_DEPARTMENT (
-    dept_code VARCHAR(50) COMMENT '科室代码',
-    dept_name VARCHAR(100) COMMENT '科室名称',
-    dept_category VARCHAR(50) COMMENT '科室分类',
-    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '数据创建时间'
-);
-COMMENT ON TABLE DM_DEPARTMENT IS '科室数据模型';
-```
-
-## Update Mode
-- **Mode**: Append (追加模式)
-- **Description**: 新数据将追加到目标表,不删除现有数据
-
-## Request Content
-将科室对照表的数据映射到数据模型中
-
-## Implementation Steps
-1. Extract data from source tables
-2. Apply transformation logic according to the rule
-3. Write data to target table using append mode
-4. Generate Python program to implement the logic
-5. Generate n8n workflow to schedule and execute the Python program
-```
-
-## 数据流程
-
-```
-前端上传 script_requirement
-        ↓
-提取 rule → request_content_str
-        ↓
-提取 source_table IDs → [2317, 2307]
-        ↓
-提取 target_table IDs → [164]
-        ↓
-查询 BusinessDomain 节点
-        ↓
-        ├─→ 获取 INCLUDES 关系 → DataMeta
-        ├─→ 获取 BELONGS_TO 关系 → DataLabel
-        └─→ 如果是"数据资源" → 获取 COME_FROM 关系 → DataSource
-        ↓
-生成源表 DDL(不含 create_time)
-        ↓
-生成目标表 DDL(含 create_time)
-        ↓
-构建 Markdown 任务描述
-        ↓
-写入 task_list 表
-```
-
-## DDL 生成规则
-
-### 源表 DDL
-
-```sql
-CREATE TABLE {table_name} (
-    {column_name} {data_type} COMMENT '{comment}',
-    ...
-);
-COMMENT ON TABLE {table_name} IS '{table_comment}';
-```
-
-### 目标表 DDL(额外添加 create_time)
-
-```sql
-CREATE TABLE {table_name} (
-    {column_name} {data_type} COMMENT '{comment}',
-    ...,
-    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '数据创建时间'
-);
-COMMENT ON TABLE {table_name} IS '{table_comment}';
-```
-
-## 字段说明
-
-### script_requirement 字段
-
-| 字段 | 类型 | 说明 | 示例 |
-|------|------|------|------|
-| code | Number | 代码标识 | 28 |
-| rule | String | 转换规则/需求描述 | "将科室对照表的数据映射到数据模型中" |
-| source_table | Array | 源表的 BusinessDomain 节点 ID 列表 | [2317, 2307] |
-| target_table | Array | 目标表的 BusinessDomain 节点 ID 列表 | [164] |
-
-### 生成的 data_source 信息
-
-| 字段 | 说明 |
-|------|------|
-| type | 数据库类型(postgresql/mysql等) |
-| host | 数据库主机地址 |
-| port | 数据库端口 |
-| database | 数据库名称 |
-
-## 优势
-
-✅ **智能提取**: 自动从 script_requirement 提取所有必要信息  
-✅ **完整 DDL**: 包含所有字段、类型、注释信息  
-✅ **数据源感知**: 自动识别并提取数据源信息  
-✅ **更新模式**: 明确说明追加或全量更新  
-✅ **时间戳记录**: 目标表自动添加 create_time 字段  
-✅ **结构清晰**: Markdown 格式,易于阅读  
-✅ **错误容忍**: 完善的异常处理,不影响主流程  
-
-## 测试示例
-
-### 输入数据
-
-```json
-{
-  "name_zh": "科室对照表映射到数据模型",
-  "name_en": "deparment_table_mapping",
-  "category": "应用类",
-  "leader": "system",
-  "organization": "citu",
-  "script_type": "python",
-  "update_mode": "append",
-  "frequency": "月",
-  "describe": "数据映射任务",
-  "status": "active",
-  "script_requirement": {
-    "code": 28,
-    "rule": "将科室对照表的数据映射到数据模型中,保留所有字段",
-    "source_table": [2317, 2307],
-    "target_table": [164]
-  }
-}
-```
-
-### 生成的任务描述
-
-保存到 `task_list.task_description` 字段,包含:
-
-1. ✅ **任务标题**: 数据流名称
-2. ✅ **数据源信息**: type, host, port, database
-3. ✅ **源表 DDL**: 完整的建表语句(不含 create_time)
-4. ✅ **目标表 DDL**: 完整的建表语句(含 create_time)
-5. ✅ **更新模式**: append 或 full,附带说明
-6. ✅ **请求内容**: rule 字段的值
-7. ✅ **实施步骤**: 详细的执行步骤
-
-## 关键特性
-
-### 1. 智能 DDL 生成
-
-**源表示例**:
-```sql
-CREATE TABLE TB_JC_KSDZB (
-    YLJGDM VARCHAR(22) COMMENT '医疗机构代码',
-    HISKSDM CHAR(20) COMMENT 'HIS科室代码',
-    HISKSMC CHAR(20) COMMENT 'HIS科室名称'
-);
-COMMENT ON TABLE TB_JC_KSDZB IS '科室对照表';
-```
-
-**目标表示例**(自动添加 create_time):
-```sql
-CREATE TABLE DM_DEPARTMENT (
-    dept_code VARCHAR(50) COMMENT '科室代码',
-    dept_name VARCHAR(100) COMMENT '科室名称',
-    dept_category VARCHAR(50) COMMENT '科室分类',
-    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '数据创建时间'
-);
-COMMENT ON TABLE DM_DEPARTMENT IS '科室数据模型';
-```
-
-### 2. 数据源自动识别
-
-只有当 BusinessDomain 的 `BELONGS_TO` 关系指向"数据资源"标签时,才会查询并返回数据源信息:
-
-```
-BusinessDomain → BELONGS_TO → DataLabel("数据资源")
-       ↓
-   COME_FROM
-       ↓
-  DataSource (type, host, port, database)
-```
-
-### 3. 更新模式说明
-
-**追加模式 (append)**:
-```markdown
-## Update Mode
-- **Mode**: Append (追加模式)
-- **Description**: 新数据将追加到目标表,不删除现有数据
-```
-
-**全量更新 (full)**:
-```markdown
-## Update Mode
-- **Mode**: Full Refresh (全量更新)
-- **Description**: 目标表将被清空后重新写入数据
-```
-
-### 4. create_time 字段
-
-**目的**: 记录数据写入的时间戳
-
-**规则**:
-- ✅ 仅目标表添加此字段
-- ✅ 类型: `TIMESTAMP`
-- ✅ 默认值: `CURRENT_TIMESTAMP`
-- ✅ 注释: "数据创建时间"
-
-## 代码结构
-
-### 主函数优化(第 365-434 行)
-
-```python
-# 1. 提取 rule
-request_content_str = req_json.get('rule', '')
-
-# 2. 提取 source_table 和 target_table
-source_table_ids = req_json.get('source_table', [])
-target_table_ids = req_json.get('target_table', [])
-
-# 4. 提取 update_mode
-update_mode = data.get('update_mode', 'append')
-
-# 生成 DDL
-for bd_id in source_table_ids:
-    ddl_info = DataFlowService._generate_businessdomain_ddl(
-        session, bd_id, is_target=False
-    )
-
-for bd_id in target_table_ids:
-    ddl_info = DataFlowService._generate_businessdomain_ddl(
-        session, bd_id, is_target=True, update_mode=update_mode
-    )
-
-# 构建任务描述
-# ... 组装 Markdown 格式的任务描述
-```
-
-### 辅助方法(第 1025-1115 行)
-
-```python
-@staticmethod
-def _generate_businessdomain_ddl(
-    session, 
-    bd_id: int, 
-    is_target: bool = False, 
-    update_mode: str = 'append'
-) -> Optional[Dict[str, Any]]:
-    """
-    根据 BusinessDomain 节点 ID 生成 DDL
-    
-    功能:
-    - 查询 BusinessDomain 节点
-    - 获取 INCLUDES 关系的 DataMeta 元数据
-    - 检查 BELONGS_TO 关系(是否为"数据资源")
-    - 获取 COME_FROM 关系的 DataSource 信息
-    - 为目标表添加 create_time 字段
-    - 生成完整的 DDL 语句
-    """
-```
-
-## 验证结果
-
-- ✅ **Linter 检查**: 无错误
-- ✅ **Python 编译**: 通过
-- ✅ **代码结构**: 清晰,易维护
-- ✅ **错误处理**: 完善的异常捕获
-- ✅ **日志记录**: 详细的操作日志
-
-## 注意事项
-
-1. **节点 ID 验证**: 确保 source_table 和 target_table 中的 ID 是有效的 BusinessDomain 节点
-2. **数据源可选**: 只有标记为"数据资源"的 BusinessDomain 才会有数据源信息
-3. **create_time 字段**: 只添加到目标表,不添加到源表
-4. **错误不中断**: DDL 生成失败不会中断整个数据流创建过程
-5. **默认值**: 如果没有元数据,会添加默认的 id 主键列
-
-## 相关接口
-
-- `POST /api/dataflow/add-dataflow` - 创建数据流(使用此优化)
-- `GET /api/dataflow/get-BD-list` - 获取 BusinessDomain 列表
-
-## 更新历史
-
-- **2024-11-28**: 优化 task_list 写入逻辑,支持智能 DDL 生成和数据源识别
-

+ 0 - 300
docs/archive/DataFlow_实施步骤优化说明.md

@@ -1,300 +0,0 @@
-# DataFlow 实施步骤优化说明
-
-## 优化概述
-
-优化了 DataFlow 创建时 `task_list` 表中实施步骤的生成逻辑,根据任务类型(远程数据源导入 vs 数据转换)智能生成不同的实施步骤。
-
-## 优化内容
-
-### 1. 判断逻辑
-
-通过检查 `data_source_info` 是否存在来判断任务类型:
-
-- **有 `data_source_info`**: 远程数据源导入任务(BusinessDomain 的 BELONGS_TO 关系连接的是"数据资源")
-- **无 `data_source_info`**: 数据转换任务(从内部表到表的数据转换)
-
-### 2. 远程数据源导入任务(简化步骤)
-
-当检测到 `data_source_info` 存在时,生成的实施步骤为:
-
-```markdown
-## Implementation Steps
-1. Create an n8n workflow to execute the data import task
-2. Configure the workflow to call `import_resource_data.py` Python script
-3. Pass the following parameters to the Python execution node:
-   - `--source-config`: JSON configuration for the remote data source
-   - `--target-table`: Target table name (data resource English name)
-   - `--update-mode`: append/full
-4. The Python script will automatically:
-   - Connect to the remote data source
-   - Extract data from the source table
-   - Write data to target table using {update_mode} mode
-```
-
-**特点**:
-- 只需创建 n8n 工作流
-- 直接调用现成的 `import_resource_data.py` 工具
-- 明确列出需要传递的参数
-- 无需手动编写 Python 代码
-
-### 3. 数据转换任务(完整步骤)
-
-当 `data_source_info` 不存在时,生成的实施步骤为:
-
-```markdown
-## Implementation Steps
-1. Extract data from source tables as specified in the DDL
-2. Apply transformation logic according to the rule:
-   - Rule: {request_content_str}
-3. Generate Python program to implement the data transformation logic
-4. Write transformed data to target table using {update_mode} mode
-5. Create an n8n workflow to schedule and execute the Python program
-```
-
-**特点**:
-- 需要从 source tables 提取数据
-- 应用指定的计算规则(rule)
-- 需要生成自定义 Python 程序
-- 将转换后的数据写入 target table
-- 创建 n8n 工作流来调度执行
-
-## 代码位置
-
-**文件**: `app/core/data_flow/dataflows.py`
-
-**函数**: `DataFlowService._save_to_pg_database()`
-
-**代码段**: 第 464-484 行
-
-## 实现示例
-
-### 示例 1: 远程数据源导入任务
-
-**场景**: 从远程 PostgreSQL 数据库导入科室对照表
-
-**输入数据**:
-```json
-{
-  "name_zh": "科室对照表数据导入",
-  "name_en": "dept_data_import",
-  "script_requirement": {
-    "rule": "从远程数据源导入科室对照表数据",
-    "source_table": [2317],  // BusinessDomain ID (BELONGS_TO "数据资源")
-    "target_table": [164]
-  },
-  "update_mode": "append"
-}
-```
-
-**生成的任务描述**(包含数据源信息):
-```markdown
-# Task: dept_data_import
-
-## Data Source
-- **Type**: postgresql
-- **Host**: 10.52.31.104
-- **Port**: 5432
-- **Database**: hospital_db
-
-## Source Tables (DDL)
-```sql
-CREATE TABLE TB_JC_KSDZB (
-  YLJGDM VARCHAR(22) NOT NULL,
-  HISKSDM CHAR(20) NOT NULL,
-  ...
-);
-```
-
-## Target Tables (DDL)
-```sql
-CREATE TABLE departments (
-  yljgdm VARCHAR(22),
-  hisksdm CHAR(20),
-  ...
-  create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-);
-```
-
-## Update Mode
-- **Mode**: Append (追加模式)
-- **Description**: 新数据将追加到目标表,不删除现有数据
-
-## Request Content
-从远程数据源导入科室对照表数据
-
-## Implementation Steps
-1. Create an n8n workflow to execute the data import task
-2. Configure the workflow to call `import_resource_data.py` Python script
-3. Pass the following parameters to the Python execution node:
-   - `--source-config`: JSON configuration for the remote data source
-   - `--target-table`: Target table name (data resource English name)
-   - `--update-mode`: append
-4. The Python script will automatically:
-   - Connect to the remote data source
-   - Extract data from the source table
-   - Write data to target table using append mode
-```
-
-### 示例 2: 数据转换任务
-
-**场景**: 将科室对照表映射到数据模型
-
-**输入数据**:
-```json
-{
-  "name_zh": "科室对照表映射到数据模型",
-  "name_en": "dept_table_mapping",
-  "script_requirement": {
-    "rule": "将科室对照表中的 HIS 科室信息映射到标准化的数据模型结构",
-    "source_table": [2307, 2308],  // BusinessDomain IDs (BELONGS_TO "数据模型")
-    "target_table": [164]
-  },
-  "update_mode": "full"
-}
-```
-
-**生成的任务描述**(无数据源信息):
-```markdown
-# Task: dept_table_mapping
-
-## Source Tables (DDL)
-```sql
-CREATE TABLE TB_JC_KSDZB (
-  YLJGDM VARCHAR(22) NOT NULL,
-  HISKSDM CHAR(20) NOT NULL,
-  ...
-);
-
-CREATE TABLE TB_JC_KSXX (
-  KSDM CHAR(20) NOT NULL,
-  KSMC CHAR(50),
-  ...
-);
-```
-
-## Target Tables (DDL)
-```sql
-CREATE TABLE departments (
-  dept_code CHAR(20),
-  dept_name CHAR(50),
-  ...
-  create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-);
-```
-
-## Update Mode
-- **Mode**: Full Refresh (全量更新)
-- **Description**: 目标表将被清空后重新写入数据
-
-## Request Content
-将科室对照表中的 HIS 科室信息映射到标准化的数据模型结构
-
-## Implementation Steps
-1. Extract data from source tables as specified in the DDL
-2. Apply transformation logic according to the rule:
-   - Rule: 将科室对照表中的 HIS 科室信息映射到标准化的数据模型结构
-3. Generate Python program to implement the data transformation logic
-4. Write transformed data to target table using full mode
-5. Create an n8n workflow to schedule and execute the Python program
-```
-
-## 优化效果
-
-### 1. 任务描述更清晰
-- 不同类型的任务有不同的实施步骤
-- 远程导入任务不再要求生成复杂的转换逻辑
-
-### 2. 降低实施难度
-- 远程导入任务只需配置 n8n 工作流调用现有工具
-- 无需为简单的数据导入编写自定义 Python 代码
-
-### 3. 提高可执行性
-- 明确列出所需参数和工具
-- 步骤更具可操作性
-
-### 4. 区分任务类型
-- 系统能够自动识别任务类型
-- 为不同类型的任务提供针对性的指导
-
-## 技术细节
-
-### 判断条件
-
-```python
-# 判断是否为远程数据源导入任务
-if data_source_info:
-    # 从远程数据源导入数据的简化步骤
-    # ...
-else:
-    # 数据转换任务的完整步骤
-    # ...
-```
-
-### data_source_info 的来源
-
-`data_source_info` 是通过查询 Neo4j 图数据库获取的:
-
-1. 从 `script_requirement` 中提取 `source_table` ID 列表
-2. 查询 BusinessDomain 节点
-3. 检查 BusinessDomain 的 `BELONGS_TO` 关系
-4. 如果 `BELONGS_TO` 连接的是 "数据资源" DataLabel,则查询 `COME_FROM` 关系获取 DataSource 信息
-
-```python
-# 3. 如果BELONGS_TO关系连接的是"数据资源",获取数据源信息
-if ddl_info.get('data_source') and not data_source_info:
-    data_source_info = ddl_info['data_source']
-```
-
-## 相关文件
-
-- `app/core/data_flow/dataflows.py`: 实施步骤生成逻辑
-- `app/core/data_flow/import_resource_data.py`: 远程数据源导入工具
-- `docs/import_resource_data使用说明.md`: 数据导入工具使用文档
-
-## 测试建议
-
-### 测试用例 1: 远程数据源导入
-
-```json
-{
-  "name_zh": "测试远程导入",
-  "name_en": "test_remote_import",
-  "script_requirement": {
-    "rule": "测试从远程数据源导入",
-    "source_table": [<远程数据资源的BD_ID>],
-    "target_table": [<目标表的BD_ID>]
-  },
-  "update_mode": "append"
-}
-```
-
-**预期结果**: 生成简化的实施步骤,包含 `import_resource_data.py` 调用说明
-
-### 测试用例 2: 数据转换
-
-```json
-{
-  "name_zh": "测试数据转换",
-  "name_en": "test_transformation",
-  "script_requirement": {
-    "rule": "测试数据转换逻辑",
-    "source_table": [<内部表1的BD_ID>, <内部表2的BD_ID>],
-    "target_table": [<目标表的BD_ID>]
-  },
-  "update_mode": "full"
-}
-```
-
-**预期结果**: 生成完整的实施步骤,包含数据提取、转换、写入和 n8n 工作流创建
-
-## 版本信息
-
-- **创建时间**: 2025-11-29
-- **作者**: cursor
-- **版本**: 1.0
-
-
-
-
-
-

+ 0 - 454
docs/archive/N8N_WORKFLOW_SUMMARY.md

@@ -1,454 +0,0 @@
-# n8n 数据治理工作流创建总结
-
-## 🔄 最新更新 (2025-11-04 16:00)
-
-### 工作流功能增强完成
-
-**新增后端支持**:
-1. ✅ 创建了 `/api/meta/check` 接口用于检查元数据是否存在
-2. ✅ 接口地址:`http://192.168.3.143:5000/api/meta/check?name_zh=xxx`
-3. ✅ 返回 `exists: true/false` 表示是否存在
-
-**工作流改进**:
-1. ✅ 更新 AI Agent 系统消息,支持收集元数据信息流程:
-   - 询问用户提供:中文名、数据类型、描述
-   - 使用工具检查是否存在
-   - 不存在则创建
-2. ✅ 准备添加 HTTP Request Tools:
-   - `check_metadata`: 检查元数据工具
-   - `create_metadata`: 创建元数据工具
-
-**下一步操作**:
-- 📖 查看 `docs/n8n_add_tools_guide.md` 获取详细配置指南
-- 🛠️ 在 n8n 界面手动添加 Tool 节点(15-20分钟)
-- 🧪 测试完整的元数据创建流程
-
----
-
-## ✅ 工作流创建成功
-
-已成功在 n8n 中创建名为 **Data-governance** 的工作流!
-
-### 工作流信息
-
-- **工作流名称**: Data-governance
-- **工作流ID**: `tWfjLZE1FmMfQAIn`
-- **状态**: 未激活(需要手动激活)
-- **创建时间**: 2025-11-04 04:54:10
-- **更新时间**: 2025-11-04 04:54:25
-- **时区**: Asia/Shanghai
-- **触发方式**: Chat Trigger (聊天触发器)
-
-## 工作流功能
-
-### 核心功能
-
-这是一个智能的数据治理对话工作流,实现了以下功能:
-
-1. **主动询问**:通过初始消息询问用户是否需要进行元数据管理
-2. **智能判断**:根据用户的回答(是/否)执行不同的操作
-3. **API 集成**:确认后自动调用 DataOps 平台的元数据新增接口
-4. **友好反馈**:为用户提供清晰的操作结果反馈
-
-### 工作流程
-
-```
-开始 → 显示欢迎消息 → 判断用户回答
-                           ↓
-            是 ←──────────────┴────────────→ 否
-            ↓                                ↓
-    调用元数据新增API                    返回对话菜单
-            ↓
-      显示确认消息
-```
-
-## 节点组成
-
-工作流包含 **7个节点**:
-
-### 1. Chat Trigger (聊天触发器) ✨
-- **功能**: 启动工作流,提供聊天界面
-- **类型**: Chat Trigger (@n8n/n8n-nodes-langchain.chatTrigger)
-- **配置**: 
-  - 公开访问:是
-  - 模式:Hosted Chat(托管聊天)
-  - 标题:数据治理助手 🤖
-  - 欢迎消息:引导用户进行元数据管理
-- **说明**: 这是一个可激活的触发器节点,提供网页聊天界面
-
-### 2. AI Agent (AI 代理) 🤖
-- **功能**: 使用 AI 理解用户意图
-- **类型**: AI Agent (@n8n/n8n-nodes-langchain.agent)
-- **配置**:
-  - 提示类型:自动(从 Chat Trigger)
-  - 系统消息:判断用户是否需要元数据管理
-  - 输出规则:
-    - 确认 → CONFIRM_METADATA
-    - 拒绝 → REJECT_METADATA
-    - 不清楚 → 询问澄清
-- **说明**: 连接到 DeepSeek Chat Model 进行智能对话
-
-### 3. DeepSeek Chat Model (语言模型) ✨
-- **功能**: 提供 AI 对话能力
-- **类型**: DeepSeek Chat Model (@n8n/n8n-nodes-langchain.lmChatDeepSeek)
-- **配置**:
-  - 模型:deepseek-chat
-  - 温度:0.7
-  - 最大令牌:500
-- **说明**: 连接到 AI Agent,需要配置 DeepSeek API 凭证
-
-### 4. 判断用户意图 (条件判断)
-- **功能**: 判断 AI 的输出结果
-- **类型**: IF Node
-- **条件**: `output 包含 "CONFIRM_METADATA"`
-- **输出**: True 分支(确认)/ False 分支(拒绝)
-
-### 5. 调用元数据新增API (API 调用)
-- **功能**: 调用 DataOps 平台元数据新增接口
-- **类型**: HTTP Request
-- **方法**: POST
-- **URL**: `http://localhost:5000/api/meta/add`
-- **请求体**:
-  ```json
-  {
-    "name_zh": "新建元数据",
-    "data_type": "string",
-    "description": "通过工作流创建的元数据",
-    "source": "data-governance-workflow"
-  }
-  ```
-
-### 6. 设置确认消息 (确认响应)
-- **功能**: 返回成功确认消息
-- **类型**: Set Node
-- **消息**: "好的!已为您发起元数据新增工作流程..."
-- **触发**: 用户确认且 API 调用成功
-
-### 7. 设置拒绝消息 (拒绝响应)
-- **功能**: 返回对话菜单
-- **类型**: Set Node
-- **消息**: "好的,已取消元数据管理操作..."
-- **触发**: 用户拒绝元数据管理
-
-## 使用方法
-
-### 1. 配置 DeepSeek API 凭证
-
-在激活工作流之前,需要先配置 DeepSeek API 凭证:
-
-```bash
-1. 在 n8n 界面中,进入 Settings → Credentials
-2. 找到已创建的 DeepSeek API 凭证
-3. 回到工作流,选择 "DeepSeek Chat Model" 节点
-4. 在 Credentials 字段中选择 DeepSeek API 凭证
-5. 保存工作流
-```
-
-**注意**: 您已经添加了 DeepSeek 凭证,只需要在节点中选择即可。
-
-### 2. 激活工作流
-
-```bash
-# 在 n8n 界面中
-1. 打开 Workflows 页面
-2. 找到 "Data-governance" 工作流
-3. 点击右上角的 "Active" 开关激活工作流
-4. 工作流激活后,Chat Trigger 会生成一个聊天界面 URL
-```
-
-### 3. 访问聊天界面
-
-激活工作流后:
-
-```bash
-1. 点击 "Chat Trigger" 节点
-2. 复制显示的 Chat URL
-3. 在浏览器中打开该 URL
-4. 您将看到一个聊天界面,标题为 "数据治理助手 🤖"
-5. 开始与 AI 助手对话!
-```
-
-**聊天界面示例**:
-```
-数据治理助手 🤖
-帮助您进行元数据管理和数据治理
-
-─────────────────────────────
-
-您好!我是数据治理助手。
-
-我可以帮助您:
-- 进行元数据管理
-- 数据标准制定
-- 数据质量检查
-
-请问您需要进行元数据管理吗?(请回答:是 或 否)
-
-─────────────────────────────
-[  请输入您的需求...  ] [发送]
-```
-
-## 集成到 DataOps 平台
-
-### 方式 1: 嵌入聊天 Widget ✨ (推荐)
-
-在 DataOps 平台页面中嵌入 n8n Chat Widget:
-
-```html
-<!-- 在 DataOps 平台的 HTML 页面中添加 -->
-<iframe 
-  src="http://your-n8n-server/chat/tWfjLZE1FmMfQAIn"
-  width="400"
-  height="600"
-  frameborder="0"
-  style="border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"
-></iframe>
-```
-
-或使用 JavaScript 动态加载:
-
-```javascript
-// 在 DataOps 前端代码中
-const chatUrl = 'http://your-n8n-server/chat/tWfjLZE1FmMfQAIn';
-
-// 创建聊天窗口
-function openDataGovernanceChat() {
-  const chatWindow = window.open(
-    chatUrl,
-    'data-governance-chat',
-    'width=400,height=600,resizable=yes,scrollbars=yes'
-  );
-}
-
-// 或者使用 iframe
-function embedChatWidget() {
-  const iframe = document.createElement('iframe');
-  iframe.src = chatUrl;
-  iframe.style.cssText = 'width:400px;height:600px;border:none;border-radius:10px;';
-  document.getElementById('chat-container').appendChild(iframe);
-}
-```
-
-### 方式 2: 链接跳转
-
-在 DataOps 平台中添加一个按钮,点击后跳转到聊天界面:
-
-```html
-<a href="http://your-n8n-server/chat/tWfjLZE1FmMfQAIn" 
-   target="_blank" 
-   class="btn btn-primary">
-  🤖 数据治理助手
-</a>
-```
-
-## 配置要求
-
-### 必需配置
-
-| 配置项 | 值 | 说明 | 优先级 |
-|--------|-----|------|--------|
-| DeepSeek API Key | sk-xxx... | DeepSeek API 密钥 | ⭐⭐⭐ 必需 |
-| N8N_API_URL | https://n8n.citupro.com | n8n 服务器地址 | ⭐⭐ 重要 |
-| DATAOPS_API_URL | http://localhost:5000 | DataOps API 地址 | ⭐⭐ 重要 |
-
-### 依赖服务
-
-- ✅ n8n 服务正常运行(需要 v1.0+ 版本,支持 LangChain 节点)
-- ✅ DeepSeek API 可访问
-- ✅ DeepSeek 凭证已配置
-- ✅ DataOps 平台 API 可访问
-- ✅ 元数据新增接口 (`/api/meta/add`) 可用
-
-### DeepSeek API 配置
-
-**DeepSeek 的优势**:
-- ✅ 国内可直接访问,无需代理
-- ✅ 性能优秀,响应速度快
-- ✅ 价格更实惠
-- ✅ 支持中文对话
-
-**配置说明**:
-```bash
-模型:deepseek-chat
-温度:0.7(控制创造性)
-最大令牌:500(控制响应长度)
-```
-
-**替代方案**(如果需要):
-- Ollama Chat Model(本地部署,完全免费)
-- Groq Chat Model(快速,有免费额度)
-- Azure OpenAI Chat Model(企业版)
-
-## 扩展功能建议
-
-### 1. 添加 AI 智能理解 🤖
-
-集成 LLM 提升对话理解能力:
-
-```javascript
-// 添加 AI Agent 节点
-{
-  type: "@n8n/n8n-nodes-langchain.agent",
-  parameters: {
-    promptType: "define",
-    text: `你是数据治理助手。判断用户是否确认元数据管理。
-    
-    如果用户表示同意(是、好的、可以等),返回: CONFIRM
-    如果用户表示拒绝(否、不用、取消等),返回: REJECT
-    如果不确定,询问用户明确回答。`
-  }
-}
-```
-
-### 2. 支持多种数据治理操作 📊
-
-添加 Switch 节点,支持:
-- 元数据管理
-- 数据标准制定
-- 数据质量检查
-- 数据标签管理
-- 数据血缘追踪
-
-### 3. 添加会话管理 💬
-
-保存用户会话历史:
-- 使用 n8n 的 Memory 节点
-- 或集成 Redis 存储会话数据
-
-### 4. 实现多轮对话 🔄
-
-支持连续的对话交互:
-- 询问元数据详细信息
-- 收集必填字段
-- 确认后再提交
-
-### 5. 添加错误处理 ⚠️
-
-- API 调用失败重试
-- 用户输入验证
-- 异常情况提示
-
-## 监控和维护
-
-### 查看执行历史
-
-```bash
-# 在 n8n 界面中
-1. 打开 "Executions" 页面
-2. 查看工作流执行记录
-3. 分析成功率和失败原因
-```
-
-### 调试技巧
-
-1. **查看节点输出**
-   - 点击节点查看输入输出数据
-   - 使用 "Test workflow" 模式
-
-2. **检查日志**
-   - 查看 n8n 服务器日志
-   - 检查 DataOps API 日志
-
-3. **数据追踪**
-   - 启用了 "saveDataSuccessExecution: all"
-   - 可以查看每次执行的完整数据
-
-## 性能优化
-
-### 当前配置
-
-- ✅ 数据保存: 成功和失败都保存
-- ✅ 执行顺序: v1
-- ✅ 时区: Asia/Shanghai
-
-### 优化建议
-
-1. **异步处理**: 对于耗时操作使用异步节点
-2. **缓存**: 添加缓存节点减少 API 调用
-3. **批处理**: 支持批量元数据创建
-
-## 故障排查
-
-### 常见问题
-
-| 问题 | 原因 | 解决方案 |
-|------|------|----------|
-| 工作流无法触发 | 未激活 | 激活工作流 |
-| API 调用失败 | DataOps 服务未运行 | 检查服务状态 |
-| 用户响应识别错误 | 条件配置错误 | 检查 IF 节点条件 |
-| 消息显示异常 | 表达式语法错误 | 检查 Set 节点配置 |
-
-## 相关文档
-
-- **详细文档**: `docs/n8n_workflow_data_governance.md`
-- **API 文档**: `docs/api_meta_data.md`
-- **n8n 官方文档**: https://docs.n8n.io
-
-## 总结
-
-✅ **工作流已成功创建**
-- 工作流ID: `rZK08l4aNUGgwmfO`
-- 状态: 待激活
-- 功能: 完整实现
-
-✅ **核心功能完备**
-- 对话式交互
-- 条件判断
-- API 集成
-- 友好反馈
-
-✅ **可扩展性强**
-- 支持添加 AI
-- 可集成更多功能
-- 易于维护升级
-
-🎉 **数据治理聊天工作流已准备就绪!**
-
----
-
-## 📚 相关文档
-
-| 文档 | 路径 | 说明 |
-|------|------|------|
-| **快速开始指南** | `docs/n8n_chat_workflow_quickstart.md` | ⭐⭐⭐ 5分钟快速启动 |
-| **详细文档** | `docs/n8n_workflow_data_governance.md` | 完整技术文档 |
-| **总结文档** | `N8N_WORKFLOW_SUMMARY.md` | 本文档 |
-
----
-
-## 🚀 下一步操作
-
-### 立即开始(推荐顺序)
-
-**1. 配置 DeepSeek API** ⭐⭐⭐ 必需
-```bash
-工作流 → DeepSeek Chat Model 节点 → 选择 DeepSeek 凭证
-```
-
-**2. 激活工作流** ⭐⭐⭐ 必需
-```bash
-Data-governance 工作流 → Active 开关 ON
-```
-
-**3. 测试聊天** ⭐⭐ 推荐
-```bash
-复制 Chat URL → 浏览器打开 → 输入"是"
-```
-
-**4. 集成平台** ⭐ 可选
-```bash
-iframe 嵌入或悬浮按钮
-```
-
----
-
-## 🎊 工作流信息
-
-**工作流 ID**: `tWfjLZE1FmMfQAIn`  
-**Chat URL**: `https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn`  
-**状态**: ✅ 已激活,使用 DeepSeek AI 模型  
-**触发方式**: Chat Trigger(聊天触发器)  
-**AI 模型**: DeepSeek Chat Model ✨
-
-**恭喜!您的 n8n 数据治理工作流已成功创建并已升级到 DeepSeek!** 🎉
-

+ 0 - 261
docs/archive/REMOVAL_SUMMARY_CLEAN_LIST.md

@@ -1,261 +0,0 @@
-# 移除 clean-list 接口 - 变更总结
-
-## 📋 变更概述
-
-已成功移除元数据清洁列表(clean-list)接口及其相关代码和文档。
-
-**变更日期**: 2024-10-31
-
-## 🗑️ 移除的内容
-
-### 1. API 路由 (app/api/meta_data/routes.py)
-
-**移除的接口**:
-- **路径**: `/api/meta/node/clean-list`
-- **方法**: `GET`
-- **函数**: `meta_node_clean_list()`
-
-**变更位置**:
-- 第16行:从导入语句中移除 `meta_clean_list`
-- 第90-105行:移除完整的路由函数定义
-
-### 2. 核心函数 (app/core/meta_data/meta_data.py)
-
-**移除的函数**:
-- **函数名**: `meta_clean_list()`
-- **位置**: 第132-162行(原)
-
-**函数功能**:
-- 查询Neo4j中所有DataMeta节点
-- 返回简洁列表(只包含name_zh和name_en)
-
-### 3. API 文档 (docs/api/meta-clean-list-api.md)
-
-**移除的文档文件**:
-- 完整删除了 `docs/api/meta-clean-list-api.md` 文件
-- 该文档包含了485行的详细API说明、使用示例和最佳实践
-
-## ✅ 验证结果
-
-### 代码验证
-- ✅ 无 linter 错误
-- ✅ 无语法错误
-- ✅ 导入语句已清理
-- ✅ 无遗留引用
-
-### 搜索验证
-```bash
-# 搜索 meta_clean_list
-grep -r "meta_clean_list" . --include="*.py"
-# 结果: 未找到任何引用
-
-# 搜索 clean-list
-grep -r "clean-list" . --include="*.py" --include="*.md"
-# 结果: 未找到任何引用
-```
-
-## 📝 变更详情
-
-### 变更 1: app/api/meta_data/routes.py
-
-#### 导入语句修改
-
-**修改前**:
-```python
-from app.core.meta_data import (
-    translate_and_parse, 
-    get_formatted_time, 
-    meta_list,
-    meta_clean_list,  # 移除此行
-    meta_kinship_graph, 
-    meta_impact_graph,
-    # ...
-)
-```
-
-**修改后**:
-```python
-from app.core.meta_data import (
-    translate_and_parse, 
-    get_formatted_time, 
-    meta_list,
-    meta_kinship_graph, 
-    meta_impact_graph,
-    # ...
-)
-```
-
-#### 路由函数移除
-
-**移除的代码**:
-```python
-# 元数据清洁列表(只返回name_zh和name_en)
-@bp.route('/node/clean-list', methods=['GET'])
-def meta_node_clean_list():
-    """
-    获取所有DataMeta节点的简洁列表
-    返回格式:[{"name_zh": "中文名称", "name_en": "English Name"}, ...]
-    """
-    try:
-        # 调用核心业务逻辑
-        result = meta_clean_list()
-        
-        # 返回结果
-        return jsonify(success(result))
-    except Exception as e:
-        logger.error(f"获取元数据清洁列表失败: {str(e)}")
-        return jsonify(failed(str(e)))
-```
-
-### 变更 2: app/core/meta_data/meta_data.py
-
-**移除的函数**:
-```python
-def meta_clean_list():
-    """
-    获取Neo4j图谱中所有DataMeta节点的简洁列表
-    每个记录只包含name_zh和name_en两个字段
-    
-    Returns:
-        list: 包含所有DataMeta节点的JSON数据列表,格式为 [{"name_zh": "", "name_en": ""}, ...]
-    """
-    try:
-        with neo4j_driver.get_session() as session:
-            # 查询所有DataMeta节点,只返回name和en_name字段
-            cypher = """
-            MATCH (n:DataMeta)
-            RETURN n.name as name_zh, n.en_name as name_en
-            ORDER BY n.name
-            """
-            result = session.run(cypher)
-            
-            # 格式化结果
-            result_list = []
-            for record in result:
-                result_list.append({
-                    "name_zh": record["name_zh"] if record["name_zh"] else "",
-                    "name_en": record["name_en"] if record["name_en"] else ""
-                })
-                
-            logger.info(f"成功获取DataMeta清洁列表,共{len(result_list)}条记录")
-            return result_list
-    except Exception as e:
-        logger.error(f"获取DataMeta清洁列表失败: {str(e)}")
-        raise
-```
-
-### 变更 3: 文档文件删除
-
-**删除的文件**:
-- `docs/api/meta-clean-list-api.md` (485行)
-
-## 🔍 影响分析
-
-### 已移除的功能
-1. **API端点**: `/api/meta/node/clean-list` 不再可用
-2. **数据查询**: 无法通过此接口获取简洁的元数据列表
-3. **文档**: 相关API文档已删除
-
-### 潜在影响
-⚠️ **警告**: 如果前端或其他服务正在使用此接口,需要进行相应调整。
-
-可能受影响的场景:
-- 前端下拉列表数据源
-- 元数据选择器
-- 数据导出功能
-- 第三方集成
-
-### 推荐的替代方案
-
-如果需要类似功能,可以使用以下替代方案:
-
-#### 方案1: 使用现有的 meta_list 接口
-```python
-# API: /api/meta/node/list (POST)
-# 可以通过参数控制返回字段
-{
-  "current": 1,
-  "size": 1000,  # 设置较大的size获取所有数据
-  "search": ""
-}
-```
-
-#### 方案2: 创建新的轻量级查询接口
-如果确实需要轻量级的列表接口,可以考虑:
-- 在 `meta_list` 中添加 `fields` 参数控制返回字段
-- 创建新的专用查询接口(根据实际需求设计)
-
-## 📊 文件变更统计
-
-| 文件 | 变更类型 | 行数变化 |
-|------|---------|----------|
-| `app/api/meta_data/routes.py` | 修改 | -17行 |
-| `app/core/meta_data/meta_data.py` | 修改 | -31行 |
-| `docs/api/meta-clean-list-api.md` | 删除 | -485行 |
-| **总计** | - | **-533行** |
-
-## ✅ 验证清单
-
-- [x] API路由已移除
-- [x] 核心函数已移除
-- [x] 导入语句已清理
-- [x] 文档文件已删除
-- [x] 无linter错误
-- [x] 无遗留引用
-- [x] 代码可以正常运行
-
-## 🚀 后续步骤
-
-### 立即行动
-1. ✅ 代码变更已完成
-2. ⚠️ **检查前端代码**:确认前端是否使用此接口
-3. ⚠️ **通知相关团队**:如果有其他服务依赖此接口
-4. ⚠️ **更新API文档汇总**:如果有API目录或索引文档需要更新
-
-### 测试建议
-1. 启动应用,确认无启动错误
-2. 访问其他元数据相关接口,确认功能正常
-3. 检查日志,确认无异常
-
-### 部署注意事项
-1. 与前端团队确认影响范围
-2. 如有依赖,先更新前端代码
-3. 协调部署时间,避免服务中断
-
-## 📞 联系信息
-
-如有问题或需要恢复此功能,请联系相关开发人员。
-
-## 🔄 版本信息
-
-- **移除版本**: DataOps Platform v1.x
-- **移除日期**: 2024-10-31
-- **执行人**: Cursor AI Assistant
-- **审核状态**: ✅ 完成
-
----
-
-**备注**: 
-此变更已清理所有相关代码和文档。如需恢复,可以从Git历史记录中找回。
-
-**Git 提交建议**:
-```bash
-git add app/api/meta_data/routes.py
-git add app/core/meta_data/meta_data.py
-git add docs/api/
-git commit -m "refactor: 移除 clean-list 接口及相关代码
-
-- 移除 /api/meta/node/clean-list API端点
-- 移除 meta_clean_list() 核心函数
-- 删除相关API文档
-- 清理导入语句和遗留引用
-"
-```
-
-
-
-
-
-
-
-

+ 0 - 303
docs/archive/TEST_REPORT_218.md

@@ -1,303 +0,0 @@
-# 服务器 192.168.3.218:18183 接口测试报告
-
-**测试时间**: 2025-11-04 18:22  
-**服务器地址**: `http://192.168.3.218:18183`  
-**测试接口**: `/api/meta/check`
-
----
-
-## ❌ 测试结果:接口不存在
-
-###错误信息
-
-```json
-{
-  "data": null,
-  "message": "请求的资源不存在",
-  "success": false
-}
-```
-
-**HTTP 状态码**: 404 Not Found
-
----
-
-## 🔍 问题分析
-
-### 接口不存在的证据
-
-1. **所有请求都返回 404**
-   - 测试 1: 检查已存在的元数据 → 404
-   - 测试 2: 检查不存在的元数据 → 404
-   - 测试 3: 参数验证 → 404
-   - 测试 4: 特殊字符处理 → 404
-
-2. **服务器连接正常**
-   - ✅ 服务器可以访问
-   - ✅ 响应时间: 0.03-0.07秒(很快)
-   - ✅ 返回标准的 404 错误格式
-
-3. **API 探索结果**
-   - ❌ `/api/meta/check` → 404 不存在
-   - ❌ `/api/meta/list` → 404 不存在
-   - ⚠️ `/api/meta/node/list` → 405 Method Not Allowed(存在但方法不对)
-
----
-
-## 🎯 根本原因
-
-### `/api/meta/check` 接口未部署到此服务器
-
-**可能的原因**:
-
-1. **代码未更新** ⭐⭐⭐⭐⭐ (最可能)
-   - 这个服务器上运行的是旧版本代码
-   - `/api/meta/check` 是新添加的接口
-   - 需要部署最新代码
-
-2. **路由配置不同**
-   - 这个服务器使用不同的路由前缀
-   - 但从探索结果看,其他 `/api/meta/*` 路径也都是 404
-   - 所以不太可能
-
-3. **服务未启动**
-   - 但服务器明显在运行(返回了标准错误)
-   - 所以不是这个原因
-
----
-
-## 📊 服务器当前状态
-
-### 可访问的内容
-
-| 路径 | 状态 | 说明 |
-|------|------|------|
-| `/` | ✅ 200 | HTML 前端页面 |
-| `/api/meta/node/list` | ⚠️ 405 | 存在但方法错误 |
-
-### 不可访问的内容
-
-| 路径 | 状态 | 说明 |
-|------|------|------|
-| `/api/meta/check` | ❌ 404 | **目标接口不存在** |
-| `/api/meta/list` | ❌ 404 | 不存在 |
-| `/api/meta` | ❌ 404 | 不存在 |
-| `/api` | ❌ 404 | 不存在 |
-
----
-
-## ✅ 对比:n8n 工作流使用的服务器
-
-### n8n 工具配置
-
-**工作流中使用的 URL**:
-- **检查元数据**: `http://192.168.3.143:5000/api/meta/check`
-- **创建元数据**: `http://192.168.3.143:5000/api/meta/node/add`
-
-**测试结果**: ✅ 完全正常(已验证)
-
-### 服务器对比
-
-| 服务器 | 端口 | `/api/meta/check` | 状态 |
-|--------|------|-------------------|------|
-| 192.168.3.143 | 5000 | ✅ 存在 | n8n 正常使用 |
-| 192.168.3.218 | 18183 | ❌ 不存在 | **需要部署** |
-
----
-
-## 🔧 解决方案
-
-### 方案 1: 部署最新代码到 192.168.3.218 ⭐⭐⭐⭐⭐
-
-**步骤**:
-
-1. **确认代码版本**
-   ```bash
-   # 在 192.168.3.143 服务器上
-   cd /path/to/DataOps-platform
-   git log -1 --oneline app/api/meta_data/routes.py
-   ```
-
-2. **在 192.168.3.218 上更新代码**
-   ```bash
-   # SSH 到 192.168.3.218
-   ssh user@192.168.3.218
-   
-   # 进入项目目录
-   cd /path/to/DataOps-platform
-   
-   # 拉取最新代码
-   git pull origin main
-   
-   # 重启服务
-   # 方法取决于你的部署方式:
-   # systemctl restart dataops-platform
-   # 或 pm2 restart dataops-platform
-   # 或 docker-compose restart
-   ```
-
-3. **验证部署**
-   ```bash
-   # 从本地测试
-   curl "http://192.168.3.218:18183/api/meta/check?name_zh=测试"
-   ```
-
-### 方案 2: 修改 n8n 工作流配置 ⭐⭐
-
-如果不能更新 192.168.3.218,可以继续使用 192.168.3.143:
-
-**n8n 工具已经配置正确**:
-- ✅ 检查元数据: `http://192.168.3.143:5000/api/meta/check`
-- ✅ 创建元数据: `http://192.168.3.143:5000/api/meta/node/add`
-
-**无需修改,已经在正常工作!**
-
----
-
-## 📝 需要部署的文件
-
-### 新增的接口代码
-
-**文件**: `app/api/meta_data/routes.py`  
-**位置**: 第 197-238 行  
-**添加时间**: 2025-11-04
-
-**代码**:
-```python
-@bp.route('/check', methods=['GET'])
-def meta_check():
-    """
-    检查元数据中文名是否已存在
-    
-    请求参数:
-    - name_zh: 元数据中文名(URL参数)
-    
-    返回:
-    - exists: true/false 表示是否存在
-    """
-    try:
-        name_zh = request.args.get('name_zh')
-        
-        if not name_zh:
-            return jsonify(failed({}, "缺少name_zh参数"))
-        
-        # 查询数据库检查是否存在
-        with neo4j_driver.get_session() as session:
-            cypher = """
-            MATCH (n:DataMeta {name_zh: $name_zh})
-            RETURN count(n) > 0 as exists
-            """
-            result = session.run(cypher, name_zh=name_zh)
-            record = result.single()
-            
-            if record:
-                exists = record["exists"]
-                logger.info(f"检查元数据 '{name_zh}': {'存在' if exists else '不存在'}")
-                return jsonify(success({
-                    "exists": exists,
-                    "name_zh": name_zh
-                }, "查询成功"))
-            else:
-                return jsonify(success({
-                    "exists": False,
-                    "name_zh": name_zh
-                }, "查询成功"))
-                
-    except Exception as e:
-        logger.error(f"检查元数据失败: {str(e)}")
-        return jsonify(failed({}, f"检查失败: {str(e)}"))
-```
-
----
-
-## 🎯 测试脚本
-
-已创建测试脚本:
-- **文件**: `test_check_218.py`
-- **用途**: 测试 192.168.3.218:18183 服务器
-
-**使用方法**:
-```bash
-python test_check_218.py
-```
-
-部署后运行此脚本验证接口是否正常。
-
----
-
-## 📊 测试数据
-
-### 连接测试
-
-| 项目 | 结果 |
-|------|------|
-| 服务器可达性 | ✅ 正常 |
-| 响应速度 | ✅ 快速 (0.03-0.07秒) |
-| 错误格式 | ✅ 标准化 |
-| 接口存在性 | ❌ `/api/meta/check` 不存在 |
-
-### 探索结果
-
-```
-测试路径数量: 12
-成功响应 (200): 1 (根路径)
-不存在 (404): 10
-方法错误 (405): 1 (/api/meta/node/list)
-```
-
----
-
-## ✅ 结论
-
-### 问题确认
-
-**`/api/meta/check` 接口在 192.168.3.218:18183 服务器上不存在。**
-
-### 原因
-
-**服务器运行的是旧版本代码,未包含新添加的 `/api/meta/check` 接口。**
-
-### 解决方案
-
-**部署最新代码到 192.168.3.218 服务器。**
-
-### 当前工作状态
-
-**n8n 工作流正常运行,使用的是 192.168.3.143:5000 服务器。**
-**无需修改 n8n 配置,已经工作正常!**
-
----
-
-## 🚀 下一步行动
-
-### 立即可行
-
-**继续使用 192.168.3.143:5000**
-- ✅ n8n 工作流已经配置正确
-- ✅ 接口工作正常
-- ✅ 无需任何修改
-
-### 后续优化
-
-**部署到 192.168.3.218:18183**(如果需要)
-1. 更新代码
-2. 重启服务
-3. 运行测试脚本验证
-4. 如需要,更新 n8n 工具 URL
-
----
-
-**测试完成时间**: 2025-11-04 18:22  
-**测试结果**: ❌ 接口不存在于目标服务器  
-**n8n 工作流状态**: ✅ 正常运行(使用 192.168.3.143)
-
----
-
-💡 **建议**: 继续使用当前配置,n8n 工作流已经完全正常!
-
-
-
-
-
-
-

+ 0 - 280
docs/archive/TRIGGER_OPTIMIZATION_SUMMARY.md

@@ -1,280 +0,0 @@
-# Cursor任务执行触发器优化总结
-
-## 🎯 优化目标
-
-将 `trigger_cursor_execution.py` 优化为支持**定期自动执行**,让Cursor能够自动检测并执行任务。
-
----
-
-## ✅ 已完成的优化
-
-### 1. 定期执行模式 ⭐
-
-**新增功能**:
-- 支持循环执行模式(类似 `auto_execute_tasks.py`)
-- 可配置检查间隔(默认5分钟)
-- 自动检测processing任务并触发执行
-
-**使用方法**:
-```bash
-# 单次执行
-python scripts/trigger_cursor_execution.py --once
-
-# 定期执行(每5分钟)
-python scripts/trigger_cursor_execution.py --interval 300
-
-# 自定义间隔(每10分钟)
-python scripts/trigger_cursor_execution.py --interval 600
-```
-
----
-
-### 2. 任务指令文件自动生成 ⭐
-
-**新增功能**:
-- 自动生成 `.cursor/task_execute_instructions.md` 文件
-- Markdown格式,包含完整的任务描述和执行步骤
-- Cursor可以自动读取此文件并执行任务
-
-**文件位置**:
-- `.cursor/task_execute_instructions.md`
-
-**文件内容**:
-- 任务列表
-- 任务描述
-- 执行步骤
-- MCP工具调用示例
-
----
-
-### 3. Cursor自动执行规则 ⭐
-
-**新增文件**:
-- `.cursor/rules/auto_task_execution.mdc`
-
-**功能**:
-- 定义Cursor自动执行机制
-- 说明检查频率和执行流程
-- 提供手动触发方式
-
----
-
-### 4. 便捷启动脚本
-
-**新增文件**:
-- `scripts/start_cursor_task_trigger.bat` - 前台运行
-- `scripts/start_cursor_task_trigger_background.bat` - 后台运行
-
-**使用方法**:
-```bash
-# 前台运行(可以看到输出)
-scripts\start_cursor_task_trigger.bat
-
-# 后台运行(无窗口)
-scripts\start_cursor_task_trigger_background.bat
-```
-
----
-
-### 5. 完整的日志记录
-
-**改进**:
-- 使用标准的logging模块
-- 后台模式输出到日志文件:`logs/cursor_task_trigger.log`
-- 前台模式输出到控制台
-
----
-
-## 🔄 工作流程
-
-### 优化前
-```
-1. 手动运行脚本
-2. 脚本输出任务信息
-3. 用户在Cursor中手动执行任务
-```
-
-### 优化后
-```
-1. 脚本定期自动检查(后台运行)
-   ↓
-2. 发现processing任务
-   ↓
-3. 自动生成任务指令文件(.cursor/task_execute_instructions.md)
-   ↓
-4. 输出任务信息到控制台/日志
-   ↓
-5. Cursor自动检测文件变化
-   ↓
-6. Cursor自动读取指令文件
-   ↓
-7. Cursor自动执行任务
-   ↓
-8. 任务完成!✅
-```
-
----
-
-## 📋 新增功能对比
-
-| 功能 | 优化前 | 优化后 |
-|------|--------|--------|
-| **执行模式** | 仅单次执行 | ✅ 单次 + 定期执行 |
-| **自动触发** | ❌ 无 | ✅ 自动生成指令文件 |
-| **Cursor集成** | ⚠️ 需手动查看 | ✅ 自动检测并执行 |
-| **日志记录** | ⚠️ 简单输出 | ✅ 完整日志系统 |
-| **启动脚本** | ❌ 无 | ✅ Windows批处理文件 |
-| **配置灵活** | ⚠️ 固定 | ✅ 可配置检查间隔 |
-
----
-
-## 🚀 使用场景
-
-### 场景1:开发调试
-```bash
-# 手动触发,查看任务
-python scripts/trigger_cursor_execution.py --once
-```
-
-### 场景2:生产环境(推荐)
-```bash
-# 后台持续运行,自动监控和执行
-scripts\start_cursor_task_trigger_background.bat
-```
-
-### 场景3:快速响应
-```bash
-# 1分钟检查一次(快速测试)
-python scripts/trigger_cursor_execution.py --interval 60
-```
-
-### 场景4:在Cursor中手动触发
-在Cursor Chat中说:
-```
-请检查并执行所有待处理任务
-```
-
----
-
-## 📁 相关文件
-
-### 核心脚本
-- `scripts/trigger_cursor_execution.py` - 主脚本(已优化)
-
-### 启动脚本
-- `scripts/start_cursor_task_trigger.bat` - 前台启动
-- `scripts/start_cursor_task_trigger_background.bat` - 后台启动
-
-### 配置文件
-- `.cursor/rules/auto_task_execution.mdc` - Cursor自动执行规则
-- `.cursor/task_execute_instructions.md` - 任务执行指令(自动生成)
-
-### 文档
-- `docs/CURSOR_AUTO_TASK_TRIGGER.md` - 完整使用指南
-
----
-
-## 🎯 关键改进点
-
-### 1. 自动化程度提升
-- **之前**:需要手动运行脚本,Cursor才能看到任务
-- **现在**:脚本自动定期检查,自动生成指令文件,Cursor自动检测并执行
-
-### 2. 用户体验优化
-- **之前**:需要查看控制台输出或JSON文件
-- **现在**:自动生成Markdown格式的指令文件,清晰易读
-
-### 3. 集成度提升
-- **之前**:脚本和Cursor是分离的
-- **现在**:通过指令文件实现无缝集成
-
-### 4. 可维护性提升
-- **之前**:单次执行脚本
-- **现在**:支持持续运行,完整的日志记录
-
----
-
-## 📊 测试验证
-
-### 测试1:单次执行
-```bash
-python scripts/trigger_cursor_execution.py --once
-```
-**结果**:✅ 成功
-- 正确识别processing任务
-- 成功生成任务指令文件
-- 正确输出任务信息
-
-### 测试2:文件生成
-**检查**:`.cursor/task_execute_instructions.md`
-**结果**:✅ 成功
-- 文件格式正确
-- 内容完整
-- 包含所有必要信息
-
-### 测试3:定期执行模式
-```bash
-python scripts/trigger_cursor_execution.py --interval 300
-```
-**结果**:✅ 成功
-- 循环执行正常
-- 日志输出正确
-- 可以正常停止(Ctrl+C)
-
----
-
-## 💡 使用建议
-
-### 开发环境
-- 使用单次执行模式:`--once`
-- 手动触发,方便调试
-
-### 生产环境
-- 使用定期执行模式:后台运行
-- 设置合适的检查间隔(建议5-10分钟)
-
-### 团队协作
-- 启动定期执行服务
-- Cursor会自动检测并执行任务
-- 团队成员只需关注任务完成情况
-
----
-
-## 🔧 下一步优化建议
-
-### 短期优化(可选)
-1. ⏳ 添加邮件/企业微信通知(任务完成时)
-2. ⏳ 支持任务优先级排序
-3. ⏳ 添加任务执行历史记录
-
-### 长期优化(可选)
-1. ⏳ 集成到CI/CD流程
-2. ⏳ Web管理界面
-3. ⏳ 任务依赖关系支持
-
----
-
-## ✅ 总结
-
-### 优化成果
-1. ✅ **定期执行模式**:支持持续监控和自动触发
-2. ✅ **指令文件生成**:自动生成Markdown格式的执行指令
-3. ✅ **Cursor集成**:通过规则文件实现自动检测和执行
-4. ✅ **便捷工具**:Windows批处理启动脚本
-5. ✅ **完整文档**:详细的使用指南
-
-### 用户体验提升
-- **自动化**:从手动执行到自动检测和执行
-- **可视化**:从JSON文件到Markdown指令文件
-- **集成化**:从分离工具到无缝集成
-
----
-
-**优化完成时间**:2025-11-29  
-**状态**:✅ 已完成并通过测试
-
-
-
-
-
-

+ 0 - 245
docs/archive/WORKFLOW_UPDATE_SUMMARY.md

@@ -1,245 +0,0 @@
-# Data-governance 工作流更新总结
-
-**更新时间**: 2025-11-04  
-**工作流 ID**: `tWfjLZE1FmMfQAIn`  
-**工作流名称**: Data-governance
-
----
-
-## 🎯 更新目标
-
-修改工作流,使其能够:
-1. 在聊天中获取元数据名称
-2. 自动判断元数据是否已经存在
-3. 如果不存在就添加元数据
-4. 如果已经存在就返回存在的信息
-
----
-
-## ✅ 已完成的更新
-
-### 1. 更新 Chat Trigger 节点
-
-**修改内容**:
-- **欢迎消息**: 更新为更直接的提示,让用户直接输入元数据名称
-- **输入提示**: 从"请输入您的需求..."改为"请输入元数据名称..."
-- **副标题**: 从"帮助您进行元数据管理和数据治理"改为"智能元数据管理和查询"
-
-**新的欢迎消息**:
-```
-您好!我是数据治理助手。
-
-我可以帮助您管理和创建元数据。
-
-请直接告诉我您想要查询或创建的元数据名称,例如:
-- "检查元数据:其他费用定额"
-- "创建元数据:其他费用定额,类型string,描述医疗行业费用元数据"
-
-我会自动检查该元数据是否已存在,如果不存在则为您创建。
-```
-
----
-
-### 2. 更新 AI Agent 系统消息
-
-**新的工作流程**:
-
-1. **当用户提供元数据名称时**(无论是要求查询还是创建),首先使用 `check_metadata` 工具检查该元数据是否已存在
-
-2. **根据检查结果**:
-   - **如果元数据已存在(exists=true)**:
-     * 告诉用户该元数据已经存在于系统中
-     * 返回元数据的相关信息
-     * 询问用户是否需要其他帮助
-   
-   - **如果元数据不存在(exists=false)**:
-     * 如果用户提供了完整信息(名称、类型、描述),使用 `create_metadata` 工具创建元数据
-     * 如果用户只提供了名称,询问用户是否需要创建,如果需要则询问类型和描述信息
-     * 创建成功后,确认创建结果并返回给用户
-
-3. **如果用户输入包含元数据名称,直接提取名称并使用工具检查,不需要额外询问**
-
-**关键原则**:
-- 始终先检查元数据是否存在,再决定是否需要创建
-- 如果元数据已存在,明确告诉用户,不要重复创建
-- 友好地引导用户提供信息(如果需要创建)
-- 每次只处理一个元数据
-- 使用工具返回的 `exists` 字段判断是否存在,不要猜测
-
----
-
-## 📊 工作流结构
-
-### 当前节点
-
-1. **Chat Trigger** - 聊天触发器
-2. **AI Agent** - AI 代理(核心处理节点)
-3. **DeepSeek Chat Model** - DeepSeek 语言模型
-4. **检查元数据工具** (toolHttpRequest) - 调用 `/api/meta/check` 接口
-5. **创建元数据工具** (toolHttpRequest) - 调用 `/api/meta/node/add` 接口
-
-### 保留但未使用的节点(可以删除)
-
-- **判断用户意图** (IF 节点) - 不再需要,AI Agent 内部处理逻辑
-- **调用元数据新增API** (HTTP Request) - 不再需要,使用工具替代
-- **设置确认消息** (Set 节点) - 不再需要
-- **设置拒绝消息** (Set 节点) - 不再需要
-
----
-
-## 🔄 工作流程
-
-### 流程 1: 用户查询已存在的元数据
-
-```
-用户输入: "其他费用定额"
-    ↓
-AI Agent 提取元数据名称
-    ↓
-调用 check_metadata 工具
-    ↓
-工具返回: exists=true
-    ↓
-AI Agent 告诉用户: "该元数据已存在"
-    ↓
-返回给用户
-```
-
-### 流程 2: 用户创建新元数据(提供完整信息)
-
-```
-用户输入: "创建元数据:测试数据,类型string,描述测试描述"
-    ↓
-AI Agent 提取元数据名称: "测试数据"
-    ↓
-调用 check_metadata 工具
-    ↓
-工具返回: exists=false
-    ↓
-AI Agent 提取类型和描述信息
-    ↓
-调用 create_metadata 工具
-    ↓
-工具返回: 创建成功
-    ↓
-AI Agent 告诉用户: "元数据创建成功"
-    ↓
-返回给用户
-```
-
-### 流程 3: 用户只提供名称,需要创建
-
-```
-用户输入: "测试元数据"
-    ↓
-AI Agent 提取元数据名称: "测试元数据"
-    ↓
-调用 check_metadata 工具
-    ↓
-工具返回: exists=false
-    ↓
-AI Agent 询问用户: "该元数据不存在,是否需要创建?如果需要,请提供类型和描述"
-    ↓
-用户提供信息
-    ↓
-调用 create_metadata 工具
-    ↓
-返回创建结果
-```
-
----
-
-## 🔧 工具配置
-
-### 检查元数据工具
-
-- **URL**: `http://192.168.3.143:5000/api/meta/check?name_zh={name_zh}`
-- **方法**: GET
-- **参数**: `name_zh` (元数据中文名)
-- **返回**: `{exists: true/false, name_zh: "..."}`
-
-### 创建元数据工具
-
-- **URL**: `http://192.168.3.143:5000/api/meta/node/add`
-- **方法**: POST
-- **参数**: 
-  - `name_zh` (必填)
-  - `data_type` (默认 string)
-  - `description` (选填)
-- **返回**: 创建结果
-
----
-
-## ✅ 测试建议
-
-### 测试用例 1: 查询已存在的元数据
-
-**输入**: "其他费用定额"
-
-**预期结果**: 
-- 调用 check_metadata 工具
-- 返回 exists=true
-- 告诉用户元数据已存在
-
----
-
-### 测试用例 2: 创建新元数据(完整信息)
-
-**输入**: "创建元数据:新测试数据,类型string,描述这是一个测试"
-
-**预期结果**:
-- 调用 check_metadata 工具
-- 返回 exists=false
-- 调用 create_metadata 工具
-- 创建成功
-- 告诉用户创建成功
-
----
-
-### 测试用例 3: 创建新元数据(只提供名称)
-
-**输入**: "新元数据名称"
-
-**预期结果**:
-- 调用 check_metadata 工具
-- 返回 exists=false
-- AI Agent 询问是否需要创建,需要类型和描述
-- 用户提供后创建
-
----
-
-### 测试用例 4: 尝试创建已存在的元数据
-
-**输入**: "创建元数据:其他费用定额,类型string,描述测试"
-
-**预期结果**:
-- 调用 check_metadata 工具
-- 返回 exists=true
-- 告诉用户元数据已存在,不进行创建
-
----
-
-## 📝 注意事项
-
-1. **AI Agent 是终端节点**: AI Agent 的输出会直接返回到聊天界面,无需后续节点
-
-2. **工具自动调用**: AI Agent 会根据系统消息的指导,自动决定何时调用工具
-
-3. **错误处理**: 如果工具调用失败,AI Agent 会收到错误信息并告知用户
-
-4. **接口验证**: 确保 `/api/meta/check` 和 `/api/meta/node/add` 接口正常工作
-
----
-
-## 🚀 下一步
-
-1. **测试工作流**: 使用上述测试用例验证工作流是否正常工作
-
-2. **清理节点** (可选): 可以删除不再使用的节点(IF、HTTP Request、Set 节点),但不影响功能
-
-3. **验证接口**: 确保接口在 `192.168.3.143:5000` 上正常工作
-
----
-
-**更新完成时间**: 2025-11-04  
-**状态**: ✅ 配置已更新,等待测试验证

+ 0 - 1053
docs/archive/add_webpage_talent_api_docs.md

@@ -1,1053 +0,0 @@
-# 网页人才信息添加API使用说明手册
-
-## API概述
-
-`/add-webpage-talent` 接口用于批量添加网页提取的人才信息到系统中。该接口能够将网页内容保存到MinIO存储,并为每个人才创建对应的业务卡片记录。系统会自动设置数据来源信息(`origin_source`)和人才档案信息(`talent_profile`)。
-
----
-
-## 基本信息
-
-| 项目 | 值 |
-|------|-----|
-| **接口路径** | `/api/data-parse/add-webpage-talent` |
-| **请求方法** | `POST` |
-| **Content-Type** | `application/json` |
-| **认证方式** | 根据系统配置 |
-
----
-
-## 输入参数
-
-### 请求体结构
-
-```json
-{
-  "talent_list": [
-    {
-      "name_zh": "张三",
-      "name_en": "Zhang San",
-      "title_zh": "总经理",
-      "title_en": "General Manager",
-      "hotel_zh": "北京万豪酒店",
-      "hotel_en": "Beijing Marriott Hotel",
-      "brand_group": "万豪",
-      "mobile": "13800138000",
-      "email": "zhangsan@example.com",
-      "phone": "010-12345678",
-      "address_zh": "北京市朝阳区XXX路123号",
-      "address_en": "123 XXX Road, Chaoyang District, Beijing",
-      "postal_code_zh": "100000",
-      "postal_code_en": "100000",
-      "brand_zh": "万豪",
-      "brand_en": "Marriott",
-      "affiliation_zh": "万豪国际集团",
-      "affiliation_en": "Marriott International",
-      "birthday": "1980-01-01",
-      "age": 44,
-      "native_place": "北京市",
-      "residence": "北京市朝阳区",
-      "pic_url": "https://example.com/photo.jpg",
-      "talent_profile": "资深酒店管理专家,拥有20年行业经验..."
-    }
-  ],
-  "web_md": "# 酒店任命公告\n\n## 人事变动\n\n1. **张三** 被任命为北京万豪酒店总经理...\n\n更多内容..."
-}
-```
-
-### 参数详细说明
-
-#### `talent_list` (必填)
-- **类型**: `Array<Object>`
-- **描述**: 人才信息列表,每个对象包含一个人才的详细信息
-- **限制**: 非空数组
-
-#### `talent_list[].name_zh` (必填)
-- **类型**: `String`
-- **描述**: 中文姓名
-- **示例**: `"张三"`
-
-#### `talent_list[].name_en` (可选)
-- **类型**: `String`
-- **描述**: 英文姓名
-- **示例**: `"Zhang San"`
-
-#### `talent_list[].title_zh` (可选)
-- **类型**: `String`
-- **描述**: 中文职位
-- **示例**: `"总经理"`
-
-#### `talent_list[].title_en` (可选)
-- **类型**: `String`
-- **描述**: 英文职位
-- **示例**: `"General Manager"`
-
-#### `talent_list[].hotel_zh` (可选)
-- **类型**: `String`
-- **描述**: 酒店中文名称
-- **示例**: `"北京万豪酒店"`
-
-#### `talent_list[].hotel_en` (可选)
-- **类型**: `String`
-- **描述**: 酒店英文名称
-- **示例**: `"Beijing Marriott Hotel"`
-
-#### `talent_list[].brand_group` (可选)
-- **类型**: `String`
-- **描述**: 品牌组合
-- **示例**: `"万豪"`
-
-#### `talent_list[].mobile` (可选)
-- **类型**: `String`
-- **描述**: 手机号码,支持多个号码用逗号分隔
-- **示例**: `"13800138000,13900139000"`
-
-#### `talent_list[].email` (可选)
-- **类型**: `String`
-- **描述**: 电子邮箱
-- **示例**: `"zhangsan@example.com"`
-
-#### `talent_list[].phone` (可选)
-- **类型**: `String`
-- **描述**: 固定电话
-- **示例**: `"010-12345678"`
-
-#### `talent_list[].address_zh` (可选)
-- **类型**: `String`
-- **描述**: 中文地址
-- **示例**: `"北京市朝阳区XXX路123号"`
-
-#### `talent_list[].address_en` (可选)
-- **类型**: `String`
-- **描述**: 英文地址
-- **示例**: `"123 XXX Road, Chaoyang District, Beijing"`
-
-#### `talent_list[].birthday` (可选)
-- **类型**: `String`
-- **描述**: 生日,格式为YYYY-MM-DD
-- **示例**: `"1980-01-01"`
-
-#### `talent_list[].age` (可选)
-- **类型**: `Integer`
-- **描述**: 年龄
-- **示例**: `44`
-
-#### `talent_list[].pic_url` (可选)
-- **类型**: `String`
-- **描述**: 人才照片URL地址,系统会自动下载并保存到MinIO
-- **示例**: `"https://example.com/photo.jpg"`
-- **注意**: 如果提供了有效的图片URL,系统会自动下载图片并保存到MinIO存储中
-
-#### `talent_list[].talent_profile` (可选)
-- **类型**: `String`
-- **描述**: 人才档案信息,包含个人简介、工作经历、专业技能等详细信息
-- **示例**: `"资深酒店管理专家,拥有20年行业经验,曾在多家国际知名酒店担任高级管理职位..."`
-
-#### `web_md` (必填)
-- **类型**: `String`
-- **描述**: 网页的markdown格式文本内容,将被保存到MinIO存储
-- **限制**: 非空字符串
-
----
-
-## 输出参数
-
-### 响应结构
-
-#### 成功响应 (所有记录处理成功)
-```json
-{
-  "code": 200,
-  "success": true,
-  "message": "所有3条人才记录处理成功",
-  "data": [
-    {
-      "id": 123,
-      "name_zh": "张三",
-      "name_en": "Zhang San",
-      "title_zh": "总经理",
-      "title_en": "General Manager",
-      "hotel_zh": "北京万豪酒店",
-      "hotel_en": "Beijing Marriott Hotel",
-      "mobile": "13800138000",
-      "email": "zhangsan@example.com",
-      "phone": "010-12345678",
-      "address_zh": "北京市朝阳区XXX路123号",
-      "address_en": "123 XXX Road, Chaoyang District, Beijing",
-      "postal_code_zh": "100000",
-      "postal_code_en": "100000",
-      "brand_zh": "万豪",
-      "brand_en": "Marriott",
-      "affiliation_zh": "万豪国际集团",
-      "affiliation_en": "Marriott International",
-      "birthday": "1980-01-01",
-      "age": 44,
-      "native_place": "北京市",
-      "residence": "北京市朝阳区",
-      "brand_group": "万豪",
-      "image_path": "talent_photos/talent_photo_张三_20240101_12345678.jpg",
-      "career_path": [
-        {
-          "date": "2024-01-01",
-          "hotel_zh": "北京万豪酒店",
-          "hotel_en": "Beijing Marriott Hotel",
-          "title_zh": "总经理",
-          "title_en": "General Manager",
-          "image_path": "talent_photos/talent_photo_张三_20240101_12345678.jpg",
-          "source": "webpage_extraction"
-        }
-      ],
-      "talent_profile": "资深酒店管理专家,拥有20年行业经验...",
-      "origin_source": {
-        "type": "webpage_talent",
-        "minio_path": "webpage_talent/webpage_talent_20240101_12345678.md",
-        "source_date": "2024-01-01 12:00:00"
-      },
-      "created_at": "2024-01-01 12:00:00",
-      "updated_at": "2024-01-01 12:00:00",
-      "updated_by": "webpage_talent_system",
-      "status": "active"
-    },
-    {
-      "id": 124,
-      "name_zh": "李四",
-      "name_en": "Li Si",
-      "title_zh": "副总经理",
-      "title_en": "Deputy General Manager",
-      "hotel_zh": "上海希尔顿酒店",
-      "hotel_en": "Shanghai Hilton Hotel",
-      "mobile": "13900139000",
-      "email": "lisi@example.com",
-      "brand_group": "希尔顿",
-      "image_path": "talent_photos/talent_photo_李四_20240101_12345679.jpg",
-      "career_path": [
-        {
-          "date": "2024-01-01",
-          "hotel_zh": "上海希尔顿酒店",
-          "hotel_en": "Shanghai Hilton Hotel",
-          "title_zh": "副总经理",
-          "title_en": "Deputy General Manager",
-          "image_path": "talent_photos/talent_photo_李四_20240101_12345679.jpg",
-          "source": "webpage_extraction"
-        }
-      ],
-      "talent_profile": "酒店业资深管理者,专注于市场营销和品牌推广...",
-             "origin_source": {
-         "type": "webpage_talent",
-         "minio_path": "webpage_talent/webpage_talent_20240101_12345678.md",
-         "source_date": "2024-01-01 12:00:00"
-       },
-      "created_at": "2024-01-01 12:00:00",
-      "updated_at": "2024-01-01 12:00:00",
-      "updated_by": "webpage_talent_system",
-      "status": "active"
-    }
-  ]
-}
-```
-
-#### 部分成功响应
-```json
-{
-  "code": 206,
-  "success": true,
-  "message": "部分处理成功:2/3条记录成功",
-  "data": {
-    "total_count": 3,
-    "success_count": 2,
-    "failed_count": 1,
-    "success_records": [
-      {
-        "id": 123,
-        "name_zh": "张三",
-        "name_en": "Zhang San",
-        "title_zh": "总经理",
-        "title_en": "General Manager",
-        "hotel_zh": "北京万豪酒店",
-        "hotel_en": "Beijing Marriott Hotel",
-        "mobile": "13800138000",
-        "email": "zhangsan@example.com",
-        "brand_group": "万豪",
-        "image_path": "talent_photos/talent_photo_张三_20240101_12345678.jpg",
-        "talent_profile": "资深酒店管理专家,拥有20年行业经验...",
-                 "origin_source": {
-           "type": "webpage_talent",
-           "minio_path": "webpage_talent/webpage_talent_20240101_12345678.md",
-           "source_date": "2024-01-01 12:00:00"
-         },
-        "created_at": "2024-01-01 12:00:00",
-        "updated_at": "2024-01-01 12:00:00",
-        "status": "active"
-      },
-      {
-        "id": 124,
-        "name_zh": "李四",
-        "title_zh": "副总经理",
-        "hotel_zh": "上海希尔顿酒店",
-        "brand_group": "希尔顿",
-        "image_path": "talent_photos/talent_photo_李四_20240101_12345679.jpg",
-        "talent_profile": "酒店业资深管理者...",
-        "origin_source": {
-           "type": "webpage_talent",
-           "minio_path": "webpage_talent/webpage_talent_20240101_12345678.md",
-           "source_date": "2024-01-01 12:00:00"
-         },
-        "created_at": "2024-01-01 12:00:00",
-        "status": "active"
-      }
-    ],
-    "failed_records": [
-      {
-        "error": "第3个记录缺少name_zh字段",
-        "input_data": {
-          "name_zh": "",
-          "title_zh": "经理"
-        }
-      }
-    ],
-    "minio_md_path": "webpage_talent/webpage_talent_20240101_12345678.md"
-  }
-}
-```
-
-#### 错误响应
-```json
-{
-  "code": 400,
-  "success": false,
-  "message": "talent_list参数必须是非空数组",
-  "data": null
-}
-```
-
-### 响应字段说明
-
-#### 通用字段
-- **`code`**: HTTP状态码 (200=全部成功, 206=部分成功, 400=参数错误, 500=服务器错误)
-- **`success`**: 操作是否成功 (true/false)
-- **`message`**: 响应消息,描述操作结果
-- **`data`**: 响应数据内容
-
-#### 全部成功时的data字段 (code=200)
-- **类型**: `Array<Object>`
-- **描述**: 成功创建/更新的业务卡片记录数组,每个对象包含完整的名片信息
-
-#### 部分成功时的data字段 (code=206)
-- **`total_count`**: 总记录数
-- **`success_count`**: 成功处理的记录数
-- **`failed_count`**: 失败记录数
-- **`success_records`**: 成功记录数组,每个对象包含完整的名片信息
-- **`failed_records`**: 失败记录数组,包含错误信息和输入数据
-- **`minio_md_path`**: 网页内容在MinIO中的存储路径
-
-#### 业务卡片记录字段说明
-每个成功创建的业务卡片记录包含以下字段:
-- **`id`**: 数据库记录ID
-- **`name_zh`**: 中文姓名
-- **`name_en`**: 英文姓名
-- **`title_zh`**: 中文职位
-- **`title_en`**: 英文职位
-- **`hotel_zh`**: 酒店中文名称
-- **`hotel_en`**: 酒店英文名称
-- **`mobile`**: 手机号码
-- **`email`**: 电子邮箱
-- **`phone`**: 固定电话
-- **`address_zh`**: 中文地址
-- **`address_en`**: 英文地址
-- **`postal_code_zh`**: 中文邮编
-- **`postal_code_en`**: 英文邮编
-- **`brand_zh`**: 品牌中文名称
-- **`brand_en`**: 品牌英文名称
-- **`affiliation_zh`**: 中文所属机构
-- **`affiliation_en`**: 英文所属机构
-- **`birthday`**: 生日 (YYYY-MM-DD格式)
-- **`age`**: 年龄
-- **`native_place`**: 籍贯
-- **`residence`**: 居住地
-- **`brand_group`**: 品牌组合
-- **`image_path`**: 人才照片在MinIO中的存储路径(如果提供了pic_url)
-- **`career_path`**: 职业轨迹数组,包含职位变化历史
-- **`talent_profile`**: 人才档案信息
-- **`origin_source`**: 数据来源信息,包含以下子字段:
-  - **`type`**: 数据来源类型,固定为"webpage_talent"
-  - **`minio_path`**: 原始网页内容在MinIO中的存储路径
-  - **`source_date`**: 数据处理时间
-- **`created_at`**: 创建时间
-- **`updated_at`**: 更新时间
-- **`updated_by`**: 更新人
-- **`status`**: 记录状态 (通常为"active")
-
----
-
-## 返回状态码
-
-| 状态码 | 说明 | 场景 |
-|--------|------|------|
-| **200** | 成功 | 所有记录处理成功 |
-| **206** | 部分成功 | 部分记录处理成功 |
-| **400** | 请求参数错误 | 参数格式不正确或缺少必填参数 |
-| **500** | 服务器内部错误 | 系统异常或数据库错误 |
-
----
-
-## 前端代码示例
-
-### JavaScript (原生)
-
-```javascript
-// 发送请求函数
-async function addWebpageTalent(talentList, webMd) {
-    const url = '/api/data-parse/add-webpage-talent';
-    
-    const requestBody = {
-        talent_list: talentList,
-        web_md: webMd
-    };
-    
-    try {
-        const response = await fetch(url, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-                // 如果需要认证,添加相应的头部
-                // 'Authorization': 'Bearer your-token'
-            },
-            body: JSON.stringify(requestBody)
-        });
-        
-        const result = await response.json();
-        
-        if (result.success) {
-            console.log('处理成功:', result.message);
-            console.log('处理详情:', result.data);
-            
-                          // 处理成功记录 - 区分全部成功和部分成功的响应格式
-             if (result.code === 200) {
-                 // 全部成功:data是业务卡片记录数组
-                 console.log(`成功处理 ${result.data.length} 条记录`);
-                 result.data.forEach((record, index) => {
-                     console.log(`记录 ${index + 1}: ${record.name_zh}`);
-                     
-                     // 显示图片路径信息
-                     if (record.image_path) {
-                         console.log(`  图片路径: ${record.image_path}`);
-                     }
-                     
-                     // 显示人才档案信息
-                     if (record.talent_profile) {
-                         console.log(`  人才档案: ${record.talent_profile.substring(0, 50)}...`);
-                     }
-                     
-                     // 显示数据来源信息
-                     if (record.origin_source) {
-                         console.log(`  数据来源: ${record.origin_source.type}`);
-                         console.log(`  原始文档: ${record.origin_source.minio_path}`);
-                     }
-                 });
-             } else if (result.code === 206) {
-                 // 部分成功:data包含详细的成功和失败信息
-                 console.log(`部分成功:${result.data.success_count}/${result.data.total_count} 条记录成功`);
-                 
-                 // 显示成功记录
-                 if (result.data.success_records.length > 0) {
-                     result.data.success_records.forEach((record, index) => {
-                         console.log(`成功记录 ${record.index}: ${record.data.name_zh} - ${record.message}`);
-                         if (record.data.image_path) {
-                             console.log(`  图片路径: ${record.data.image_path}`);
-                         }
-                     });
-                 }
-                 
-                 // 显示失败记录
-                 if (result.data.failed_records.length > 0) {
-                     result.data.failed_records.forEach((record, index) => {
-                         console.error(`失败记录 ${record.index}: ${record.error}`);
-                     });
-                 }
-             }
-            
-            return result;
-        } else {
-            console.error('处理失败:', result.message);
-            throw new Error(result.message);
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        throw error;
-    }
-}
-
-// 使用示例
-const talentData = [
-    {
-        name_zh: "张三",
-        name_en: "Zhang San",
-        title_zh: "总经理",
-        title_en: "General Manager",
-        hotel_zh: "北京万豪酒店",
-        hotel_en: "Beijing Marriott Hotel",
-        brand_group: "万豪",
-        mobile: "13800138000",
-        email: "zhangsan@example.com",
-        pic_url: "https://example.com/photos/zhangsan.jpg",
-        talent_profile: "资深酒店管理专家,拥有20年行业经验,曾在多家国际知名酒店担任高级管理职位。精通酒店运营管理、客户服务、团队建设等多个领域。"
-    },
-    {
-        name_zh: "李四",
-        name_en: "Li Si",
-        title_zh: "副总经理",
-        title_en: "Deputy General Manager",
-        hotel_zh: "上海希尔顿酒店",
-        hotel_en: "Shanghai Hilton Hotel",
-        brand_group: "希尔顿",
-        mobile: "13900139000",
-        email: "lisi@example.com",
-        pic_url: "https://example.com/photos/lisi.jpg",
-        talent_profile: "酒店业资深管理者,专注于市场营销和品牌推广,具有丰富的国际酒店管理经验。"
-    }
-];
-
-const webContent = `
-# 酒店人事任命公告
-
-## 重要人事变动
-
-![张三照片](https://example.com/photos/zhangsan.jpg)
-
-1. **张三**被任命为北京万豪酒店总经理
-   - 手机:13800138000
-   - 邮箱:zhangsan@example.com
-   - 简介:资深酒店管理专家,拥有20年行业经验
-
-![李四照片](https://example.com/photos/lisi.jpg)
-
-2. **李四**被任命为上海希尔顿酒店副总经理
-   - 手机:13900139000
-   - 邮箱:lisi@example.com
-   - 简介:酒店业资深管理者,专注于市场营销和品牌推广
-`;
-
-// 调用函数
-addWebpageTalent(talentData, webContent)
-    .then(result => {
-        console.log('操作完成:', result);
-    })
-    .catch(error => {
-        console.error('操作失败:', error);
-    });
-```
-
-### jQuery
-
-```javascript
-function addWebpageTalent(talentList, webMd) {
-    return $.ajax({
-        url: '/api/data-parse/add-webpage-talent',
-        type: 'POST',
-        contentType: 'application/json',
-        data: JSON.stringify({
-            talent_list: talentList,
-            web_md: webMd
-        }),
-        success: function(result) {
-            if (result.success) {
-                console.log('处理成功:', result.message);
-                
-                                 // 显示处理结果 - 区分全部成功和部分成功的响应格式
-                 if (result.code === 200) {
-                     // 全部成功
-                     alert(`成功处理 ${result.data.length} 条记录`);
-                     
-                     // 显示详细信息
-                     result.data.forEach(record => {
-                         console.log(`人才: ${record.name_zh}`);
-                         if (record.image_path) {
-                             console.log(`图片已保存: ${record.image_path}`);
-                         }
-                         if (record.talent_profile) {
-                             console.log(`人才档案: ${record.talent_profile.substring(0, 50)}...`);
-                         }
-                     });
-                 } else if (result.code === 206) {
-                     // 部分成功
-                     alert(`部分成功: ${result.data.success_count}/${result.data.total_count} 条记录成功`);
-                     
-                     // 显示成功记录
-                     result.data.success_records.forEach(record => {
-                         console.log(`成功: ${record.data.name_zh}`);
-                         if (record.data.image_path) {
-                             console.log(`图片已保存: ${record.data.image_path}`);
-                         }
-                     });
-                     
-                     // 显示失败记录
-                     result.data.failed_records.forEach(record => {
-                         console.error(`失败: ${record.error}`);
-                     });
-                }
-                
-                if (result.data.failed_count > 0) {
-                    console.warn(`失败 ${result.data.failed_count} 条记录`);
-                    result.data.failed_records.forEach(record => {
-                        console.error(`记录 ${record.index}: ${record.error}`);
-                    });
-                }
-            } else {
-                alert('处理失败: ' + result.message);
-            }
-        },
-        error: function(xhr, status, error) {
-            console.error('请求失败:', error);
-            alert('请求失败: ' + error);
-        }
-    });
-}
-
-// 使用示例
-const talentData = [
-    {
-        name_zh: "张三",
-        title_zh: "总经理",
-        hotel_zh: "北京万豪酒店",
-        mobile: "13800138000",
-        pic_url: "https://example.com/photos/zhangsan.jpg",
-        talent_profile: "资深酒店管理专家,拥有20年行业经验..."
-    }
-];
-
-const webContent = "# 人事任命公告\n\n![张三照片](https://example.com/photos/zhangsan.jpg)\n\n张三被任命为总经理...";
-
-addWebpageTalent(talentData, webContent);
-```
-
-### Vue.js
-
-```vue
-<template>
-  <div>
-    <h3>添加网页人才信息</h3>
-    
-    <!-- 人才信息表单 -->
-    <div v-for="(talent, index) in talentList" :key="index" class="talent-form">
-      <h4>人才 {{ index + 1 }}</h4>
-      <div class="form-group">
-        <label>中文姓名 (必填):</label>
-        <input v-model="talent.name_zh" type="text" required>
-      </div>
-      <div class="form-group">
-        <label>英文姓名:</label>
-        <input v-model="talent.name_en" type="text">
-      </div>
-      <div class="form-group">
-        <label>中文职位:</label>
-        <input v-model="talent.title_zh" type="text">
-      </div>
-      <div class="form-group">
-        <label>酒店名称:</label>
-        <input v-model="talent.hotel_zh" type="text">
-      </div>
-      <div class="form-group">
-        <label>手机号码:</label>
-        <input v-model="talent.mobile" type="text">
-      </div>
-      <div class="form-group">
-        <label>电子邮箱:</label>
-        <input v-model="talent.email" type="email">
-      </div>
-      <div class="form-group">
-        <label>照片URL:</label>
-        <input v-model="talent.pic_url" type="url" placeholder="https://example.com/photo.jpg">
-      </div>
-      <div class="form-group">
-        <label>人才档案:</label>
-        <textarea v-model="talent.talent_profile" rows="4" cols="50" 
-                  placeholder="请输入人才的详细档案信息,包括工作经历、专业技能等..."></textarea>
-      </div>
-    </div>
-    
-    <!-- 网页内容 -->
-    <div class="form-group">
-      <label>网页内容 (Markdown格式):</label>
-      <textarea v-model="webMd" rows="10" cols="50" required></textarea>
-    </div>
-    
-    <!-- 操作按钮 -->
-    <div class="actions">
-      <button @click="addTalent">添加人才</button>
-      <button @click="submitData" :disabled="loading">
-        {{ loading ? '处理中...' : '提交数据' }}
-      </button>
-    </div>
-    
-    <!-- 结果显示 -->
-    <div v-if="result" class="result">
-      <h4>处理结果</h4>
-      <p :class="result.success ? 'success' : 'error'">{{ result.message }}</p>
-      
-              <div v-if="result.data">
-         <!-- 全部成功的显示 -->
-         <div v-if="result.code === 200">
-           <p>总记录数: {{ result.data.length }}</p>
-           <p>全部处理成功</p>
-           
-           <div class="success-records">
-             <h5>成功记录:</h5>
-             <ul>
-               <li v-for="(record, index) in result.data" :key="index">
-                 <strong>{{ record.name_zh }}</strong>
-                 <div v-if="record.image_path" class="detail">
-                   图片已保存: {{ record.image_path }}
-                 </div>
-                 <div v-if="record.talent_profile" class="detail">
-                   人才档案: {{ record.talent_profile.substring(0, 100) }}...
-                 </div>
-                 <div v-if="record.origin_source" class="detail">
-                   数据来源: {{ record.origin_source.type }} 
-                   ({{ record.origin_source.source_date }})
-                 </div>
-               </li>
-             </ul>
-           </div>
-         </div>
-         
-         <!-- 部分成功的显示 -->
-         <div v-if="result.code === 206">
-           <p>总记录数: {{ result.data.total_count }}</p>
-           <p>成功: {{ result.data.success_count }}</p>
-           <p>失败: {{ result.data.failed_count }}</p>
-           
-           <!-- 成功记录详情 -->
-           <div v-if="result.data.success_records.length > 0" class="success-records">
-             <h5>成功记录:</h5>
-             <ul>
-               <li v-for="record in result.data.success_records" :key="record.index">
-                 <strong>{{ record.data.name_zh }}</strong> - {{ record.message }}
-                 <div v-if="record.data.image_path" class="detail">
-                   图片已保存: {{ record.data.image_path }}
-                 </div>
-                 <div v-if="record.data.talent_profile" class="detail">
-                   人才档案: {{ record.data.talent_profile.substring(0, 100) }}...
-                 </div>
-                                    <div v-if="record.data.origin_source" class="detail">
-                     数据来源: {{ record.data.origin_source.type }} 
-                     ({{ record.data.origin_source.source_date }})
-                   </div>
-               </li>
-             </ul>
-           </div>
-           
-           <!-- 失败记录详情 -->
-           <div v-if="result.data.failed_records.length > 0" class="failed-records">
-             <h5>失败记录:</h5>
-             <ul>
-               <li v-for="record in result.data.failed_records" :key="record.index">
-                 记录 {{ record.index }}: {{ record.error }}
-               </li>
-             </ul>
-           </div>
-         </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'AddWebpageTalent',
-  data() {
-    return {
-      talentList: [
-        {
-          name_zh: '',
-          name_en: '',
-          title_zh: '',
-          title_en: '',
-          hotel_zh: '',
-          hotel_en: '',
-          brand_group: '',
-          mobile: '',
-          email: '',
-          phone: '',
-          address_zh: '',
-          address_en: '',
-          pic_url: '',
-          talent_profile: ''
-        }
-      ],
-      webMd: '',
-      loading: false,
-      result: null
-    };
-  },
-  methods: {
-    addTalent() {
-      this.talentList.push({
-        name_zh: '',
-        name_en: '',
-        title_zh: '',
-        title_en: '',
-        hotel_zh: '',
-        hotel_en: '',
-        brand_group: '',
-        mobile: '',
-        email: '',
-        phone: '',
-        address_zh: '',
-        address_en: '',
-        pic_url: '',
-        talent_profile: ''
-      });
-    },
-    
-    async submitData() {
-      if (!this.validateData()) {
-        return;
-      }
-      
-      this.loading = true;
-      this.result = null;
-      
-      try {
-        const response = await fetch('/api/data-parse/add-webpage-talent', {
-          method: 'POST',
-          headers: {
-            'Content-Type': 'application/json'
-          },
-          body: JSON.stringify({
-            talent_list: this.talentList,
-            web_md: this.webMd
-          })
-        });
-        
-        const result = await response.json();
-        this.result = result;
-        
-        if (result.success) {
-          this.$message.success(result.message);
-          
-                     // 显示详细处理结果
-           if (result.code === 200) {
-             console.log('成功处理的记录详情:');
-             result.data.forEach(record => {
-               console.log(`- ${record.name_zh}:`);
-               if (record.image_path) {
-                 console.log(`  图片: ${record.image_path}`);
-               }
-               if (record.talent_profile) {
-                 console.log(`  档案: ${record.talent_profile.substring(0, 50)}...`);
-               }
-             });
-           } else if (result.code === 206) {
-             console.log('部分成功的记录详情:');
-             result.data.success_records.forEach(record => {
-               console.log(`- ${record.data.name_zh}: ${record.message}`);
-               if (record.data.image_path) {
-                 console.log(`  图片: ${record.data.image_path}`);
-               }
-             });
-             
-             if (result.data.failed_records.length > 0) {
-               console.log('失败的记录详情:');
-               result.data.failed_records.forEach(record => {
-                 console.error(`- 记录 ${record.index}: ${record.error}`);
-               });
-             }
-           }
-        } else {
-          this.$message.error(result.message);
-        }
-      } catch (error) {
-        this.result = {
-          success: false,
-          message: '请求失败: ' + error.message
-        };
-        this.$message.error('请求失败: ' + error.message);
-      } finally {
-        this.loading = false;
-      }
-    },
-    
-    validateData() {
-      // 验证必填字段
-      for (let i = 0; i < this.talentList.length; i++) {
-        if (!this.talentList[i].name_zh.trim()) {
-          this.$message.error(`第 ${i + 1} 个人才的中文姓名不能为空`);
-          return false;
-        }
-      }
-      
-      if (!this.webMd.trim()) {
-        this.$message.error('网页内容不能为空');
-        return false;
-      }
-      
-      return true;
-    }
-  }
-};
-</script>
-
-<style scoped>
-.talent-form {
-  border: 1px solid #ddd;
-  padding: 15px;
-  margin: 10px 0;
-  border-radius: 5px;
-}
-
-.form-group {
-  margin: 10px 0;
-}
-
-.form-group label {
-  display: inline-block;
-  width: 120px;
-  font-weight: bold;
-}
-
-.form-group input,
-.form-group textarea {
-  width: 300px;
-  padding: 5px;
-  border: 1px solid #ccc;
-  border-radius: 3px;
-}
-
-.actions {
-  margin: 20px 0;
-}
-
-.actions button {
-  margin-right: 10px;
-  padding: 10px 20px;
-  background: #007bff;
-  color: white;
-  border: none;
-  border-radius: 5px;
-  cursor: pointer;
-}
-
-.actions button:disabled {
-  background: #6c757d;
-  cursor: not-allowed;
-}
-
-.result {
-  margin: 20px 0;
-  padding: 15px;
-  border: 1px solid #ddd;
-  border-radius: 5px;
-}
-
-.success {
-  color: #28a745;
-}
-
-.error {
-  color: #dc3545;
-}
-
-.success-records, .failed-records {
-  margin-top: 15px;
-}
-
-.detail {
-  font-size: 12px;
-  color: #666;
-  margin-left: 10px;
-}
-</style>
-```
-
----
-
-## 错误处理
-
-### 常见错误及解决方案
-
-#### 1. 参数验证错误
-**错误**: `talent_list参数必须是非空数组`
-**解决**: 确保talent_list是一个非空数组
-
-#### 2. 缺少必填字段
-**错误**: `第X个记录缺少name_zh字段`
-**解决**: 确保每个人才记录都包含name_zh字段
-
-#### 3. MinIO上传失败
-**错误**: `上传网页内容到MinIO失败`
-**解决**: 检查MinIO服务状态和配置
-
-#### 4. 图片下载失败
-**错误**: `人员XXX的图片处理失败`
-**解决**: 检查pic_url是否有效,网络连接是否正常。图片下载失败不会影响其他数据的处理
-
-#### 5. 数据库错误
-**错误**: `数据库操作失败`
-**解决**: 检查数据库连接和表结构
-
----
-
-## 数据字段详解
-
-### 新增字段说明
-
-#### `origin_source` (JSON类型)
-系统自动生成的数据来源信息,包含:
-- **`type`**: 数据来源类型,网页人才固定为"webpage_talent"
-- **`minio_path`**: 原始网页内容在MinIO中的存储路径
-- **`source_date`**: 数据处理时间
-
-#### `talent_profile` (文本类型)
-人才档案信息,可包含:
-- 个人简介和背景
-- 工作经历和职业发展
-- 专业技能和资质认证
-- 教育背景
-- 获奖情况和荣誉
-- 其他相关信息
-
-#### `image_path` (字符串类型)
-如果提供了`pic_url`,系统会:
-1. 自动下载图片
-2. 保存到MinIO的`talent_photos/`目录
-3. 生成唯一的文件名
-4. 将MinIO路径保存到此字段
-
----
-
-## 注意事项
-
-1. **数据完整性**: 确保talent_list中每个对象都包含name_zh字段
-2. **内容大小**: web_md内容不应过大,建议控制在合理范围内
-3. **并发处理**: 该接口支持批量处理,但建议控制单次请求的记录数量
-4. **重复检查**: 系统会自动检查重复记录并相应处理
-5. **存储路径**: 网页内容会自动保存到MinIO的`webpage_talent/`目录下
-6. **数据溯源**: origin_source字段保存了完整的数据来源信息,便于后续追溯
-7. **图片处理**: 
-   - 支持常见图片格式(JPG、PNG、GIF、WebP)
-   - 图片下载失败不会影响其他数据处理
-   - 图片文件名会包含人员姓名、时间戳和唯一标识
-8. **人才档案**: talent_profile字段支持存储大量文本信息,建议合理控制内容长度
-9. **自动字段**: origin_source字段由系统自动生成,无需在请求中提供
-10. **向后兼容**: 新增字段都是可选的,现有的API调用不会受到影响
-
----
-
-## 版本信息
-
-- **API版本**: v1.1
-- **文档版本**: 1.1.0
-- **创建日期**: 2025年
-- **最后更新**: 2025年07月10日
-- **更新内容**: 
-  - 新增 `origin_source` 字段支持
-  - 新增 `talent_profile` 字段支持
-  - 新增 `pic_url` 字段和图片自动下载功能
-  - 更新响应示例和代码示例
-  - 增加详细的字段说明和注意事项
-
----
-
-## 联系支持
-
-如果在使用过程中遇到问题,请联系技术支持团队。 

+ 0 - 430
docs/archive/get-parsed-talents-api-documentation.md

@@ -1,430 +0,0 @@
-# get-parsed-talents 接口使用说明书
-
-## 接口概述
-
-`get-parsed-talents` 接口用于获取解析人才记录列表,支持按状态过滤查询。该接口返回 `parsed_talents` 数据库表中的记录,按创建时间倒序排列。
-
-## 基本信息
-
-- **接口名称**: get-parsed-talents
-- **HTTP方法**: GET
-- **访问路径**: `/get-parsed-talents`
-- **接口描述**: 获取解析人才记录列表,支持状态过滤
-- **数据来源**: `parsed_talents` 数据库表
-
-## 请求参数
-
-### 查询参数 (Query Parameters)
-
-| 参数名 | 类型 | 必填 | 默认值 | 说明 |
-|--------|------|------|--------|------|
-| status | string | 否 | null | 状态过滤参数,如果为空则查询所有记录 |
-
-### 参数说明
-
-- **status**: 用于过滤特定状态的记录
-  - 可选值: `"待审核"`, `"已入库"`, `"已拒绝"`, `"active"`, `"inactive"` 等
-  - 如果参数为空或不传,则返回所有状态的记录
-  - 参数值不区分大小写,但建议使用标准状态值
-
-## 响应格式
-
-### 成功响应 (200 OK)
-
-```json
-{
-    "success": true,
-    "message": "成功获取 X 条解析人才记录",
-    "data": [
-        {
-            "id": 1,
-            "name_zh": "张三",
-            "name_en": "Zhang San",
-            "title_zh": "总经理",
-            "title_en": "General Manager",
-            "mobile": "13800138000",
-            "phone": "010-12345678",
-            "email": "zhangsan@example.com",
-            "hotel_zh": "北京希尔顿酒店",
-            "hotel_en": "Beijing Hilton Hotel",
-            "address_zh": "北京市朝阳区建国门外大街1号",
-            "address_en": "1 Jianguomenwai Street, Chaoyang District, Beijing",
-            "postal_code_zh": "100020",
-            "postal_code_en": "100020",
-            "brand_zh": "希尔顿",
-            "brand_en": "Hilton",
-            "affiliation_zh": "希尔顿酒店集团",
-            "affiliation_en": "Hilton Hotels & Resorts",
-            "image_path": "minio/talents/zhangsan.jpg",
-            "career_path": [
-                {
-                    "date": "2024-01-15",
-                    "hotel_zh": "北京希尔顿酒店",
-                    "hotel_en": "Beijing Hilton Hotel",
-                    "title_zh": "总经理",
-                    "title_en": "General Manager",
-                    "image_path": "minio/talents/zhangsan.jpg",
-                    "source": "webpage_extraction"
-                }
-            ],
-            "brand_group": "希尔顿集团",
-            "birthday": "1980-05-15",
-            "residence": "北京市朝阳区",
-            "age": 44,
-            "native_place": "北京市",
-            "origin_source": [
-                {
-                    "task_type": "webpage_talent",
-                    "minio_path": "minio/webpage/zhangsan.md",
-                    "source_date": "2024-01-15 14:30:25"
-                }
-            ],
-            "talent_profile": "资深酒店管理专家,拥有15年酒店管理经验...",
-            "task_id": "task_001",
-            "task_type": "webpage_talent",
-            "created_at": "2024-01-15T14:30:25",
-            "updated_at": "2024-01-15T14:30:25",
-            "updated_by": "system",
-            "status": "待审核"
-        }
-    ],
-    "count": 1
-}
-```
-
-### 错误响应 (500 Internal Server Error)
-
-```json
-{
-    "success": false,
-    "message": "获取解析人才记录失败: 数据库连接错误",
-    "data": [],
-    "count": 0
-}
-```
-
-## 响应字段说明
-
-### 顶层字段
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| success | boolean | 请求是否成功 |
-| message | string | 响应消息 |
-| data | array | 人才记录数组 |
-| count | integer | 记录总数 |
-
-### 人才记录字段 (data 数组中的对象)
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| id | integer | 记录唯一标识符 |
-| name_zh | string | 中文姓名 |
-| name_en | string | 英文姓名 |
-| title_zh | string | 中文职位 |
-| title_en | string | 英文职位 |
-| mobile | string | 手机号码 |
-| phone | string | 固定电话 |
-| email | string | 电子邮箱 |
-| hotel_zh | string | 中文酒店名称 |
-| hotel_en | string | 英文酒店名称 |
-| address_zh | string | 中文地址 |
-| address_en | string | 英文地址 |
-| postal_code_zh | string | 中文邮政编码 |
-| postal_code_en | string | 英文邮政编码 |
-| brand_zh | string | 中文品牌名称 |
-| brand_en | string | 英文品牌名称 |
-| affiliation_zh | string | 中文隶属关系 |
-| affiliation_en | string | 英文隶属关系 |
-| image_path | string | 图片文件路径 |
-| career_path | array | 职业轨迹数组 |
-| brand_group | string | 品牌组合 |
-| birthday | string | 生日 (YYYY-MM-DD格式) |
-| residence | string | 居住地 |
-| age | integer | 年龄 |
-| native_place | string | 籍贯 |
-| origin_source | array | 原始资料记录数组 |
-| talent_profile | string | 人才档案描述 |
-| task_id | string | 解析任务ID |
-| task_type | string | 解析任务类型 |
-| created_at | string | 创建时间 |
-| updated_at | string | 更新时间 |
-| updated_by | string | 更新人 |
-| status | string | 记录状态 |
-
-### career_path 数组字段
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| date | string | 职业记录日期 |
-| hotel_zh | string | 中文酒店名称 |
-| hotel_en | string | 英文酒店名称 |
-| title_zh | string | 中文职位 |
-| title_en | string | 英文职位 |
-| image_path | string | 相关图片路径 |
-| source | string | 数据来源 |
-
-### origin_source 数组字段
-
-| 字段名 | 类型 | 说明 |
-|--------|------|------|
-| task_type | string | 任务类型 |
-| minio_path | string | MinIO存储路径 |
-| source_date | string | 数据来源时间 |
-
-## 使用示例
-
-### 1. 查询所有记录
-
-**请求**:
-```
-GET /get-parsed-talents
-```
-
-**响应**:
-```json
-{
-    "success": true,
-    "message": "成功获取 150 条解析人才记录",
-    "data": [...],
-    "count": 150
-}
-```
-
-### 2. 查询待审核记录
-
-**请求**:
-```
-GET /get-parsed-talents?status=待审核
-```
-
-**响应**:
-```json
-{
-    "success": true,
-    "message": "成功获取 25 条解析人才记录",
-    "data": [...],
-    "count": 25
-}
-```
-
-### 3. 查询已入库记录
-
-**请求**:
-```
-GET /get-parsed-talents?status=已入库
-```
-
-**响应**:
-```json
-{
-    "success": true,
-    "message": "成功获取 120 条解析人才记录",
-    "data": [...],
-    "count": 120
-}
-```
-
-### 4. 查询已拒绝记录
-
-**请求**:
-```
-GET /get-parsed-talents?status=已拒绝
-```
-
-**响应**:
-```json
-{
-    "success": true,
-    "message": "成功获取 5 条解析人才记录",
-    "data": [...],
-    "count": 5
-}
-```
-
-## 前端集成示例
-
-### JavaScript (使用 fetch)
-
-```javascript
-// 获取所有记录
-async function getAllParsedTalents() {
-    try {
-        const response = await fetch('/get-parsed-talents');
-        const data = await response.json();
-        
-        if (data.success) {
-            console.log(`获取到 ${data.count} 条记录`);
-            return data.data;
-        } else {
-            console.error('获取失败:', data.message);
-            return [];
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        return [];
-    }
-}
-
-// 获取特定状态的记录
-async function getParsedTalentsByStatus(status) {
-    try {
-        const response = await fetch(`/get-parsed-talents?status=${encodeURIComponent(status)}`);
-        const data = await response.json();
-        
-        if (data.success) {
-            console.log(`获取到 ${data.count} 条 ${status} 状态的记录`);
-            return data.data;
-        } else {
-            console.error('获取失败:', data.message);
-            return [];
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        return [];
-    }
-}
-
-// 使用示例
-getAllParsedTalents().then(talents => {
-    console.log('所有人才记录:', talents);
-});
-
-getParsedTalentsByStatus('待审核').then(talents => {
-    console.log('待审核人才记录:', talents);
-});
-```
-
-### JavaScript (使用 axios)
-
-```javascript
-import axios from 'axios';
-
-// 获取所有记录
-async function getAllParsedTalents() {
-    try {
-        const response = await axios.get('/get-parsed-talents');
-        const { success, data, count, message } = response.data;
-        
-        if (success) {
-            console.log(`获取到 ${count} 条记录`);
-            return data;
-        } else {
-            console.error('获取失败:', message);
-            return [];
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        return [];
-    }
-}
-
-// 获取特定状态的记录
-async function getParsedTalentsByStatus(status) {
-    try {
-        const response = await axios.get('/get-parsed-talents', {
-            params: { status }
-        });
-        const { success, data, count, message } = response.data;
-        
-        if (success) {
-            console.log(`获取到 ${count} 条 ${status} 状态的记录`);
-            return data;
-        } else {
-            console.error('获取失败:', message);
-            return [];
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        return [];
-    }
-}
-```
-
-### React Hook 示例
-
-```javascript
-import { useState, useEffect } from 'react';
-
-function useParsedTalents(status = null) {
-    const [talents, setTalents] = useState([]);
-    const [loading, setLoading] = useState(false);
-    const [error, setError] = useState(null);
-
-    useEffect(() => {
-        const fetchTalents = async () => {
-            setLoading(true);
-            setError(null);
-            
-            try {
-                const url = status 
-                    ? `/get-parsed-talents?status=${encodeURIComponent(status)}`
-                    : '/get-parsed-talents';
-                    
-                const response = await fetch(url);
-                const data = await response.json();
-                
-                if (data.success) {
-                    setTalents(data.data);
-                } else {
-                    setError(data.message);
-                }
-            } catch (err) {
-                setError('请求失败: ' + err.message);
-            } finally {
-                setLoading(false);
-            }
-        };
-
-        fetchTalents();
-    }, [status]);
-
-    return { talents, loading, error };
-}
-
-// 使用示例
-function TalentList() {
-    const { talents, loading, error } = useParsedTalents('待审核');
-
-    if (loading) return <div>加载中...</div>;
-    if (error) return <div>错误: {error}</div>;
-
-    return (
-        <div>
-            <h2>待审核人才列表 ({talents.length})</h2>
-            {talents.map(talent => (
-                <div key={talent.id}>
-                    <h3>{talent.name_zh}</h3>
-                    <p>职位: {talent.title_zh}</p>
-                    <p>酒店: {talent.hotel_zh}</p>
-                    <p>状态: {talent.status}</p>
-                </div>
-            ))}
-        </div>
-    );
-}
-```
-
-## 注意事项
-
-1. **分页处理**: 当前接口返回所有匹配的记录,如果数据量很大,建议前端实现分页显示
-2. **状态值**: 建议使用标准状态值,如 `"待审核"`, `"已入库"`, `"已拒绝"`
-3. **错误处理**: 始终检查 `success` 字段,处理可能的错误情况
-4. **数据格式**: 日期时间字段使用 ISO 8601 格式
-5. **空值处理**: 某些字段可能为 `null` 或空字符串,前端需要适当处理
-6. **字符编码**: 中文字段使用 UTF-8 编码
-
-## 状态码说明
-
-| HTTP状态码 | 说明 |
-|-----------|------|
-| 200 | 请求成功 |
-| 500 | 服务器内部错误 |
-
-## 更新日志
-
-- **v1.0.0** (2024-01-15): 初始版本,支持基本查询和状态过滤功能
-
-## 联系方式
-
-如有问题或建议,请联系开发团队。 

+ 0 - 25
docs/archive/get_calendar_info.txt

@@ -1,25 +0,0 @@
-import requests
-
-# 174-日历 - 代码参考(根据实际业务情况修改)
-
-# 基本参数配置
-apiUrl = 'http://v.juhe.cn/laohuangli/d'  # 接口请求URL
-apiKey = '1573ead1bdc8af8d948660aaf9848c6e'  # 在个人中心->我的数据,接口名称上方查看
-
-# 接口请求入参配置
-requestParams = {
-    'key': apiKey,
-    'date': '20250824',
-}
-
-# 发起接口网络请求
-response = requests.get(apiUrl, params=requestParams)
-
-# 解析响应结果
-if response.status_code == 200:
-    responseResult = response.json()
-    # 网络请求成功。可依据业务逻辑和接口文档说明自行处理。
-    print(responseResult)
-else:
-    # 网络异常等因素,解析结果异常。可依据业务逻辑自行处理。
-    print('请求异常')   

+ 0 - 380
docs/archive/n8n_chat_trigger_error_diagnosis.md

@@ -1,380 +0,0 @@
-# n8n Chat Trigger Internal Server Error 深度诊断
-
-**错误**: Internal Server Error  
-**URL**: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn  
-**工作流**: Data-governance  
-**诊断时间**: 2025-11-04
-
----
-
-## 🔍 根本原因分析
-
-### 发现的关键问题
-
-#### 1. **Webhook未正确注册** ❌
-
-```json
-"hasWebhookTrigger": false,
-"webhookPath": null
-```
-
-**说明**: n8n 没有识别 Chat Trigger 为有效的 webhook 触发器,导致 `/chat/xxx` 路径无法访问。
-
-#### 2. **工作流结构问题** ⚠️
-
-**当前结构**:
-```
-Chat Trigger → AI Agent (已断开后续连接)
-```
-
-**问题**: AI Agent 虽然已经是终点,但系统消息中的换行符格式可能导致问题:
-- 使用了 `\\n\\n` (双重转义)
-- 应该使用 `\n` (单次转义)
-
-#### 3. **Chat Trigger 模式配置** ⚠️
-
-当前配置没有明确指定响应模式(`responseMode`),可能导致默认行为不正确。
-
----
-
-## ✅ 解决方案
-
-### 方案 1: 在 n8n 界面手动修复(推荐)⭐⭐⭐
-
-#### 步骤 1: 登录 n8n 界面
-```
-访问: https://n8n.citupro.com
-登录账号
-```
-
-#### 步骤 2: 打开工作流
-```
-1. 进入 Workflows 页面
-2. 找到 "Data-governance" 工作流
-3. 点击打开编辑器
-```
-
-#### 步骤 3: 简化工作流结构
-
-**删除不必要的节点**:
-1. 删除节点:
-   - "判断用户意图" (IF节点)
-   - "调用元数据新增API" (HTTP节点)
-   - "设置确认消息" (Set节点)
-   - "设置拒绝消息" (Set节点)
-
-2. 保留节点:
-   - Chat Trigger
-   - AI Agent  
-   - DeepSeek Chat Model
-
-3. 确保连接:
-   ```
-   Chat Trigger → AI Agent
-   DeepSeek Chat Model → AI Agent (语言模型连接)
-   ```
-
-#### 步骤 4: 修复 AI Agent 系统消息
-
-点击 AI Agent 节点,修改系统消息,**删除多余的换行符转义**:
-
-**修改前**(有问题):
-```
-好的!已为您发起元数据新增工作流程。\\n\\n操作结果:成功创建元数据\\n\\n...
-```
-
-**修改后**(正确):
-```
-你是一个数据治理助手。你的任务是判断用户是否需要进行元数据管理。
-
-规则:
-1. 如果用户明确表示同意或需要(如:是、好的、可以、需要、确认等),你必须回复:
-好的!已为您发起元数据新增工作流程。
-
-操作结果:成功创建元数据
-
-如需继续其他数据治理操作,请告诉我。
-
-2. 如果用户明确表示拒绝或不需要(如:否、不用、取消、不需要等),你必须回复:
-好的,已取消元数据管理操作。
-
-还有其他需要帮助的吗?
-- 数据标准制定
-- 数据质量检查
-- 其他数据治理服务
-
-3. 如果用户的意图不明确,友好地询问用户:"请明确回答是否需要进行元数据管理(是/否)?"
-```
-
-#### 步骤 5: 配置 Chat Trigger 响应模式
-
-1. 点击 Chat Trigger 节点
-2. 展开 "Options"
-3. 找到或添加 "Response Mode" 选项
-4. 选择 **"When Last Node Finishes"** 或 **"Streaming"**
-   - **When Last Node Finishes**: 等待整个工作流完成后返回
-   - **Streaming**: 流式返回(推荐,体验更好)
-
-#### 步骤 6: 停用并重新激活工作流
-
-```
-1. 点击右上角的 "Active" 开关关闭工作流
-2. 等待 2-3 秒
-3. 再次点击 "Active" 开关激活工作流
-4. 确认工作流状态变为绿色(已激活)
-```
-
-#### 步骤 7: 测试
-
-```
-访问: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-```
-
----
-
-### 方案 2: 创建全新的简化工作流 ⭐⭐
-
-如果方案 1 不work,创建一个全新的简化工作流:
-
-#### 新工作流结构
-
-```yaml
-节点:
-  1. Chat Trigger:
-     - public: true
-     - title: "数据治理助手 🤖"
-     - welcomeMessage: "您好!我是数据治理助手..."
-     - responseMode: "lastNode"  # 或 "streaming"
-  
-  2. DeepSeek Chat Model:
-     - model: "deepseek-chat"
-     - temperature: 0.7
-     - maxTokens: 500
-     - credentials: DeepSeek account
-  
-  3. AI Agent:
-     - promptType: "auto"
-     - systemMessage: "简洁的系统消息(无转义问题)"
-
-连接:
-  Chat Trigger → AI Agent
-  DeepSeek Chat Model → AI Agent (ai_languageModel)
-```
-
-#### 创建步骤
-
-1. 在 n8n 界面点击 "+" 创建新工作流
-2. 添加 "Chat Trigger" 节点
-3. 添加 "DeepSeek Chat Model" 节点
-4. 添加 "AI Agent" 节点
-5. 连接节点(重要:DeepSeek → AI Agent 需要连接到 ai_languageModel 端口)
-6. 配置各节点参数
-7. 保存并激活
-
----
-
-### 方案 3: 使用 Webhook + 基础 AI 聊天 ⭐
-
-如果 Chat Trigger 持续有问题,可以使用 Webhook 替代:
-
-#### 工作流结构
-
-```
-Webhook → AI Agent → Respond to Webhook
-```
-
-#### 优点
-- 更简单
-- 更可控
-- 容易调试
-
-#### 缺点
-- 没有内置的聊天界面
-- 需要自己构建前端
-
----
-
-## 🧪 调试检查清单
-
-### 在 n8n 界面中检查
-
-- [ ] 工作流已保存
-- [ ] 工作流已激活(绿色开关)
-- [ ] Chat Trigger 节点配置完整
-- [ ] AI Agent 有系统消息
-- [ ] DeepSeek Chat Model 已连接凭证
-- [ ] DeepSeek → AI Agent 连接到 ai_languageModel 端口
-- [ ] AI Agent 是工作流的最后一个节点(没有后续main连接)
-- [ ] 系统消息中没有双重转义(`\\n` 应该是 `\n`)
-
-### 使用 n8n 调试工具
-
-1. **测试 Chat URL**:
-   ```
-   在 Chat Trigger 节点面板中查看 Production URL
-   复制 URL 并在浏览器中打开
-   ```
-
-2. **查看执行日志**:
-   ```
-   n8n界面 → Executions → 查看最近的执行
-   检查是否有错误消息
-   ```
-
-3. **手动执行测试**:
-   ```
-   点击 "Test workflow"
-   输入测试数据
-   观察每个节点的输出
-   ```
-
----
-
-## 📊 常见 Chat Trigger 问题
-
-### 问题 1: Chat URL 返回 404
-
-**原因**: 工作流未激活或 Webhook 未注册  
-**解决**: 停用再重新激活工作流
-
-### 问题 2: Chat URL 返回 500 (Internal Server Error)
-
-**原因**: 
-- AI Agent 配置错误
-- 语言模型连接问题
-- 系统消息格式错误
-- 凭证未配置
-
-**解决**: 
-1. 简化系统消息
-2. 检查凭证
-3. 确认DeepSeek API可访问
-4. 移除多余的节点
-
-### 问题 3: 聊天界面加载但无响应
-
-**原因**: AI Agent 执行超时或错误  
-**解决**: 
-1. 检查 DeepSeek API 额度
-2. 减少 maxTokens
-3. 简化系统消息
-4. 查看执行日志
-
-### 问题 4: 响应格式不正确
-
-**原因**: 
-- 响应模式配置不当
-- 工作流有未连接的节点
-
-**解决**:
-1. 确保 AI Agent 是最后一个节点
-2. 配置正确的 responseMode
-3. 移除断开连接的节点
-
----
-
-## 🔧 紧急修复方案
-
-如果以上方案都不行,使用这个最简单的工作流:
-
-### 最小可行工作流
-
-```json
-{
-  "nodes": [
-    {
-      "name": "Chat Trigger",
-      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
-      "parameters": {
-        "public": true,
-        "options": {
-          "title": "测试聊天",
-          "responseMode": "lastNode"
-        }
-      }
-    },
-    {
-      "name": "AI Agent",
-      "type": "@n8n/n8n-nodes-langchain.agent",
-      "parameters": {
-        "promptType": "auto",
-        "options": {
-          "systemMessage": "你是一个友好的助手。"
-        }
-      }
-    },
-    {
-      "name": "DeepSeek",
-      "type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
-      "parameters": {
-        "model": "deepseek-chat"
-      },
-      "credentials": {
-        "deepSeekApi": "你的凭证"
-      }
-    }
-  ],
-  "connections": {
-    "Chat Trigger": {"main": [[{"node": "AI Agent"}]]},
-    "DeepSeek": {"ai_languageModel": [[{"node": "AI Agent", "type": "ai_languageModel"}]]}
-  }
-}
-```
-
-**这个配置应该 100% 可以工作。**
-
----
-
-## 📞 需要帮助?
-
-### 检查 n8n 服务器日志
-
-```bash
-# 如果使用 Docker
-docker logs -f n8n-container | grep -i "error\|chat\|webhook"
-
-# 如果使用 PM2
-pm2 logs n8n | grep -i "error\|chat\|webhook"
-```
-
-### 常见日志错误信息
-
-| 错误 | 原因 | 解决 |
-|------|------|------|
-| "No webhook handler found" | Webhook 未注册 | 重新激活工作流 |
-| "AI Agent execution failed" | DeepSeek API 错误 | 检查凭证和额度 |
-| "TypeError: Cannot read property" | 节点配置缺失 | 检查所有必需参数 |
-| "Timeout" | AI 响应超时 | 减少 maxTokens 或简化提示 |
-
----
-
-## ✅ 推荐操作顺序
-
-1. **立即尝试**: 在 n8n 界面手动修复(方案 1)
-2. **如果失败**: 创建新的简化工作流(方案 2)
-3. **最后手段**: 使用最小可行工作流配置
-4. **调试**: 查看 n8n 服务器日志
-5. **联系支持**: 如果以上都不行
-
-**关键**: Chat Trigger + AI Agent 的工作流必须保持简单,AI Agent 必须是最后一个节点。
-
----
-
-## 🎯 成功标志
-
-修复成功后,您应该看到:
-
-✅ 访问 URL 不再显示 Internal Server Error  
-✅ 看到聊天界面和欢迎消息  
-✅ 可以输入消息  
-✅ AI 正常响应  
-✅ n8n 执行日志显示成功  
-
-祝您修复顺利!🚀
-
-
-
-
-
-
-

+ 0 - 381
docs/archive/n8n_internal_error_fix.md

@@ -1,381 +0,0 @@
-# n8n 工作流 Internal Server Error 修复报告
-
-**问题时间**: 2025-11-04  
-**工作流 ID**: tWfjLZE1FmMfQAIn  
-**错误信息**: Internal Server Error  
-**访问地址**: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-
----
-
-## 🔍 问题分析
-
-### 发现的问题
-
-#### 1. **Set 节点配置不完整** ❌
-
-在执行日志中发现两个 Set 节点配置异常:
-
-**问题节点**:
-- `设置确认消息` - `assignments: {}` (空配置)
-- `设置拒绝消息` - `assignments: {}` (空配置)
-
-**影响**:
-- 这两个节点无法正确设置输出数据
-- 导致工作流执行失败
-- Chat 界面报 Internal Server Error
-
-#### 2. **工作流配置问题**
-
-```json
-{
-  "parameters": {
-    "mode": "manual",
-    "duplicateItem": false,
-    "assignments": {},  // ❌ 空配置
-    "includeOtherFields": false,
-    "options": {}
-  }
-}
-```
-
-**正确的配置应该是**:
-```json
-{
-  "parameters": {
-    "mode": "manual",
-    "fields": {
-      "values": [
-        {
-          "name": "output",
-          "type": "string",
-          "stringValue": "消息内容..."
-        }
-      ]
-    },
-    "options": {}
-  }
-}
-```
-
----
-
-## ✅ 修复方案
-
-### 已执行的修复
-
-#### 1. 更新 "设置确认消息" 节点
-
-```json
-{
-  "type": "updateNode",
-  "nodeName": "设置确认消息",
-  "updates": {
-    "parameters": {
-      "mode": "manual",
-      "fields": {
-        "values": [
-          {
-            "name": "output",
-            "type": "string",
-            "stringValue": "好的!已为您发起元数据新增工作流程。\n\n操作结果:成功创建元数据\n\n如需继续其他数据治理操作,请告诉我。"
-          }
-        ]
-      },
-      "options": {}
-    }
-  }
-}
-```
-
-#### 2. 更新 "设置拒绝消息" 节点
-
-```json
-{
-  "type": "updateNode",
-  "nodeName": "设置拒绝消息",
-  "updates": {
-    "parameters": {
-      "mode": "manual",
-      "fields": {
-        "values": [
-          {
-            "name": "output",
-            "type": "string",
-            "stringValue": "好的,已取消元数据管理操作。\n\n还有其他需要帮助的吗?\n- 数据标准制定\n- 数据质量检查\n- 其他数据治理服务"
-          }
-        ]
-      },
-      "options": {}
-    }
-  }
-}
-```
-
----
-
-## 📊 修复后的状态
-
-### 工作流验证结果
-
-✅ **工作流有效**: `valid: true`  
-✅ **总节点数**: 7  
-✅ **有效连接**: 6  
-✅ **无效连接**: 0  
-✅ **错误数**: 0  
-⚠️ **警告数**: 12 (大部分是版本过时警告,不影响功能)
-
-### 节点配置状态
-
-| 节点 | 状态 | 说明 |
-|------|------|------|
-| Chat Trigger | ✅ 正常 | Webhook 已注册 |
-| AI Agent | ✅ 正常 | 系统消息已配置 |
-| DeepSeek Chat Model | ✅ 正常 | 凭证已连接 |
-| 判断用户意图 | ✅ 正常 | IF 条件正确 |
-| 调用元数据新增API | ✅ 正常 | HTTP 请求配置正确 |
-| 设置确认消息 | ✅ 已修复 | 输出字段已配置 |
-| 设置拒绝消息 | ✅ 已修复 | 输出字段已配置 |
-
----
-
-## 🧪 测试建议
-
-### 1. 验证修复
-
-**立即测试步骤**:
-
-```bash
-1. 访问 Chat URL
-   https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-
-2. 检查是否还有 Internal Server Error
-   - 如果没有错误,进入下一步
-   - 如果仍有错误,查看 n8n 服务器日志
-
-3. 测试确认流程
-   输入: "是"
-   预期: 看到确认消息和成功信息
-
-4. 测试拒绝流程
-   输入: "否"
-   预期: 看到拒绝消息和选项列表
-
-5. 查看执行日志
-   在 n8n 界面 → Executions → 查看最新执行
-   检查所有节点是否正常执行
-```
-
-### 2. 检查执行日志
-
-访问 n8n 界面的 Executions 页面:
-```
-https://n8n.citupro.com/workflows/tWfjLZE1FmMfQAIn/executions
-```
-
-检查项:
-- [ ] Chat Trigger 正常触发
-- [ ] AI Agent 正常响应
-- [ ] DeepSeek API 调用成功
-- [ ] IF 节点正确判断
-- [ ] Set 节点输出正确
-- [ ] 消息正确返回到聊天界面
-
----
-
-## 🔧 其他潜在问题
-
-### 1. DeepSeek API 可能的问题
-
-虽然凭证已配置,但可能存在的问题:
-
-**检查清单**:
-- [ ] DeepSeek API Key 是否有效
-- [ ] API Key 是否有足够的额度
-- [ ] 网络连接是否正常
-- [ ] 是否有防火墙限制
-
-**测试方法**:
-```bash
-# 在 n8n 服务器上测试 DeepSeek API
-curl -X POST https://api.deepseek.com/v1/chat/completions \
-  -H "Authorization: Bearer YOUR_API_KEY" \
-  -H "Content-Type: application/json" \
-  -d '{
-    "model": "deepseek-chat",
-    "messages": [{"role": "user", "content": "测试"}],
-    "max_tokens": 100
-  }'
-```
-
-### 2. Chat Trigger 响应模式
-
-**当前配置**: 未明确指定响应模式
-
-**建议配置**:
-```json
-{
-  "options": {
-    "responseMode": "lastNode"  // 或 "streaming"
-  }
-}
-```
-
-**说明**:
-- `lastNode`: 等待整个工作流执行完成后返回最后一个节点的输出
-- `streaming`: 流式返回 AI 响应(推荐,用户体验更好)
-
-### 3. AI Agent 输出格式
-
-**当前问题**: AI Agent 输出可能不是标准格式
-
-**建议修改 IF 节点条件**:
-
-当前:
-```javascript
-={{ $json.output }}
-```
-
-可能需要改为:
-```javascript
-={{ $json.text || $json.output || $json.content }}
-```
-
----
-
-## 📋 完整测试清单
-
-### 测试前检查
-
-- [x] Set 节点配置已修复
-- [x] 工作流已保存
-- [x] 工作流已激活
-- [ ] n8n 服务器正常运行
-- [ ] DeepSeek API 可访问
-
-### 功能测试
-
-- [ ] Chat 界面可以打开(无 Internal Server Error)
-- [ ] 欢迎消息正常显示
-- [ ] 可以输入消息
-- [ ] 输入 "是" 能看到确认消息
-- [ ] 输入 "否" 能看到拒绝消息
-- [ ] AI 响应时间合理(< 5秒)
-
-### 执行日志检查
-
-- [ ] Chat Trigger 执行成功
-- [ ] AI Agent 执行成功(无 DeepSeek API 错误)
-- [ ] IF 节点正确判断
-- [ ] Set 节点输出正确(不是空对象)
-- [ ] 整个工作流执行成功
-
----
-
-## 🚨 如果问题仍然存在
-
-### 调试步骤
-
-#### 1. 查看 n8n 服务器日志
-
-```bash
-# 如果使用 Docker
-docker logs n8n-container
-
-# 如果使用 PM2
-pm2 logs n8n
-
-# 查找错误关键字
-grep -i "error\|exception\|failed" n8n.log
-```
-
-#### 2. 检查 DeepSeek API
-
-在 n8n 界面中:
-```
-1. 进入 Settings → Credentials
-2. 找到 DeepSeek API 凭证
-3. 点击 "Test" 按钮测试连接
-4. 查看是否有错误消息
-```
-
-#### 3. 简化工作流测试
-
-创建一个简化版本测试:
-```
-Chat Trigger → Set (静态消息) → 返回
-```
-
-如果这个可以工作,说明问题在 AI Agent 或 DeepSeek 配置。
-
-#### 4. 检查 AI Agent 输出
-
-在 n8n 界面中:
-```
-1. 手动执行工作流
-2. 输入测试数据到 Chat Trigger
-3. 查看 AI Agent 节点的输出
-4. 确认输出格式是否包含 "output" 字段
-```
-
----
-
-## 📄 相关文档
-
-| 文档 | 路径 | 说明 |
-|------|------|------|
-| 测试报告 | `docs/n8n_workflow_test_report.md` | 完整测试指南 |
-| DeepSeek 升级 | `docs/n8n_deepseek_upgrade.md` | DeepSeek 配置说明 |
-| 快速开始 | `docs/n8n_chat_workflow_quickstart.md` | 快速启动指南 |
-
----
-
-## ✅ 修复总结
-
-### 已完成
-
-- ✅ 识别了 Set 节点配置问题
-- ✅ 修复了 "设置确认消息" 节点
-- ✅ 修复了 "设置拒绝消息" 节点
-- ✅ 工作流验证通过
-- ✅ 更新时间:2025-11-04 07:23:44
-
-### 下一步
-
-1. **立即测试** ⭐⭐⭐
-   ```
-   访问: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-   验证: 是否还有 Internal Server Error
-   ```
-
-2. **功能测试** ⭐⭐
-   ```
-   输入: "是" 和 "否"
-   验证: 消息是否正确返回
-   ```
-
-3. **查看日志** ⭐
-   ```
-   n8n Executions → 查看最新执行
-   确认: 所有节点执行成功
-   ```
-
----
-
-## 🎊 预期结果
-
-修复后,访问 `https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn` 应该:
-
-✅ 不再显示 Internal Server Error  
-✅ 看到聊天界面和欢迎消息  
-✅ 可以正常输入和发送消息  
-✅ AI 能正确理解用户意图  
-✅ 返回正确的确认或拒绝消息  
-
-**现在请重新访问 Chat URL 测试!** 🚀
-
-
-
-
-
-
-

+ 0 - 314
docs/archive/n8n_tools_added_status.md

@@ -1,314 +0,0 @@
-# Data-governance 工作流 Tools 添加状态报告
-
-**完成时间**: 2025-11-04 17:30  
-**工作流ID**: `tWfjLZE1FmMfQAIn`
-
----
-
-## ✅ 已完成的工作
-
-### 1. 成功添加了两个 HTTP Request Tool 节点
-
-#### 检查元数据工具 ✅
-- **节点名称**: 检查元数据工具
-- **节点类型**: `@n8n/n8n-nodes-langchain.toolHttpRequest`
-- **配置**:
-  ```
-  Description: 检查元数据中文名是否已经存在。需要参数:name_zh(元数据中文名)。返回exists字段表示是否存在(true/false)
-  Method: GET
-  URL: http://192.168.3.143:5000/api/meta/check?name_zh={name_zh}
-  Authentication: None
-  Placeholder: name_zh (元数据中文名, type: string)
-  ```
-- **位置**: (680, 250)
-- **连接**: 已连接到 AI Agent
-
-#### 创建元数据工具 ✅
-- **节点名称**: 创建元数据工具
-- **节点类型**: `@n8n/n8n-nodes-langchain.toolHttpRequest`
-- **配置**:
-  ```
-  Description: 创建新的元数据。需要参数:name_zh(中文名,必填), data_type(数据类型,默认string), description(描述信息,选填)。返回创建结果
-  Method: POST
-  URL: http://192.168.3.143:5000/api/meta/node/add
-  Authentication: None
-  Send Body: true
-  Body Type: JSON
-  JSON Body: {
-    "name_zh": "{name_zh}",
-    "data_type": "{data_type}",
-    "describe": "{description}",
-    "source": "data-governance-workflow",
-    "status": true
-  }
-  Placeholders: 
-    - name_zh (元数据中文名,必填, type: string)
-    - data_type (数据类型,默认string, type: string)
-    - description (描述信息,选填, type: string)
-  ```
-- **位置**: (680, 450)
-- **连接**: 已连接到 AI Agent
-
----
-
-## 📊 工作流验证结果
-
-### 验证状态: ✅ VALID
-
-- **总节点数**: 9
-- **已启用节点**: 9
-- **触发器节点**: 1
-- **有效连接**: 7
-- **无效连接**: 0
-- **错误数**: 0
-- **警告数**: 15
-
-### 工作流结构
-
-```
-Chat Trigger (聊天触发器)
-    ↓
-AI Agent (AI代理) ←── DeepSeek Chat Model (语言模型)
-    ↑                   ↑
-    |                   |
-    ├── 检查元数据工具 (HTTP Request Tool)
-    └── 创建元数据工具 (HTTP Request Tool)
-    
-(还有一些旧节点未清理:判断用户意图、调用元数据新增API、设置确认消息、设置拒绝消息)
-```
-
----
-
-## ⚠️ 发现的问题
-
-### 问题 1: Internal Server Error (聊天界面)
-
-**症状**: 访问 `https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn` 返回 Internal Server Error
-
-**可能原因**:
-1. Chat Trigger 仍然无法识别正确的 webhook 路径
-2. AI Agent 后面可能还有未清理的节点连接
-3. Tools 的连接类型可能不正确
-
-**警告信息**:
-- "AI Agent has no ai_tool connections. Consider adding tools to enhance the agent's capabilities."
-- 这表明虽然工具已添加,但可能没有通过正确的 `ai_tool` 连接类型连接
-
-### 问题 2: 旧节点未清理
-
-工作流中仍然保留了以下旧节点(这些节点已不需要):
-- 判断用户意图 (IF节点)
-- 调用元数据新增API (HTTP Request节点)
-- 设置确认消息 (Set节点)
-- 设置拒绝消息 (Set节点)
-
-这些节点虽然不会影响功能,但会让工作流变得复杂。
-
----
-
-## 🔧 推荐的下一步操作
-
-### 方案 1: 在 n8n 界面手动检查和修复(推荐)⭐⭐⭐
-
-#### 步骤 1: 检查工具连接
-
-1. 登录 n8n 界面:https://n8n.citupro.com
-2. 打开 Data-governance 工作流
-3. 检查两个工具节点的连接:
-   - 点击连接线查看连接类型
-   - 应该显示为 `ai_tool` 而不是 `main`
-   - 如果不是,重新连接:
-     - 删除现有连接
-     - 从工具节点拖线到 AI Agent
-     - 在弹出菜单中选择 `ai_tool` 连接类型
-
-#### 步骤 2: 清理旧节点(可选)
-
-删除以下不再需要的节点:
-- 判断用户意图
-- 调用元数据新增API
-- 设置确认消息
-- 设置拒绝消息
-
-保留核心节点:
-- Chat Trigger
-- AI Agent
-- DeepSeek Chat Model
-- 检查元数据工具
-- 创建元数据工具
-
-#### 步骤 3: 重新激活工作流
-
-1. 停用工作流(关闭 Active 开关)
-2. 等待 2-3 秒
-3. 重新激活(打开 Active 开关)
-4. 确认工作流状态为激活
-
-#### 步骤 4: 测试
-
-访问:https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-
-测试对话:
-```
-用户: "是,我要创建元数据"
-预期: AI 询问元数据信息
-
-用户: "中文名:测试字段123,类型:string,描述:这是测试"
-预期: AI 检查后创建元数据
-```
-
----
-
-### 方案 2: 创建新的简化工作流(如果方案1失败)⭐⭐
-
-如果上述方案无法解决问题,建议创建一个全新的简化工作流:
-
-#### 最小化工作流结构
-
-```yaml
-节点:
-  1. Chat Trigger
-     - public: true
-     - title: "数据治理助手 🤖"
-     - responseMode: "lastNode"
-  
-  2. DeepSeek Chat Model
-     - model: "deepseek-chat"
-     - temperature: 0.7
-     - credentials: DeepSeek account
-  
-  3. AI Agent
-     - systemMessage: [当前配置]
-  
-  4. 检查元数据工具 (HTTP Request Tool)
-     - [当前配置]
-  
-  5. 创建元数据工具 (HTTP Request Tool)
-     - [当前配置]
-
-连接:
-  Chat Trigger → AI Agent (main)
-  DeepSeek → AI Agent (ai_languageModel)
-  检查元数据工具 → AI Agent (ai_tool) ⚠️ 重要
-  创建元数据工具 → AI Agent (ai_tool) ⚠️ 重要
-```
-
----
-
-## 📝 技术说明
-
-### LangChain Tools 的连接方式
-
-在 n8n 中,LangChain Tools 必须通过特殊的 `ai_tool` 连接类型连接到 AI Agent:
-
-**正确连接**:
-```
-Tool → AI Agent (连接类型: ai_tool)
-```
-
-**错误连接**:
-```
-Tool → AI Agent (连接类型: main)  ❌
-```
-
-### ai_tool 连接的特点
-
-1. **不是数据流**: `ai_tool` 连接不传递数据流,而是告诉 AI Agent "这个工具可用"
-2. **AI 自动调用**: AI Agent 根据对话内容自动决定何时调用哪个工具
-3. **参数提取**: AI 从对话中提取工具需要的参数
-4. **结果反馈**: 工具执行结果会自动反馈给 AI Agent
-
-### 为什么 MCP API 可能无法正确建立 ai_tool 连接
-
-n8n 的 MCP API 在处理 LangChain 特殊连接类型时存在限制:
-- API 可能将 `ai_tool` 连接识别为 `main` 连接
-- 图形界面可以正确识别和建立 `ai_tool` 连接
-- 因此手动操作更可靠
-
----
-
-## ✅ 当前状态总结
-
-### 已完成 ✅
-- [x] 后端 API `/api/meta/check` 已创建
-- [x] AI Agent 系统消息已更新
-- [x] 检查元数据工具已添加
-- [x] 创建元数据工具已添加
-- [x] 工具已连接到 AI Agent
-
-### 待确认 ⏳
-- [ ] 工具连接类型是否为 `ai_tool`
-- [ ] Chat Trigger 是否能正常工作
-- [ ] AI Agent 是否能调用工具
-
-### 待操作 📋
-- [ ] 在 n8n 界面检查工具连接类型
-- [ ] 如需要,重新建立 `ai_tool` 连接
-- [ ] 清理旧节点(可选)
-- [ ] 重新激活工作流
-- [ ] 测试完整流程
-
----
-
-## 📞 需要帮助?
-
-### 检查连接类型的方法
-
-1. 在 n8n 界面打开工作流
-2. 点击工具节点和 AI Agent 之间的连接线
-3. 查看连接信息面板
-4. 确认连接类型显示为 `ai_tool`
-
-### 重新建立 ai_tool 连接
-
-1. 删除现有连接:
-   - 点击连接线
-   - 按 Delete 键或点击删除按钮
-
-2. 创建新连接:
-   - 从工具节点的输出点拖线到 AI Agent
-   - 在弹出的连接类型菜单中
-   - 选择 `ai_tool` 选项
-
-3. 确认:
-   - 连接线应该显示特殊的颜色或标记
-   - AI Agent 面板应该显示已连接的工具
-
----
-
-## 🎯 最终目标
-
-工作流正常运行后,应该实现:
-
-```
-用户: "是,我要创建元数据"
-  ↓
-AI: "好的!请提供以下信息:
-     1. 元数据中文名(必填)
-     2. 数据类型(选填,默认string)
-     3. 描述信息(选填)"
-  ↓
-用户: "中文名:用户年龄,类型:int,描述:用户的年龄"
-  ↓
-AI: [调用 check_metadata 检查 "用户年龄"]
-  ↓
-AI: [如果不存在,调用 create_metadata 创建]
-  ↓
-AI: "✅ 元数据创建成功!
-     - 中文名:用户年龄
-     - 类型:int
-     - 描述:用户的年龄"
-```
-
----
-
-**状态**: 工具已添加,待在 n8n 界面确认连接类型  
-**下一步**: 登录 n8n 界面检查和修复工具连接  
-**预计时间**: 5-10 分钟
-
-
-
-
-
-
-

+ 0 - 493
docs/archive/n8n_workflow_enhancement_summary.md

@@ -1,493 +0,0 @@
-# Data-governance 工作流功能增强总结
-
-**完成时间**: 2025-11-04 16:00  
-**任务**: 完善 Data-governance 工作流元数据管理功能
-
----
-
-## 📋 需求回顾
-
-用户需求:
-> 需要持续完善 Data-governance 工作流。当回答是的时候,需要有一个节点来提示用户输入元数据信息。并判断元数据中文名是否已经存在,如果不存在,则调用 192.168.3.143 上的接口记录这个元数据。
-
-**核心需求**:
-1. 收集用户输入的元数据信息(中文名、类型、描述)
-2. 检查元数据中文名是否已存在
-3. 如果不存在,调用 192.168.3.143 接口创建元数据
-
----
-
-## ✅ 已完成的工作
-
-### 1. 后端 API 实现 ✅
-
-#### 新增接口:检查元数据
-
-**文件**: `app/api/meta_data/routes.py`
-
-**接口**: `GET /api/meta/check`
-
-**功能**: 检查元数据中文名是否已存在
-
-**请求示例**:
-```http
-GET http://192.168.3.143:5000/api/meta/check?name_zh=用户姓名
-```
-
-**响应示例**:
-```json
-{
-  "code": 200,
-  "data": {
-    "exists": true,
-    "name_zh": "用户姓名"
-  },
-  "msg": "查询成功"
-}
-```
-
-**实现代码**:
-```python
-@bp.route('/check', methods=['GET'])
-def meta_check():
-    """检查元数据中文名是否已存在"""
-    try:
-        name_zh = request.args.get('name_zh')
-        
-        if not name_zh:
-            return jsonify(failed({}, "缺少name_zh参数"))
-        
-        with neo4j_driver.get_session() as session:
-            cypher = """
-            MATCH (n:DataMeta {name_zh: $name_zh})
-            RETURN count(n) > 0 as exists
-            """
-            result = session.run(cypher, name_zh=name_zh)
-            record = result.single()
-            
-            if record:
-                exists = record["exists"]
-                logger.info(f"检查元数据 '{name_zh}': {'存在' if exists else '不存在'}")
-                return jsonify(success({
-                    "exists": exists,
-                    "name_zh": name_zh
-                }, "查询成功"))
-            else:
-                return jsonify(success({
-                    "exists": False,
-                    "name_zh": name_zh
-                }, "查询成功"))
-                
-    except Exception as e:
-        logger.error(f"检查元数据失败: {str(e)}")
-        return jsonify(failed({}, f"检查失败: {str(e)}"))
-```
-
-**特点**:
-- ✅ 使用 Neo4j Cypher 查询
-- ✅ 统一的返回格式
-- ✅ 完整的错误处理
-- ✅ 日志记录
-
----
-
-### 2. n8n 工作流更新 ✅
-
-#### 更新 AI Agent 系统消息
-
-**工作流ID**: `tWfjLZE1FmMfQAIn`
-
-**新系统消息**:
-```
-你是一个专业的数据治理助手,负责帮助用户创建元数据。
-
-工作流程:
-1. 当用户表示需要创建元数据时,询问用户提供以下信息:
-   - 元数据中文名(必填)
-   - 数据类型(string/int/float/date等,默认string)
-   - 描述信息(选填)
-
-2. 收集完信息后:
-   - 使用 check_metadata 工具检查元数据是否已存在
-   - 如果不存在,使用 create_metadata 工具创建
-   - 如果已存在,告诉用户该元数据已存在
-
-3. 返回创建结果给用户
-
-注意:
-- 友好地引导用户提供信息
-- 元数据中文名是必需的
-- 如果元数据已存在,告诉用户并询问是否需要其他帮助
-- 每次只处理一个元数据的创建
-```
-
-**更新结果**:
-- ✅ AI Agent 现在会主动引导用户提供信息
-- ✅ 明确了工作流程和工具使用方式
-- ✅ 添加了错误处理指导
-
----
-
-### 3. 文档编写 ✅
-
-#### 创建的文档
-
-1. **`docs/n8n_improved_workflow_design.md`**
-   - 详细的需求分析
-   - 工作流设计方案(方案 A 和 方案 B)
-   - API 接口规范
-   - 测试场景
-   - 实施步骤
-
-2. **`docs/n8n_add_tools_guide.md`**
-   - 在 n8n 界面添加 HTTP Request Tools 的详细步骤
-   - 每个 Tool 的完整配置参数
-   - 连接方式说明
-   - 测试场景和验收标准
-   - 调试技巧
-
-3. **`docs/n8n_chat_trigger_error_diagnosis.md`**
-   - Internal Server Error 问题的深度诊断
-   - 根本原因分析
-   - 3 种解决方案
-   - 完整的修复步骤
-
-4. **`N8N_WORKFLOW_SUMMARY.md`** (更新)
-   - 添加了最新更新章节
-   - 记录了新增功能和改进
-
----
-
-## 🎯 待完成的工作
-
-### 在 n8n 界面手动添加 Tools
-
-**原因**: 
-- MCP API 无法正确建立 LangChain Tools 的 `ai_tool` 连接
-- 需要在 n8n 图形界面中手动操作
-
-**需要添加的 Tools**:
-
-#### 1. HTTP Request Tool - check_metadata
-
-**配置**:
-- Name: `check_metadata`
-- Method: GET
-- URL: `http://192.168.3.143:5000/api/meta/check?name_zh={{ $parameter.name_zh }}`
-- Placeholder: `name_zh` (元数据中文名)
-- 连接: 通过 `ai_tool` 连接到 AI Agent
-
-#### 2. HTTP Request Tool - create_metadata
-
-**配置**:
-- Name: `create_metadata`
-- Method: POST
-- URL: `http://192.168.3.143:5000/api/meta/node/add`
-- Body: JSON with `name_zh`, `data_type`, `describe`, `source`, `status`
-- Placeholders: `name_zh`, `data_type`, `description`
-- 连接: 通过 `ai_tool` 连接到 AI Agent
-
-**详细步骤**: 参见 `docs/n8n_add_tools_guide.md`
-
-**预计时间**: 15-20 分钟
-
----
-
-## 🧪 测试计划
-
-### 测试场景 1: 创建新元数据(正常流程)
-
-```
-步骤 1: 访问 https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-步骤 2: 输入 "是,我要创建元数据"
-预期: AI 询问元数据信息
-
-步骤 3: 输入 "中文名:测试字段001,类型:string,描述:测试字段"
-预期: 
-- AI 调用 check_metadata 检查
-- 元数据不存在
-- AI 调用 create_metadata 创建
-- 返回成功消息
-
-验收: ✅ 元数据成功创建,可在 Neo4j 中查询到
-```
-
-### 测试场景 2: 元数据已存在
-
-```
-步骤 1: 输入 "是,我要创建元数据"
-步骤 2: 输入 "中文名:测试字段001,类型:string"
-预期:
-- AI 调用 check_metadata 检查
-- 发现元数据已存在
-- AI 提示用户该元数据已存在
-- 询问是否需要其他帮助
-
-验收: ✅ 未重复创建,友好提示
-```
-
-### 测试场景 3: 信息不完整
-
-```
-步骤 1: 输入 "是,我要创建元数据"
-步骤 2: 输入 "类型是 string"
-预期:
-- AI 识别信息不完整
-- 提示需要提供中文名(必填)
-
-验收: ✅ 友好提示缺少必填信息
-```
-
-### 测试场景 4: API 错误处理
-
-```
-步骤: 断开 192.168.3.143 网络
-预期:
-- Tool 调用失败
-- AI 友好提示用户服务暂时不可用
-
-验收: ✅ 错误处理正确
-```
-
----
-
-## 📊 技术方案总结
-
-### 架构设计
-
-**选择的方案**: LangChain AI Agent + HTTP Request Tools
-
-**优势**:
-1. ✅ **用户体验好**: 对话式交互,自然流畅
-2. ✅ **灵活性高**: AI 自动判断何时调用哪个工具
-3. ✅ **可扩展**: 后续可以轻松添加更多工具
-4. ✅ **错误处理**: AI 可以理解错误并友好反馈
-
-**工作流程**:
-```
-用户输入
-    ↓
-Chat Trigger
-    ↓
-AI Agent (DeepSeek)
-    ├─ 分析用户意图
-    ├─ 提取参数信息
-    ├─ 决定调用哪个工具
-    ↓
-工具层
-    ├─ check_metadata Tool → GET /api/meta/check
-    └─ create_metadata Tool → POST /api/meta/node/add
-    ↓
-API 层 (192.168.3.143)
-    ├─ Neo4j 查询
-    └─ Neo4j 创建
-    ↓
-AI Agent 处理响应
-    ↓
-返回用户
-```
-
-### 关键技术点
-
-1. **LangChain Tools**
-   - 使用 `@n8n/n8n-nodes-langchain.toolHttpRequest`
-   - 通过 `ai_tool` 连接类型连接到 AI Agent
-   - AI 根据 Tool Description 自动决定调用时机
-
-2. **参数提取**
-   - AI Agent 从对话中提取结构化参数
-   - 使用 Placeholder Definitions 定义参数
-   - 支持默认值(如 `data_type || 'string'`)
-
-3. **错误处理**
-   - Tool 调用失败时 AI Agent 可以感知
-   - AI 会友好地告诉用户发生了什么
-   - 支持重试机制
-
----
-
-## 🎓 经验总结
-
-### 遇到的问题
-
-#### 问题 1: MCP API 无法正确添加 LangChain Tools
-
-**原因**: 
-- LangChain Tools 需要特殊的 `ai_tool` 连接类型
-- MCP API 在建立这种连接时存在限制
-- 验证机制要求 Tools 必须有连接,否则报错
-
-**解决方案**:
-- 放弃通过 API 自动化添加
-- 改为提供详细的手动操作指南
-- 编写 `docs/n8n_add_tools_guide.md` 文档
-
-**经验**:
-- 对于复杂的 LangChain 工作流,图形界面操作更可靠
-- API 适合简单的节点操作,不适合复杂的连接配置
-
-#### 问题 2: Chat Trigger 的 Internal Server Error
-
-**原因**: 
-- Chat Trigger 期望 AI Agent 是最后一个节点
-- 在 AI Agent 后面添加其他节点会导致响应失败
-- Set 节点配置不完整(空的 assignments)
-
-**解决方案**:
-- 移除 AI Agent 后的所有 main 连接
-- 使用 Tools 来执行操作,而不是后续节点
-- 修复 Set 节点配置
-
-**经验**:
-- Chat Trigger + AI Agent 必须保持简单结构
-- AI Agent 应该是 main 连接的终点
-- 所有操作通过 Tools 完成
-
-### 最佳实践
-
-1. **AI Agent 系统消息设计**
-   - ✅ 明确工作流程
-   - ✅ 说明何时使用哪个工具
-   - ✅ 包含错误处理指导
-   - ✅ 保持语言自然友好
-
-2. **Tool Description 编写**
-   - ✅ 清晰描述功能
-   - ✅ 明确说明需要哪些参数
-   - ✅ 说明返回什么结果
-   - ✅ AI 根据 Description 决定是否调用
-
-3. **API 设计**
-   - ✅ 统一的返回格式
-   - ✅ 完整的错误处理
-   - ✅ 清晰的错误消息
-   - ✅ 日志记录
-
----
-
-## 📈 项目价值
-
-### 业务价值
-
-1. **效率提升**: 
-   - 从手动填表到对话式交互
-   - 自动检查重复,避免数据冗余
-   - 实时反馈,减少等待时间
-
-2. **用户体验**:
-   - 自然语言交互,无需学习界面
-   - 友好的错误提示
-   - 智能引导,减少输入错误
-
-3. **数据质量**:
-   - 自动检查重复,保证数据唯一性
-   - 必填字段验证
-   - 类型约束
-
-### 技术价值
-
-1. **架构示范**:
-   - Chat Trigger + AI Agent + Tools 的标准模式
-   - 可复用到其他数据治理场景
-
-2. **扩展性**:
-   - 易于添加新工具(如数据质量检查、数据标准制定)
-   - 易于添加新功能
-
-3. **可维护性**:
-   - 清晰的架构
-   - 完整的文档
-   - 标准的 API 接口
-
----
-
-## 🚀 后续建议
-
-### 短期(1-2周)
-
-1. **添加更多元数据字段**
-   - 支持更多属性(如分类、标签、责任人)
-   - 支持自定义字段
-
-2. **增强验证规则**
-   - 数据类型格式验证
-   - 中文名命名规范检查
-   - 必填字段完整性验证
-
-3. **批量操作**
-   - 支持一次创建多个元数据
-   - 支持从文件导入
-
-### 中期(1-3个月)
-
-1. **元数据管理扩展**
-   - 元数据更新功能
-   - 元数据删除功能
-   - 元数据查询功能
-
-2. **数据标准制定**
-   - 添加数据标准创建工具
-   - 数据标准关联到元数据
-
-3. **数据质量检查**
-   - 添加数据质量规则配置
-   - 自动检查数据质量
-
-### 长期(3-6个月)
-
-1. **智能推荐**
-   - 基于历史数据推荐元数据属性
-   - 自动关联相关元数据
-
-2. **工作流编排**
-   - 支持复杂的数据治理流程
-   - 多步骤审批机制
-
-3. **可视化分析**
-   - 元数据血缘图谱
-   - 数据质量仪表板
-
----
-
-## 📞 支持与反馈
-
-### 文档索引
-
-- **操作指南**: `docs/n8n_add_tools_guide.md`
-- **设计方案**: `docs/n8n_improved_workflow_design.md`
-- **故障排除**: `docs/n8n_chat_trigger_error_diagnosis.md`
-- **总体概览**: `N8N_WORKFLOW_SUMMARY.md`
-
-### 联系方式
-
-如遇问题或需要协助,请查阅以上文档或联系项目团队。
-
----
-
-## ✅ 工作完成确认
-
-**后端开发**: ✅ 完成  
-**工作流设计**: ✅ 完成  
-**文档编写**: ✅ 完成  
-**手动配置指南**: ✅ 完成  
-
-**待用户操作**: 
-- ⏳ 在 n8n 界面添加 HTTP Request Tools(15-20分钟)
-- ⏳ 测试工作流功能
-
-**预计总工作量**: 
-- 已完成:2-3 小时(后端 + 工作流 + 文档)
-- 待完成:15-20 分钟(手动添加 Tools)
-
----
-
-**完成日期**: 2025-11-04  
-**版本**: v1.0  
-**状态**: ✅ 后端和设计完成,等待前端配置
-
-
-
-
-
-
-

+ 0 - 461
docs/archive/n8n_workflow_test_report.md

@@ -1,461 +0,0 @@
-# n8n 数据治理工作流测试报告
-
-**测试时间**: 2025-11-04  
-**工作流 ID**: tWfjLZE1FmMfQAIn  
-**工作流名称**: Data-governance  
-**测试人员**: AI Assistant
-
----
-
-## 📊 工作流配置检查
-
-### ✅ 基本信息
-
-| 项目 | 状态 | 详情 |
-|------|------|------|
-| 工作流状态 | ✅ 已激活 | `active: true` |
-| 创建时间 | ✅ 正常 | 2025-11-04 04:54:10 |
-| 更新时间 | ✅ 正常 | 2025-11-04 07:11:49 |
-| 节点数量 | ✅ 正常 | 7 个节点 |
-| 触发器类型 | ✅ 正常 | Chat Trigger |
-| Webhook ID | ✅ 已注册 | ea308350-ba34-4c9c-8e33-b78297842987 |
-
-### ✅ DeepSeek 配置检查
-
-| 项目 | 状态 | 详情 |
-|------|------|------|
-| DeepSeek 节点 | ✅ 已添加 | DeepSeek Chat Model |
-| 凭证配置 | ✅ 已配置 | "DeepSeek account" (ID: LSdatLFCoTSCGXmn) |
-| 模型配置 | ✅ 正常 | deepseek-chat |
-| 温度参数 | ✅ 正常 | 0.7 |
-| 最大令牌 | ✅ 正常 | 500 |
-| AI 连接 | ✅ 正常 | DeepSeek → AI Agent (ai_languageModel) |
-
-### ✅ 节点连接检查
-
-```
-Chat Trigger (启动)
-    ↓
-AI Agent (处理用户输入)
-    ↓
-判断用户意图 (IF 条件判断)
-    ↓                    ↓
-确认分支              拒绝分支
-    ↓                    ↓
-调用元数据新增API    设置拒绝消息
-    ↓
-设置确认消息
-
-DeepSeek Chat Model → AI Agent (语言模型连接)
-```
-
-**连接状态**: ✅ 所有连接正常
-
----
-
-## 🧪 功能测试
-
-### 测试环境
-
-- **n8n 服务器**: https://n8n.citupro.com
-- **Chat URL**: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-- **DataOps API**: http://localhost:5000
-- **AI 模型**: DeepSeek Chat Model
-
-### 执行记录分析
-
-**最近执行**: 
-- 执行 ID: 5
-- 执行时间: 2025-11-04 07:16:12
-- 执行模式: manual
-- 执行状态: ✅ success
-- 执行耗时: 19ms
-- 执行节点: Chat Trigger (1/7)
-
-**用户输入**: "元数据治理"
-
-**分析**: 这是一个 Chat Trigger 的测试执行,只执行了触发节点。需要进行完整的端到端测试。
-
----
-
-## 📋 测试场景
-
-### 场景 1: 用户确认元数据管理 ✅
-
-**测试步骤**:
-1. 访问 Chat URL: `https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn`
-2. 输入: "是"
-3. 观察 AI 响应
-
-**预期流程**:
-```
-用户输入 "是"
-    ↓
-DeepSeek AI 理解意图
-    ↓
-AI 输出: CONFIRM_METADATA
-    ↓
-IF 节点判断: TRUE
-    ↓
-调用 API: POST /api/meta/add
-    ↓
-返回确认消息
-```
-
-**预期输出**:
-```
-好的!已为您发起元数据新增工作流程。
-
-操作结果:成功创建元数据
-
-如需继续其他数据治理操作,请告诉我。
-```
-
-**测试状态**: ⏳ 待完整测试
-
----
-
-### 场景 2: 用户拒绝元数据管理 ✅
-
-**测试步骤**:
-1. 访问 Chat URL
-2. 输入: "否"
-3. 观察 AI 响应
-
-**预期流程**:
-```
-用户输入 "否"
-    ↓
-DeepSeek AI 理解意图
-    ↓
-AI 输出: REJECT_METADATA
-    ↓
-IF 节点判断: FALSE
-    ↓
-返回拒绝消息
-```
-
-**预期输出**:
-```
-好的,已取消元数据管理操作。
-
-还有其他需要帮助的吗?
-- 数据标准制定
-- 数据质量检查
-- 其他数据治理服务
-```
-
-**测试状态**: ⏳ 待完整测试
-
----
-
-### 场景 3: 用户意图不明确 ✅
-
-**测试步骤**:
-1. 访问 Chat URL
-2. 输入: "我不太确定"
-3. 观察 AI 响应
-
-**预期流程**:
-```
-用户输入 "我不太确定"
-    ↓
-DeepSeek AI 识别不清楚
-    ↓
-AI 输出: "请明确回答是否需要进行元数据管理(是/否)?"
-    ↓
-返回澄清消息
-```
-
-**预期输出**:
-```
-请明确回答是否需要进行元数据管理(是/否)?
-```
-
-**测试状态**: ⏳ 待完整测试
-
----
-
-### 场景 4: 多种确认表达方式 ✅
-
-**测试输入变体**:
-- "是" ✅
-- "好的" ✅
-- "可以" ✅
-- "需要" ✅
-- "确认" ✅
-- "同意" ✅
-
-**预期**: 所有变体都应该触发 CONFIRM_METADATA
-
-**测试状态**: ⏳ 待测试
-
----
-
-### 场景 5: 多种拒绝表达方式 ✅
-
-**测试输入变体**:
-- "否" ✅
-- "不用" ✅
-- "不需要" ✅
-- "取消" ✅
-- "不要" ✅
-
-**预期**: 所有变体都应该触发 REJECT_METADATA
-
-**测试状态**: ⏳ 待测试
-
----
-
-## 🔍 详细测试建议
-
-### 1. Chat 界面测试
-
-**测试方法**:
-```bash
-# 在浏览器中打开
-https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-```
-
-**检查项**:
-- [ ] 聊天界面正常显示
-- [ ] 标题显示: "数据治理助手 🤖"
-- [ ] 副标题显示: "帮助您进行元数据管理和数据治理"
-- [ ] 欢迎消息正常显示
-- [ ] 输入框可用
-- [ ] 发送按钮可点击
-
----
-
-### 2. AI 响应测试
-
-**测试 DeepSeek AI**:
-
-| 测试项 | 输入 | 预期输出 | 状态 |
-|--------|------|----------|------|
-| 确认-直接 | "是" | CONFIRM_METADATA | ⏳ |
-| 确认-礼貌 | "好的" | CONFIRM_METADATA | ⏳ |
-| 确认-肯定 | "需要" | CONFIRM_METADATA | ⏳ |
-| 拒绝-直接 | "否" | REJECT_METADATA | ⏳ |
-| 拒绝-礼貌 | "不用了" | REJECT_METADATA | ⏳ |
-| 不清楚 | "可能吧" | 询问澄清 | ⏳ |
-
----
-
-### 3. API 调用测试
-
-**测试元数据新增 API**:
-
-```bash
-# 直接测试 API(独立于工作流)
-curl -X POST http://localhost:5000/api/meta/add \
-  -H "Content-Type: application/json" \
-  -d '{
-    "name_zh": "工作流测试元数据",
-    "data_type": "string",
-    "description": "通过工作流创建的元数据",
-    "source": "data-governance-workflow"
-  }'
-```
-
-**预期响应**:
-```json
-{
-  "code": 200,
-  "data": {...},
-  "msg": "success"
-}
-```
-
-**检查项**:
-- [ ] API 响应正常
-- [ ] 状态码为 200
-- [ ] 数据成功保存到数据库
-- [ ] 返回创建的元数据信息
-
----
-
-### 4. 端到端测试
-
-**完整流程测试**:
-
-```
-步骤 1: 打开聊天界面
-  ↓
-步骤 2: 看到欢迎消息
-  ↓
-步骤 3: 输入 "是"
-  ↓
-步骤 4: DeepSeek AI 处理
-  ↓
-步骤 5: 判断节点识别为确认
-  ↓
-步骤 6: 调用 DataOps API
-  ↓
-步骤 7: 返回成功消息
-  ↓
-步骤 8: 在聊天界面看到确认消息
-```
-
-**检查项**:
-- [ ] 整个流程无错误
-- [ ] 响应时间合理(< 5秒)
-- [ ] 消息显示正确
-- [ ] 元数据成功创建
-
----
-
-## 🚨 潜在问题检查
-
-### 1. DeepSeek API 连接
-
-**检查项**:
-- [x] DeepSeek 凭证已配置
-- [x] 凭证 ID 正确: LSdatLFCoTSCGXmn
-- [ ] API Key 有效且有额度
-- [ ] 网络连接正常
-- [ ] API 响应时间正常
-
-### 2. DataOps API 连接
-
-**检查项**:
-- [ ] DataOps 服务运行正常
-- [ ] API 端点可访问: `http://localhost:5000/api/meta/add`
-- [ ] 数据库连接正常
-- [ ] 权限配置正确
-
-**注意**: API 使用 `localhost:5000`,需要确认 n8n 和 DataOps 是否在同一服务器上。
-
-### 3. 工作流配置
-
-**检查项**:
-- [x] 所有节点配置正确
-- [x] 节点连接完整
-- [x] 凭证已关联
-- [ ] 系统消息内容正确
-- [ ] IF 条件判断正确
-
----
-
-## 📈 性能指标
-
-### 预期性能
-
-| 指标 | 目标值 | 实际值 | 状态 |
-|------|--------|--------|------|
-| Chat Trigger 响应 | < 100ms | 0ms | ✅ |
-| DeepSeek AI 处理 | < 3秒 | ⏳ 待测 | ⏳ |
-| API 调用时间 | < 1秒 | ⏳ 待测 | ⏳ |
-| 总响应时间 | < 5秒 | ⏳ 待测 | ⏳ |
-
----
-
-## 🎯 测试建议
-
-### 立即测试
-
-**推荐测试步骤**:
-
-1. **访问聊天界面** ⭐⭐⭐
-   ```
-   https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-   ```
-
-2. **测试确认流程** ⭐⭐⭐
-   - 输入: "是"
-   - 观察响应
-   - 检查是否调用 API
-   - 验证消息正确
-
-3. **测试拒绝流程** ⭐⭐
-   - 输入: "否"
-   - 观察响应
-   - 验证消息正确
-
-4. **测试 DeepSeek AI** ⭐⭐
-   - 输入多种表达方式
-   - 验证 AI 理解能力
-   - 检查响应时间
-
-5. **查看执行日志** ⭐
-   - 在 n8n 界面查看 Executions
-   - 检查每个节点的输入输出
-   - 验证数据流转正确
-
----
-
-## 📝 测试记录模板
-
-### 测试记录
-
-**测试日期**: 2025-11-04  
-**测试人员**: _________
-
-| 测试场景 | 输入 | 预期输出 | 实际输出 | 状态 | 备注 |
-|---------|------|---------|---------|------|------|
-| 确认-1 | "是" | CONFIRM_METADATA + 成功消息 | | ⏳ | |
-| 确认-2 | "好的" | CONFIRM_METADATA + 成功消息 | | ⏳ | |
-| 拒绝-1 | "否" | REJECT_METADATA + 拒绝消息 | | ⏳ | |
-| 拒绝-2 | "不用" | REJECT_METADATA + 拒绝消息 | | ⏳ | |
-| 不清楚 | "我不确定" | 询问澄清 | | ⏳ | |
-
----
-
-## ✅ 检查清单
-
-### 测试前检查
-
-- [x] 工作流已激活
-- [x] DeepSeek 凭证已配置
-- [x] 节点连接正确
-- [ ] DataOps API 可访问
-- [ ] 数据库连接正常
-
-### 测试中检查
-
-- [ ] Chat 界面可以打开
-- [ ] 欢迎消息正常显示
-- [ ] 可以输入消息
-- [ ] DeepSeek AI 正常响应
-- [ ] IF 条件判断正确
-- [ ] API 调用成功
-- [ ] 消息返回正确
-
-### 测试后检查
-
-- [ ] 查看执行日志
-- [ ] 验证数据已保存
-- [ ] 检查错误日志
-- [ ] 记录性能数据
-
----
-
-## 🎊 总结
-
-### 当前状态
-
-✅ **工作流配置**: 完整且正确  
-✅ **DeepSeek 集成**: 凭证已配置  
-✅ **节点连接**: 所有连接正常  
-⏳ **功能测试**: 需要完整的端到端测试  
-
-### 下一步
-
-1. **立即测试**: 访问 Chat URL 进行实际测试
-2. **验证 AI**: 测试 DeepSeek 的理解能力
-3. **检查 API**: 确认 DataOps API 可访问
-4. **查看日志**: 检查执行记录和错误日志
-5. **性能测试**: 记录响应时间
-
-### 测试 URL
-
-**Chat 界面**: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-
-**立即测试,看看数据治理助手的表现吧!** 🚀
-
-
-
-
-
-
-

+ 0 - 326
docs/archive/n8n_workflow_test_success.md

@@ -1,326 +0,0 @@
-# 🎉 Data-governance 工作流测试成功报告
-
-**测试时间**: 2025-11-04 17:35  
-**工作流ID**: `tWfjLZE1FmMfQAIn`  
-**测试URL**: https://n8n.citupro.com/webhook/ea308350-ba34-4c9c-8e33-b78297842987/chat
-
----
-
-## ✅ 测试结果:完全成功!
-
-### 测试元数据信息
-- **中文名**: 其他费用定额
-- **类型**: string
-- **描述**: 医疗行业费用元数据
-
----
-
-## 📊 测试流程
-
-### 步骤 1: 访问聊天界面 ✅
-- **URL**: https://n8n.citupro.com/webhook/ea308350-ba34-4c9c-8e33-b78297842987/chat
-- **结果**: 成功加载,显示欢迎消息
-- **欢迎消息**: "您好!我是数据治理助手。我可以帮助您:进行元数据管理、数据标准制定、数据质量检查"
-
-### 步骤 2: 发起元数据创建请求 ✅
-- **用户输入**: "是,我要创建元数据"
-- **AI 响应**: 
-  ```
-  好的,我来帮您创建元数据。请提供以下信息:
-  - 元数据中文名(必填)
-  - 数据类型(可选,如 string/int/float/date 等,默认为 string)
-  - 描述信息(可选)
-  
-  请先告诉我您要创建的元数据中文名是什么?
-  ```
-- **结果**: ✅ AI 正确理解意图,询问详细信息
-
-### 步骤 3: 提供元数据详细信息 ✅
-- **用户输入**: "中文名:其他费用定额,类型:string,描述:医疗行业费用元数据"
-- **AI 处理过程**:
-  1. ✅ 确认收到信息
-  2. ✅ 调用 `check_metadata` 工具检查是否存在
-  3. ✅ 检查结果:元数据不存在
-  4. ✅ 调用 `create_metadata` 工具创建
-  5. ✅ 创建成功
-
-### 步骤 4: 确认创建结果 ✅
-- **AI 最终响应**:
-  ```
-  ✅ 创建成功!
-  
-  元数据"其他费用定额"已经成功创建:
-  - 中文名:其他费用定额
-  - 数据类型:string
-  - 描述:医疗行业费用元数据
-  
-  这个医疗行业的费用元数据现在已经可以在系统中使用了。
-  您是否需要创建其他元数据,或者有其他关于数据治理的问题需要帮助吗?
-  ```
-- **结果**: ✅ 创建成功,反馈清晰
-
----
-
-## 🔍 工具调用验证
-
-### check_metadata 工具 ✅
-- **调用时机**: 收集完用户信息后
-- **参数**: `name_zh = "其他费用定额"`
-- **API**: `GET http://192.168.3.143:5000/api/meta/check?name_zh=其他费用定额`
-- **返回**: `exists: false`
-- **结果**: ✅ 工具正确调用并返回结果
-
-### create_metadata 工具 ✅
-- **调用时机**: 确认元数据不存在后
-- **参数**:
-  - `name_zh = "其他费用定额"`
-  - `data_type = "string"`
-  - `description = "医疗行业费用元数据"`
-- **API**: `POST http://192.168.3.143:5000/api/meta/node/add`
-- **返回**: 成功创建
-- **结果**: ✅ 工具正确调用并创建元数据
-
----
-
-## 🎯 功能验收
-
-### 核心功能 ✅
-
-| 功能 | 状态 | 说明 |
-|------|------|------|
-| 欢迎消息显示 | ✅ | 正确显示工作流配置的欢迎消息 |
-| 意图识别 | ✅ | AI 正确识别用户创建元数据的意图 |
-| 信息收集 | ✅ | AI 主动询问并收集必需信息 |
-| 参数提取 | ✅ | AI 从自然语言中正确提取结构化参数 |
-| check_metadata 调用 | ✅ | 自动调用检查工具 |
-| create_metadata 调用 | ✅ | 元数据不存在时自动调用创建工具 |
-| 结果反馈 | ✅ | 清晰友好的成功反馈 |
-| 后续引导 | ✅ | 询问用户是否需要其他帮助 |
-
-### 用户体验 ✅
-
-| 体验指标 | 评分 | 说明 |
-|---------|------|------|
-| 界面美观度 | ⭐⭐⭐⭐⭐ | 界面简洁美观,品牌标识清晰 |
-| 交互流畅度 | ⭐⭐⭐⭐⭐ | 对话流畅,响应及时 |
-| 提示友好度 | ⭐⭐⭐⭐⭐ | 提示清晰,引导到位 |
-| 错误处理 | ⭐⭐⭐⭐⭐ | 能够正确处理各种情况 |
-| 反馈及时性 | ⭐⭐⭐⭐⭐ | 每个步骤都有清晰反馈 |
-
----
-
-## 📈 性能表现
-
-### 响应时间
-- **欢迎消息**: 即时显示
-- **第一次 AI 响应**: ~2-3 秒
-- **工具调用 + 第二次响应**: ~4-5 秒
-- **总体体验**: 流畅,无明显延迟
-
-### 准确性
-- **意图识别准确率**: 100%
-- **参数提取准确率**: 100%
-- **工具调用成功率**: 100%
-- **整体成功率**: 100%
-
----
-
-## 💡 亮点总结
-
-### 1. 完全自动化的流程 🌟
-- 用户只需自然语言描述
-- AI 自动调用后端 API
-- 无需手动填写表单
-
-### 2. 智能的对话交互 🌟
-- AI 理解自然语言
-- 主动询问缺失信息
-- 友好的引导和反馈
-
-### 3. 实时的状态提示 🌟
-- 每个步骤都有进度提示
-- "正在使用 xxx 工具..."
-- 明确的成功/失败反馈
-
-### 4. 完整的错误处理 🌟
-- 检查元数据是否已存在
-- 避免重复创建
-- 清晰的错误提示
-
----
-
-## 🔄 完整对话示例
-
-```
-系统: 您好!我是数据治理助手。
-      我可以帮助您:
-      - 进行元数据管理
-      - 数据标准制定
-      - 数据质量检查
-      请问您需要进行元数据管理吗?(请回答:是 或 否)
-
-用户: 是,我要创建元数据
-
-AI:   好的,我来帮您创建元数据。请提供以下信息:
-      - 元数据中文名(必填)
-      - 数据类型(可选,如 string/int/float/date 等,默认为 string)
-      - 描述信息(可选)
-      请先告诉我您要创建的元数据中文名是什么?
-
-用户: 中文名:其他费用定额,类型:string,描述:医疗行业费用元数据
-
-AI:   感谢您提供的信息!我已经收到了您要创建的元数据详情:
-      - 中文名:其他费用定额
-      - 类型:string
-      - 描述:医疗行业费用元数据
-      
-      现在让我帮您检查这个元数据是否已经存在...
-      (正在使用 check_metadata 工具检查...)
-      
-      检查完成!好消息是"其他费用定额"这个元数据目前不存在于系统中。
-      
-      现在我将为您创建这个元数据...
-      (正在使用 create_metadata 工具创建...)
-      
-      ✅ 创建成功!
-      
-      元数据"其他费用定额"已经成功创建:
-      - 中文名:其他费用定额
-      - 数据类型:string
-      - 描述:医疗行业费用元数据
-      
-      这个医疗行业的费用元数据现在已经可以在系统中使用了。
-      您是否需要创建其他元数据,或者有其他关于数据治理的问题需要帮助吗?
-```
-
----
-
-## 🎓 技术验证
-
-### 后端 API ✅
-- ✅ `/api/meta/check` 接口工作正常
-- ✅ `/api/meta/node/add` 接口工作正常
-- ✅ Neo4j 数据库连接正常
-- ✅ 数据持久化成功
-
-### n8n 工作流 ✅
-- ✅ Chat Trigger 正常触发
-- ✅ AI Agent 正常工作
-- ✅ DeepSeek Chat Model 正常调用
-- ✅ HTTP Request Tools 正常连接
-- ✅ 工具自动调用机制正常
-
-### LangChain Tools ✅
-- ✅ 检查元数据工具正确配置
-- ✅ 创建元数据工具正确配置
-- ✅ Placeholder 参数正确提取
-- ✅ API 请求正确构建
-- ✅ 响应正确解析
-
----
-
-## 📝 测试结论
-
-### 总体评价:优秀 ⭐⭐⭐⭐⭐
-
-**所有功能完全正常!工作流已达到生产就绪状态。**
-
-### 优点
-1. ✅ 功能完整,流程顺畅
-2. ✅ 用户体验优秀
-3. ✅ 错误处理完善
-4. ✅ 性能表现良好
-5. ✅ 技术架构稳定
-
-### 建议(可选优化)
-1. 可以添加更多元数据字段(如分类、标签、责任人等)
-2. 可以支持批量创建元数据
-3. 可以添加元数据更新和删除功能
-4. 可以添加元数据查询功能
-
----
-
-## 🚀 已验证的功能清单
-
-- [x] 聊天界面正常访问
-- [x] 欢迎消息正确显示
-- [x] 用户意图识别正确
-- [x] 信息收集流程完整
-- [x] 参数提取准确
-- [x] check_metadata 工具正常调用
-- [x] create_metadata 工具正常调用
-- [x] 元数据成功创建到 Neo4j
-- [x] 成功反馈清晰友好
-- [x] 后续交互引导到位
-
----
-
-## 📸 测试截图
-
-### 成功截图
-![元数据创建成功](../test_metadata_workflow/n8n-metadata-creation-success.png)
-
-### 关键信息
-- **创建的元数据**: 其他费用定额
-- **数据类型**: string
-- **描述**: 医疗行业费用元数据
-- **创建时间**: 2025-11-04 17:35
-- **状态**: ✅ 成功
-
----
-
-## 🎯 下一步建议
-
-### 立即可用 ✅
-工作流已完全可用,可以:
-1. 分享给团队成员使用
-2. 部署到生产环境
-3. 开始实际的元数据管理工作
-
-### 后续优化(可选)
-1. **功能扩展**:
-   - 添加元数据更新功能
-   - 添加元数据删除功能
-   - 添加元数据查询功能
-   - 支持批量操作
-
-2. **用户体验优化**:
-   - 添加元数据模板
-   - 支持文件导入
-   - 添加数据验证规则
-
-3. **集成扩展**:
-   - 集成到数据资源管理
-   - 与数据模型关联
-   - 与数据标准对接
-
----
-
-## 📞 支持信息
-
-### 工作流信息
-- **工作流名称**: Data-governance
-- **工作流ID**: `tWfjLZE1FmMfQAIn`
-- **访问URL**: https://n8n.citupro.com/webhook/ea308350-ba34-4c9c-8e33-b78297842987/chat
-
-### 技术支持
-- **后端 API**: http://192.168.3.143:5000
-- **检查接口**: `/api/meta/check`
-- **创建接口**: `/api/meta/node/add`
-
----
-
-**测试完成时间**: 2025-11-04 17:35  
-**测试结果**: ✅ 完全成功  
-**状态**: 🚀 生产就绪
-
----
-
-🎉 **恭喜!Data-governance 工作流测试完全成功!** 🎉
-
-
-
-
-
-
-

+ 0 - 293
docs/archive/translate_api_documentation.md

@@ -1,293 +0,0 @@
-# System Translate API 使用说明文档
-
-## 接口概述
-
-translate接口用于将中文节点名称翻译成符合数据库规范的英文名称,主要用于数据建模和表名/字段名的英文化处理。
-
-## 接口信息
-
-### 访问路径
-```
-POST /api/system/translate
-```
-
-### 请求头
-```
-Content-Type: application/json
-```
-
-## 输入参数
-
-### 请求体参数
-
-| 参数名 | 类型 | 必填 | 描述 | 示例 |
-|--------|------|------|------|------|
-| node_name | string | 是 | 需要翻译的中文节点名称 | "用户数据表" |
-
-### 请求体示例
-```json
-{
-    "node_name": "用户数据表"
-}
-```
-
-## 输出参数
-
-### 成功响应
-
-| 字段名 | 类型 | 描述 |
-|--------|------|------|
-| code | integer | 响应状态码,成功时为200 |
-| message | string | 响应消息,成功时为"翻译成功" |
-| data | object | 翻译结果数据对象 |
-| data.original | string | 原始输入的中文名称 |
-| data.translated | string | 翻译后的英文名称(主要结果) |
-| data.translated_list | array | 翻译结果数组(兼容性字段) |
-
-### 错误响应
-
-| 字段名 | 类型 | 描述 |
-|--------|------|------|
-| code | integer | 错误状态码 |
-| message | string | 错误描述信息 |
-| data | null | 错误时数据为空 |
-
-## 样例代码
-
-### cURL 示例
-
-```bash
-curl -X POST \
-  http://localhost:5000/api/system/translate \
-  -H 'Content-Type: application/json' \
-  -d '{
-    "node_name": "用户数据表"
-  }'
-```
-
-### JavaScript (Fetch API) 示例
-
-```javascript
-// 发送翻译请求
-async function translateNodeName(nodeName) {
-    try {
-        const response = await fetch('/api/system/translate', {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                node_name: nodeName
-            })
-        });
-        
-        const result = await response.json();
-        
-        if (result.code === 200) {
-            console.log('翻译成功:', result.data.translated);
-            return result.data.translated;
-        } else {
-            console.error('翻译失败:', result.message);
-            return null;
-        }
-    } catch (error) {
-        console.error('请求失败:', error);
-        return null;
-    }
-}
-
-// 使用示例
-translateNodeName('用户数据表').then(translated => {
-    if (translated) {
-        console.log('英文名称:', translated); // 输出: user_data_table
-    }
-});
-```
-
-### Python 示例
-
-```python
-import requests
-import json
-
-def translate_node_name(node_name):
-    """
-    调用翻译API
-    
-    Args:
-        node_name (str): 需要翻译的中文名称
-        
-    Returns:
-        str: 翻译后的英文名称,失败时返回None
-    """
-    url = 'http://localhost:5000/api/system/translate'
-    headers = {
-        'Content-Type': 'application/json'
-    }
-    data = {
-        'node_name': node_name
-    }
-    
-    try:
-        response = requests.post(url, headers=headers, json=data)
-        result = response.json()
-        
-        if result['code'] == 200:
-            print(f"翻译成功: {result['data']['translated']}")
-            return result['data']['translated']
-        else:
-            print(f"翻译失败: {result['message']}")
-            return None
-    except Exception as e:
-        print(f"请求失败: {str(e)}")
-        return None
-
-# 使用示例
-translated_name = translate_node_name('用户数据表')
-if translated_name:
-    print(f"英文名称: {translated_name}")  # 输出: user_data_table
-```
-
-### Java 示例
-
-```java
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-
-public class TranslateAPI {
-    
-    public static String translateNodeName(String nodeName) {
-        try {
-            // 构建请求体
-            JsonObject requestBody = new JsonObject();
-            requestBody.addProperty("node_name", nodeName);
-            
-            // 创建HTTP请求
-            HttpRequest request = HttpRequest.newBuilder()
-                .uri(URI.create("http://localhost:5000/api/system/translate"))
-                .header("Content-Type", "application/json")
-                .POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
-                .build();
-            
-            // 发送请求
-            HttpClient client = HttpClient.newHttpClient();
-            HttpResponse<String> response = client.send(request, 
-                HttpResponse.BodyHandlers.ofString());
-            
-            // 解析响应
-            Gson gson = new Gson();
-            JsonObject result = gson.fromJson(response.body(), JsonObject.class);
-            
-            if (result.get("code").getAsInt() == 200) {
-                String translated = result.getAsJsonObject("data")
-                    .get("translated").getAsString();
-                System.out.println("翻译成功: " + translated);
-                return translated;
-            } else {
-                String message = result.get("message").getAsString();
-                System.err.println("翻译失败: " + message);
-                return null;
-            }
-        } catch (Exception e) {
-            System.err.println("请求失败: " + e.getMessage());
-            return null;
-        }
-    }
-    
-    // 使用示例
-    public static void main(String[] args) {
-        String translated = translateNodeName("用户数据表");
-        if (translated != null) {
-            System.out.println("英文名称: " + translated); // 输出: user_data_table
-        }
-    }
-}
-```
-
-## 返回状态和编码
-
-### HTTP状态码
-
-| 状态码 | 描述 |
-|--------|------|
-| 200 | 请求成功 |
-| 400 | 请求参数错误 |
-| 500 | 服务器内部错误 |
-
-### 业务状态码
-
-| 业务码 | 描述 | 场景 |
-|--------|------|------|
-| 200 | 成功 | 翻译操作成功完成 |
-| 400 | 参数错误 | node_name参数为空或格式错误 |
-| 500 | 服务器错误 | LLM服务调用失败或其他内部错误 |
-
-## 响应示例
-
-### 成功响应
-
-```json
-{
-    "code": 200,
-    "message": "翻译成功",
-    "data": {
-        "original": "用户数据表",
-        "translated": "user_data_table",
-        "translated_list": ["user_data_table"]
-    }
-}
-```
-
-### 参数错误响应
-
-```json
-{
-    "code": 400,
-    "message": "node_name参数不能为空",
-    "data": null
-}
-```
-
-### 服务器错误响应
-
-```json
-{
-    "code": 500,
-    "message": "翻译失败: LLM服务调用超时",
-    "data": null
-}
-```
-
-## 使用场景
-
-1. **数据库表名翻译**:将中文表名翻译为符合数据库规范的英文表名
-2. **字段名翻译**:将中文字段名翻译为英文字段名
-3. **数据建模**:在数据建模过程中自动生成英文标识符
-4. **系统集成**:为第三方系统提供标准化的英文名称
-
-## 翻译规则
-
-翻译结果遵循以下规则:
-- 使用小写字母
-- 多个单词用下划线连接
-- 符合PostgreSQL数据库命名规范
-- 优先使用简短、准确的英文单词
-- "表"字统一翻译为"table"
-
-## 注意事项
-
-1. 请确保输入的`node_name`为有效的字符串
-2. 翻译结果适用于数据库表名和字段名,符合SQL命名规范
-3. 如果翻译服务不可用,接口会返回相应的错误信息
-4. 建议在生产环境中实现重试机制以提高可靠性
-
-## 版本信息
-
-- API版本:v1.0
-- 文档版本:1.0.0
-- 最后更新:2025年 

+ 0 - 311
docs/archive/verify_check_api.md

@@ -1,311 +0,0 @@
-# /api/meta/check 接口验证报告
-
-**验证时间**: 2025-11-04 17:50  
-**接口地址**: `http://192.168.3.143:5000/api/meta/check`
-
----
-
-## ✅ 代码审查结果
-
-### 1. 接口代码检查 ✅
-
-**文件**: `app/api/meta_data/routes.py` (第 197-238 行)
-
-**代码实现**:
-```python
-@bp.route('/check', methods=['GET'])
-def meta_check():
-    """
-    检查元数据中文名是否已存在
-    """
-    try:
-        name_zh = request.args.get('name_zh')
-        
-        # 参数验证
-        if not name_zh:
-            return jsonify(failed({}, "缺少name_zh参数"))
-        
-        # Neo4j 查询
-        with neo4j_driver.get_session() as session:
-            cypher = """
-            MATCH (n:DataMeta {name_zh: $name_zh})
-            RETURN count(n) > 0 as exists
-            """
-            result = session.run(cypher, name_zh=name_zh)
-            record = result.single()
-            
-            if record:
-                exists = record["exists"]
-                return jsonify(success({
-                    "exists": exists,
-                    "name_zh": name_zh
-                }, "查询成功"))
-            else:
-                return jsonify(success({
-                    "exists": False,
-                    "name_zh": name_zh
-                }, "查询成功"))
-                
-    except Exception as e:
-        logger.error(f"检查元数据失败: {str(e)}")
-        return jsonify(failed({}, f"检查失败: {str(e)}"))
-```
-
-**验证点**:
-- ✅ 路由正确: `@bp.route('/check', methods=['GET'])`
-- ✅ 参数获取: `request.args.get('name_zh')`
-- ✅ 参数验证: 检查 name_zh 是否为空
-- ✅ Neo4j 查询: 正确的 Cypher 语法
-- ✅ 错误处理: try-except 包裹
-- ✅ 日志记录: logger.info 和 logger.error
-- ✅ 返回格式: 统一的 success/failed 格式
-
----
-
-## ✅ n8n 工作流配置检查
-
-### 工具节点配置
-
-**节点名称**: 检查元数据工具  
-**节点类型**: `@n8n/n8n-nodes-langchain.toolHttpRequest`
-
-**配置参数**:
-```yaml
-toolDescription: "检查元数据中文名是否已经存在。需要参数:name_zh(元数据中文名)。返回exists字段表示是否存在(true/false)"
-method: GET
-url: "http://192.168.3.143:5000/api/meta/check?name_zh={name_zh}"
-authentication: none
-placeholderDefinitions:
-  - name: name_zh
-    description: "元数据中文名"
-    type: string
-```
-
-**验证点**:
-- ✅ URL 正确: 指向 192.168.3.143:5000
-- ✅ 方法正确: GET
-- ✅ 参数配置: {name_zh} placeholder 正确
-- ✅ Description 清晰: AI 能理解何时调用
-- ✅ 连接正确: 通过 ai_tool 连接到 AI Agent
-
----
-
-## ✅ 实际运行验证
-
-### 最近执行记录 (n8n Execution ID: 12)
-
-**执行时间**: 2025-11-04 09:49:53  
-**状态**: ✅ Success  
-**持续时间**: 6.36 秒
-
-**AI Agent 输出内容**:
-```
-现在让我帮您检查这个元数据是否已经存在...
-
-(正在使用 check_metadata 工具检查...)
-
-检查完成!好消息是"其他费用定额"这个元数据目前不存在于系统中。
-```
-
-**验证点**:
-- ✅ AI Agent 成功调用了 check_metadata 工具
-- ✅ 工具返回了结果
-- ✅ AI 正确理解了返回结果(不存在)
-- ✅ 整个流程执行成功
-
----
-
-## ✅ 浏览器测试验证
-
-### 测试场景
-
-**测试时间**: 2025-11-04 17:35  
-**测试元数据**: 其他费用定额
-
-**测试流程**:
-1. 用户输入: "是,我要创建元数据"
-2. 用户输入: "中文名:其他费用定额,类型:string,描述:医疗行业费用元数据"
-3. AI 调用 check_metadata 工具
-4. 工具返回: 元数据不存在
-5. AI 调用 create_metadata 工具
-6. 创建成功
-
-**验证点**:
-- ✅ check_metadata 工具被正确调用
-- ✅ API 返回了正确的结果
-- ✅ AI 正确解析了返回结果
-- ✅ 后续流程正确执行
-
----
-
-## 📊 接口功能验证矩阵
-
-| 功能点 | 状态 | 说明 |
-|--------|------|------|
-| 路由注册 | ✅ | `/api/meta/check` 正确注册 |
-| HTTP 方法 | ✅ | GET 方法 |
-| 参数获取 | ✅ | 从 query string 获取 name_zh |
-| 参数验证 | ✅ | 检查 name_zh 是否为空 |
-| Neo4j 连接 | ✅ | neo4j_driver.get_session() |
-| Cypher 查询 | ✅ | 正确的查询语法 |
-| 结果处理 | ✅ | exists 字段返回 true/false |
-| 错误处理 | ✅ | try-except 捕获异常 |
-| 日志记录 | ✅ | logger.info 和 logger.error |
-| 返回格式 | ✅ | 统一的 JSON 格式 |
-| n8n 集成 | ✅ | 工具节点正确配置 |
-| 实际运行 | ✅ | 执行记录显示成功 |
-| 端到端测试 | ✅ | 浏览器测试成功 |
-
----
-
-## 🔍 接口调用示例
-
-### 请求示例
-
-```http
-GET http://192.168.3.143:5000/api/meta/check?name_zh=其他费用定额
-```
-
-### 成功响应示例(元数据存在)
-
-```json
-{
-  "code": 200,
-  "data": {
-    "exists": true,
-    "name_zh": "其他费用定额"
-  },
-  "msg": "查询成功"
-}
-```
-
-### 成功响应示例(元数据不存在)
-
-```json
-{
-  "code": 200,
-  "data": {
-    "exists": false,
-    "name_zh": "不存在的元数据"
-  },
-  "msg": "查询成功"
-}
-```
-
-### 错误响应示例(缺少参数)
-
-```json
-{
-  "code": 500,
-  "data": {},
-  "msg": "缺少name_zh参数"
-}
-```
-
----
-
-## ✅ 验证结论
-
-### 总体状态: 完全正常 ⭐⭐⭐⭐⭐
-
-**接口代码**: ✅ 完全正确  
-**n8n 配置**: ✅ 完全正确  
-**实际运行**: ✅ 完全正常  
-**端到端测试**: ✅ 完全通过
-
-### 证据链
-
-1. **代码审查**: 
-   - 接口代码实现正确
-   - 参数验证完整
-   - 错误处理完善
-
-2. **配置验证**:
-   - n8n 工具节点配置正确
-   - URL 指向正确的服务器
-   - 参数定义清晰
-
-3. **运行日志**:
-   - n8n 执行记录显示成功
-   - AI Agent 明确提到调用了工具
-   - 没有任何错误日志
-
-4. **实际测试**:
-   - 浏览器测试完全成功
-   - 工具正确调用
-   - 结果正确返回
-   - 创建流程完整
-
----
-
-## 🎯 关键发现
-
-### 接口完全可用 ✅
-
-1. **后端实现**: 代码正确,逻辑完整
-2. **n8n 集成**: 配置正确,连接正常
-3. **运行状态**: 所有执行都成功
-4. **实际效果**: 端到端测试通过
-
-### 无需任何修复 ✅
-
-接口 `http://192.168.3.143:5000/api/meta/check` 工作完全正常,无需任何修改或优化。
-
----
-
-## 📈 性能表现
-
-### 执行时间分析
-
-根据 n8n 执行记录 (Execution ID: 12):
-- **总执行时间**: 6.36 秒
-- **AI Agent 执行**: 6.35 秒
-  - 包含 check_metadata 工具调用
-  - 包含 create_metadata 工具调用
-  - 包含 AI 推理时间
-
-### 性能评估: 优秀 ⭐⭐⭐⭐⭐
-
-- API 响应速度快
-- 工具调用延迟低
-- 整体用户体验流畅
-
----
-
-## 🔐 安全性检查
-
-| 安全点 | 状态 | 说明 |
-|--------|------|------|
-| 参数验证 | ✅ | 检查 name_zh 是否为空 |
-| SQL 注入防护 | ✅ | 使用参数化 Cypher 查询 |
-| 错误信息 | ✅ | 不泄露敏感信息 |
-| 异常处理 | ✅ | 捕获并记录异常 |
-| 日志记录 | ✅ | 记录操作和错误 |
-
----
-
-## 📝 建议(可选)
-
-虽然接口完全正常,但可以考虑以下优化(非必需):
-
-1. **缓存优化**: 对频繁查询的元数据添加缓存
-2. **性能优化**: 添加索引提升查询速度
-3. **监控告警**: 添加接口调用监控
-4. **限流保护**: 防止接口被滥用
-
----
-
-**验证人**: AI Assistant  
-**验证时间**: 2025-11-04 17:50  
-**结论**: ✅ 接口完全正常,可以放心使用
-
----
-
-🎉 **验证通过!`/api/meta/check` 接口工作完全正常!** 🎉
-
-
-
-
-
-
-

+ 0 - 80
docs/archive/数据结构(2).txt

@@ -1,80 +0,0 @@
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-        "data_model": {
-        "resource_selected": [
-          {
-            "resource_id": [],
-            "data_resource": [],
-            "meta_ids": [
-              {
-                "id": 2223,
-                "name_en": "dim_key",
-                "data_type": "varchar",
-                "name_zh": "维度key"
-              },
-              {
-                "id": 2219,
-                "name_en": "ny",
-                "data_type": "timestamp(6)",
-                "name_zh": "年月"
-              },
-              {
-                "id": 2222,
-                "name_en": "income",
-                "data_type": "numeric",
-                "name_zh": "收入"
-              },
-              {
-                "id": 2224,
-                "name_en": "numberCharges",
-                "data_type": "numeric",
-                "name_zh": "收费次数"
-              },
-              {
-                "id": 2221,
-                "name_en": "departmentCode",
-                "data_type": "varchar",
-                "name_zh": "科室代码"
-              },
-              {
-                "id": 162,
-                "name_en": "id",
-                "data_type": "integer",
-                "name_zh": ""
-              },
-              {
-                "id": 2220,
-                "name_en": "chargeCode",
-                "data_type": "varchar",
-                "name_zh": "收费编码"
-              },
-              {
-                "id": 2225,
-                "name_en": "incomeAmount",
-                "data_type": "numeric",
-                "name_zh": "收入金额"
-              }
-            ]
-          }
-        ],
-        "blood_resource": 299,
-        "name_en": "data_model_1764061",
-        "status": true,
-        "tag": {},
-        "origin": "resource",
-        "data_sensitivity": "低",
-        "frequency": "日",
-        "id": 114,
-        "organization": "citu",
-        "category": "应用类",
-        "level": 0,
-        "leader": "citu",
-        "describe": "",
-        "create_time": "2025-11-25 17:00:58",
-        "name_zh": "数据模型_1764061243362",
-        "data_source": 2257
-      }
-   }
-}

+ 0 - 11
docs/archive/每日签运需求.txt

@@ -1,11 +0,0 @@
-每日签运小程序
-1. 微信小程序注册登录(微信号,手机)
-2. 日历展示每日签运。阅读过的有标记。没阅读过的保持空白。 
-3. 每日签运推荐颜色。阅读过的标记也可以用同一种颜色标记。 
-	金:白      #FAFAFA
-	水:黑      #212121
-	木:绿      #43A047
-	火:红      #F44336
-	土:黄      #FDD835
-4. 每日手账。可以记录简短文字,不超过500字。 纯文本。 (可以语音录入)
-5. 日历中点击某一天,可以查看当日签运,可以查看当日手账。  

+ 0 - 42
docs/archive/科室对照表_原始.sql

@@ -1,42 +0,0 @@
-CREATE TABLE TB_JC_KSDZB (
-    YLJGDM VARCHAR(22) NOT NULL,
-    HISKSDM CHAR(20) NOT NULL,
-    HISKSMC CHAR(20) NOT NULL,
-    BAKSDM CHAR(20),
-    BAKSMC CHAR(20),
-    CBZXDM CHAR(20) NOT NULL,
-    CBZXMC CHAR(20) NOT NULL,
-    HSDYDM CHAR(20) NOT NULL,
-    HSDYMC CHAR(20) NOT NULL,
-    HISKSLX CHAR(10) NOT NULL,
-    HISKSNWKBS CHAR(10) NOT NULL,
-    HISKSBQBS CHAR(10) NOT NULL,
-    TYBS INTEGER NOT NULL,
-    TBRQ TIMESTAMP NOT NULL,
-    XGBZ CHAR(1) NOT NULL,
-    YLYL1 VARCHAR(128),
-    YLYL2 VARCHAR(128),
-    PRIMARY KEY (YLJGDM, HISKSDM)
-);
-
--- 为表添加注释
-COMMENT ON TABLE TB_JC_KSDZB IS '科室对照表';
-
--- 为字段添加注释
-COMMENT ON COLUMN TB_JC_KSDZB.YLJGDM IS '医疗机构代码,复合主键';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSDM IS 'HIS科室代码,主键、唯一';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSMC IS 'HIS科室名称';
-COMMENT ON COLUMN TB_JC_KSDZB.BAKSDM IS '病案科室代码,应与"病案明细表"里的科室ID对应';
-COMMENT ON COLUMN TB_JC_KSDZB.BAKSMC IS '病案科室名称';
-COMMENT ON COLUMN TB_JC_KSDZB.CBZXDM IS '成本中心代码,与HIS科室对应';
-COMMENT ON COLUMN TB_JC_KSDZB.CBZXMC IS '成本中心名称';
-COMMENT ON COLUMN TB_JC_KSDZB.HSDYDM IS '核算单元代码,字符类型避免丢失前导0';
-COMMENT ON COLUMN TB_JC_KSDZB.HSDYMC IS '核算单元名称';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSLX IS 'HIS科室类型,可取值:临床/医技/其它';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSNWKBS IS 'HIS科室内外科标识,可取值:内科/外科';
-COMMENT ON COLUMN TB_JC_KSDZB.HISKSBQBS IS 'HIS科室病区标识,可取值:病区/非病区';
-COMMENT ON COLUMN TB_JC_KSDZB.TYBS IS '停用标识,科室是否停用标识:0-正在使用;1-已停用';
-COMMENT ON COLUMN TB_JC_KSDZB.TBRQ IS '数据上传时间';
-COMMENT ON COLUMN TB_JC_KSDZB.XGBZ IS '修改标志:0-正常;1-撤销';
-COMMENT ON COLUMN TB_JC_KSDZB.YLYL1 IS '预留字段一';
-COMMENT ON COLUMN TB_JC_KSDZB.YLYL2 IS '预留字段二';

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
docs/archive/返回格式.txt


+ 0 - 270
docs/cohere_bearer_token_guide.md

@@ -1,270 +0,0 @@
-# Cohere API Key 作为 Bearer Token 使用指南
-
-## 📌 重要说明
-
-**Cohere API Key 本身就是 Bearer Token!**
-
-你不需要"创建"一个新的 Bearer authorization key。Cohere API Key (`4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`) 可以直接用作 Bearer token 在 HTTP 请求中进行身份验证。
-
----
-
-## 🔑 Bearer Token 格式
-
-Bearer token 的标准格式是:
-
-```
-Authorization: Bearer <API_KEY>
-```
-
-对于 Cohere API,格式为:
-
-```
-Authorization: Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C
-```
-
----
-
-## 💻 在不同场景中使用
-
-### 1. Python 代码中使用
-
-```python
-import requests
-
-# Cohere API Key
-api_key = "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C"
-
-# 设置请求头
-headers = {
-    "Authorization": f"Bearer {api_key}",
-    "Content-Type": "application/json",
-}
-
-# 调用 Cohere API
-response = requests.get(
-    "https://api.cohere.ai/v1/models",
-    headers=headers,
-    timeout=10,
-)
-
-print(response.json())
-```
-
-### 2. cURL 命令中使用
-
-```bash
-curl -X GET "https://api.cohere.ai/v1/models" \
-  -H "Authorization: Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C" \
-  -H "Content-Type: application/json"
-```
-
-### 3. JavaScript/Node.js 中使用
-
-```javascript
-const apiKey = "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C";
-
-fetch("https://api.cohere.ai/v1/models", {
-  method: "GET",
-  headers: {
-    "Authorization": `Bearer ${apiKey}`,
-    "Content-Type": "application/json",
-  },
-})
-  .then((response) => response.json())
-  .then((data) => console.log(data));
-```
-
-### 4. n8n HTTP Request 节点中使用
-
-#### 方法 1: 直接在 Header 中设置
-
-1. 添加 **HTTP Request** 节点
-2. 配置如下:
-   - **Method**: `GET` 或 `POST`
-   - **URL**: `https://api.cohere.ai/v1/models`(或其他 Cohere API 端点)
-   - **Authentication**: `None`
-   - **Send Headers**: ✅ 启用
-   - **Headers**:
-     ```
-     Authorization: Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C
-     Content-Type: application/json
-     ```
-
-#### 方法 2: 使用表达式(推荐,更安全)
-
-在 n8n 中使用表达式,避免硬编码 API Key:
-
-1. 在 **Headers** 中设置:
-   - **Name**: `Authorization`
-   - **Value**: `Bearer {{ $env.COHERE_API_KEY }}` 或使用凭证
-
-2. 或者使用 n8n 的凭证系统:
-   - 创建 **Generic Credential Type** 凭证
-   - 存储 API Key
-   - 在 HTTP Request 节点中引用
-
----
-
-## 🔧 在 n8n 中创建通用 Bearer Token 凭证
-
-如果你想在 n8n 中创建一个通用的 Bearer Token 凭证(用于 HTTP Request 节点),可以这样做:
-
-### 步骤 1: 创建 Generic Credential Type
-
-1. 访问: https://n8n.citupro.com/home/credentials
-2. 点击 **"Add Credential"**
-3. 搜索 **"Generic Credential Type"** 或 **"HTTP Header Auth"**
-4. 选择相应的凭证类型
-
-### 步骤 2: 配置凭证
-
-**选项 A: Generic Credential Type**
-- **Name**: `Cohere Bearer Token`
-- **Credential Data**:
-  ```json
-  {
-    "apiKey": "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C"
-  }
-  ```
-
-**选项 B: HTTP Header Auth**(如果可用)
-- **Name**: `Cohere Bearer Token`
-- **Header Name**: `Authorization`
-- **Header Value**: `Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`
-
-### 步骤 3: 在 HTTP Request 节点中使用
-
-1. 打开 HTTP Request 节点
-2. 在 **Authentication** 字段中选择创建的凭证
-3. 或者手动在 **Headers** 中添加:
-   ```
-   Authorization: Bearer {{ $credentials.cohereBearerToken.apiKey }}
-   ```
-
----
-
-## 📝 完整示例:调用 Cohere Rerank API
-
-### Python 示例
-
-```python
-import requests
-
-api_key = "4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C"
-
-headers = {
-    "Authorization": f"Bearer {api_key}",
-    "Content-Type": "application/json",
-}
-
-# Rerank API 请求
-data = {
-    "model": "rerank-multilingual-v3.0",
-    "query": "什么是人工智能?",
-    "documents": [
-        "人工智能是计算机科学的一个分支",
-        "机器学习是人工智能的核心技术",
-        "深度学习是机器学习的一个子集",
-    ],
-    "top_n": 2,
-}
-
-response = requests.post(
-    "https://api.cohere.ai/v1/rerank",
-    headers=headers,
-    json=data,
-    timeout=30,
-)
-
-print(response.json())
-```
-
-### n8n HTTP Request 节点配置
-
-**节点配置**:
-- **Method**: `POST`
-- **URL**: `https://api.cohere.ai/v1/rerank`
-- **Authentication**: `None`
-- **Send Headers**: ✅
-- **Headers**:
-  ```
-  Authorization: Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C
-  Content-Type: application/json
-  ```
-- **Send Body**: ✅
-- **Body Content Type**: `JSON`
-- **JSON Body**:
-  ```json
-  {
-    "model": "rerank-multilingual-v3.0",
-    "query": "{{ $json.query }}",
-    "documents": {{ $json.documents }},
-    "top_n": 2
-  }
-  ```
-
----
-
-## 🛠️ 实用脚本
-
-我已经创建了一个测试脚本 `scripts/test_cohere_api_key.py`,它展示了如何使用 Bearer token:
-
-```bash
-python scripts/test_cohere_api_key.py 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C
-```
-
-这个脚本会:
-1. 使用 Bearer token 格式调用 Cohere API
-2. 验证 API Key 是否有效
-3. 测试 Rerank API 功能
-
----
-
-## 🔒 安全最佳实践
-
-1. **不要在代码中硬编码 API Key**
-   - 使用环境变量
-   - 使用配置文件(不提交到 Git)
-   - 使用 n8n 凭证系统
-
-2. **使用环境变量**:
-   ```python
-   import os
-   api_key = os.environ.get("COHERE_API_KEY")
-   ```
-
-3. **在 n8n 中使用表达式**:
-   ```
-   Bearer {{ $env.COHERE_API_KEY }}
-   ```
-
-4. **定期轮换 API Key**
-   - 在 Cohere Dashboard 中生成新 Key
-   - 更新所有使用该 Key 的地方
-
----
-
-## 📚 相关文档
-
-- [Cohere API 文档](https://docs.cohere.com/)
-- [Cohere Rerank API](https://docs.cohere.com/docs/reranking)
-- [n8n HTTP Request 节点文档](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/)
-- [Bearer Token 认证说明](https://oauth.net/2/bearer-tokens/)
-
----
-
-## ✅ 总结
-
-**你不需要创建新的 Bearer authorization key!**
-
-- Cohere API Key 就是 Bearer token
-- 格式: `Authorization: Bearer <API_KEY>`
-- 直接在 HTTP 请求头中使用即可
-- 在 n8n 中,可以使用 HTTP Request 节点 + Header 配置,或使用 Cohere 专用节点(已配置好凭证)
-
-**你的 Cohere API Key**: `4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`  
-**Bearer Token 格式**: `Bearer 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`
-
----
-
-**最后更新**: 2026-01-19

+ 0 - 908
docs/data_flow_apis.md

@@ -1,908 +0,0 @@
-# DataFlow API 操作说明文档
-
-## 概述
-
-DataFlow API 提供了完整的数据流管理功能,包括数据流的创建、查询、更新、删除、执行等操作。系统基于Neo4j图数据库和PostgreSQL关系数据库的混合架构,支持复杂的数据流编排和管理。
-
-**版本**: v1.0  
-**基础URL**: `/api/dataflow`  
-**内容类型**: `application/json`  
-**字符编码**: UTF-8
-
-## 通用响应格式
-
-所有API接口都遵循统一的响应格式:
-
-```json
-{
-  "code": 200,           // HTTP状态码
-  "message": "操作成功",  // 操作结果消息
-  "data": {}             // 返回的具体数据
-}
-```
-
-## 状态码说明
-
-| 状态码 | 含义 | 说明 |
-|--------|------|------|
-| 200 | 成功 | 请求成功执行 |
-| 400 | 请求错误 | 请求参数错误或缺失 |
-| 404 | 未找到 | 请求的资源不存在 |
-| 500 | 服务器错误 | 服务器内部错误 |
-
----
-
-## API 接口详情
-
-### 1. 获取数据流列表
-
-**接口描述**: 获取数据流列表,支持分页和搜索功能。
-
-#### 请求信息
-- **HTTP方法**: `GET`
-- **请求路径**: `/get-dataflows-list`
-- **请求头**: `Content-Type: application/json`
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 默认值 | 说明 |
-|--------|------|------|--------|------|
-| page | int | 否 | 1 | 页码,从1开始 |
-| page_size | int | 否 | 10 | 每页记录数,最大100 |
-| search | string | 否 | "" | 搜索关键词,支持名称和描述模糊匹配 |
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "list": [
-      {
-        "id": 1,
-        "name": "用户数据ETL流程",
-        "en_name": "user_data_etl_process",
-        "description": "处理用户数据的ETL流程",
-        "status": "active",
-        "created_at": "2023-12-01 10:00:00",
-        "updated_at": "2023-12-01 12:00:00",
-        "created_by": "admin",
-        "config": "{\"source\": \"mysql\", \"target\": \"elasticsearch\"}"
-      }
-    ],
-    "pagination": {
-      "page": 1,
-      "page_size": 10,
-      "total": 25,
-      "total_pages": 3
-    }
-  }
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-url = "http://your-domain/api/data-flow/get-dataflows-list"
-params = {
-    "page": 1,
-    "page_size": 10,
-    "search": "ETL"
-}
-
-response = requests.get(url, params=params)
-data = response.json()
-
-if data["code"] == 200:
-    dataflows = data["data"]["list"]
-    for df in dataflows:
-        print(f"数据流: {df['name']} - {df['description']}")
-```
-
-**JavaScript示例**:
-```javascript
-const params = new URLSearchParams({
-    page: 1,
-    page_size: 10,
-    search: 'ETL'
-});
-
-fetch(`/api/data-flow/get-dataflows-list?${params}`)
-    .then(response => response.json())
-    .then(data => {
-        if (data.code === 200) {
-            console.log('数据流列表:', data.data.list);
-        }
-    })
-    .catch(error => console.error('错误:', error));
-```
-
-#### 测试数据
-```
-GET /api/data-flow/get-dataflows-list?page=1&page_size=5&search=数据处理
-```
-
----
-
-### 2. 获取数据流详情
-
-**接口描述**: 根据数据流ID获取详细信息,包括关联的标签、子节点和父节点。
-
-#### 请求信息
-- **HTTP方法**: `GET`
-- **请求路径**: `/get-dataflow/{dataflow_id}`
-- **请求头**: `Content-Type: application/json`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "id": 1,
-    "name": "用户数据ETL流程",
-    "en_name": "user_data_etl_process",
-    "description": "处理用户数据的ETL流程",
-    "status": "active",
-    "created_at": "2023-12-01 10:00:00",
-    "updated_at": "2023-12-01 12:00:00",
-    "created_by": "admin",
-    "config": "{\"source\": \"mysql\", \"target\": \"elasticsearch\"}",
-    "tags": [
-      {"id": 10, "name": "数据处理"},
-      {"id": 11, "name": "ETL"}
-    ],
-    "children": [
-      {"id": 2, "name": "数据清洗子流程"},
-      {"id": 3, "name": "数据转换子流程"}
-    ],
-    "parents": [
-      {"id": 0, "name": "主数据流"}
-    ]
-  }
-}
-```
-
-#### 错误响应
-
-```json
-{
-  "code": 404,
-  "message": "数据流不存在",
-  "data": null
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-dataflow_id = 1
-url = f"http://your-domain/api/data-flow/get-dataflow/{dataflow_id}"
-
-response = requests.get(url)
-data = response.json()
-
-if data["code"] == 200:
-    dataflow = data["data"]
-    print(f"数据流名称: {dataflow['name']}")
-    print(f"关联标签: {[tag['name'] for tag in dataflow['tags']]}")
-elif data["code"] == 404:
-    print("数据流不存在")
-```
-
-#### 测试数据
-```
-GET /api/data-flow/get-dataflow/1
-```
-
----
-
-### 3. 创建数据流
-
-**接口描述**: 创建新的数据流,自动生成英文名称,支持标签和子节点关联。
-
-#### 请求信息
-- **HTTP方法**: `POST`
-- **请求路径**: `/add-dataflow`
-- **请求头**: `Content-Type: application/json`
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| name | string | 是 | 数据流名称 |
-| description | string | 是 | 数据流描述 |
-| status | string | 否 | 状态,默认"inactive",可选值: "active", "inactive" |
-| created_by | string | 否 | 创建者,默认"system" |
-| config | object | 否 | 配置信息,JSON对象 |
-| children_ids | array | 否 | 子节点ID列表 |
-| tag_id | int | 否 | 关联标签ID |
-
-#### 请求体示例
-
-```json
-{
-  "name": "客户数据同步流程",
-  "description": "将客户数据从CRM同步到数据仓库",
-  "status": "active",
-  "created_by": "admin",
-  "config": {
-    "source": {
-      "type": "mysql",
-      "host": "192.168.1.100",
-      "database": "crm"
-    },
-    "target": {
-      "type": "postgresql",
-      "host": "192.168.1.200",
-      "database": "datawarehouse"
-    },
-    "schedule": "0 2 * * *"
-  },
-  "children_ids": [2, 3],
-  "tag_id": 10
-}
-```
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "数据流创建成功",
-  "data": {
-    "id": 5,
-    "name": "客户数据同步流程",
-    "en_name": "customer_data_sync_process",
-    "description": "将客户数据从CRM同步到数据仓库",
-    "status": "active",
-    "created_at": "2023-12-01 14:30:00",
-    "updated_at": "2023-12-01 14:30:00",
-    "created_by": "admin",
-    "config": "{\"source\":{\"type\":\"mysql\",\"host\":\"192.168.1.100\",\"database\":\"crm\"},\"target\":{\"type\":\"postgresql\",\"host\":\"192.168.1.200\",\"database\":\"datawarehouse\"},\"schedule\":\"0 2 * * *\"}"
-  }
-}
-```
-
-#### 错误响应
-
-```json
-{
-  "code": 400,
-  "message": "参数错误: 缺少必填字段: name",
-  "data": null
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-import json
-
-url = "http://your-domain/api/data-flow/add-dataflow"
-data = {
-    "name": "订单数据处理流程",
-    "description": "处理电商订单数据",
-    "status": "active",
-    "created_by": "data_team",
-    "config": {
-        "batch_size": 1000,
-        "retry_count": 3
-    }
-}
-
-response = requests.post(url, json=data)
-result = response.json()
-
-if result["code"] == 200:
-    print(f"创建成功,数据流ID: {result['data']['id']}")
-else:
-    print(f"创建失败: {result['message']}")
-```
-
-#### 测试数据
-```json
-{
-  "name": "测试数据流",
-  "description": "用于测试的数据流",
-  "status": "inactive",
-  "config": {"test": true}
-}
-```
-
----
-
-### 4. 更新数据流
-
-**接口描述**: 更新现有数据流的信息,支持部分字段更新。
-
-#### 请求信息
-- **HTTP方法**: `PUT`
-- **请求路径**: `/update-dataflow/{dataflow_id}`
-- **请求头**: `Content-Type: application/json`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| name | string | 否 | 数据流名称 |
-| description | string | 否 | 数据流描述 |
-| status | string | 否 | 状态 |
-| config | object | 否 | 配置信息 |
-
-#### 请求体示例
-
-```json
-{
-  "description": "更新后的数据流描述",
-  "status": "active",
-  "config": {
-    "batch_size": 2000,
-    "timeout": 300
-  }
-}
-```
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "数据流更新成功",
-  "data": {
-    "id": 1,
-    "name": "用户数据ETL流程",
-    "en_name": "user_data_etl_process",
-    "description": "更新后的数据流描述",
-    "status": "active",
-    "created_at": "2023-12-01 10:00:00",
-    "updated_at": "2023-12-01 15:45:00",
-    "created_by": "admin",
-    "config": "{\"batch_size\":2000,\"timeout\":300}"
-  }
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-dataflow_id = 1
-url = f"http://your-domain/api/data-flow/update-dataflow/{dataflow_id}"
-data = {
-    "status": "active",
-    "config": {"enabled": True}
-}
-
-response = requests.put(url, json=data)
-result = response.json()
-
-if result["code"] == 200:
-    print("更新成功")
-elif result["code"] == 404:
-    print("数据流不存在")
-```
-
----
-
-### 5. 删除数据流
-
-**接口描述**: 删除指定的数据流及其所有关联关系。
-
-#### 请求信息
-- **HTTP方法**: `DELETE`
-- **请求路径**: `/delete-dataflow/{dataflow_id}`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 响应数据
-
-**成功响应**:
-```json
-{
-  "code": 200,
-  "message": "数据流删除成功",
-  "data": {}
-}
-```
-
-**失败响应**:
-```json
-{
-  "code": 404,
-  "message": "数据流不存在",
-  "data": {}
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-dataflow_id = 1
-url = f"http://your-domain/api/data-flow/delete-dataflow/{dataflow_id}"
-
-response = requests.delete(url)
-result = response.json()
-
-if result["code"] == 200:
-    print("删除成功")
-elif result["code"] == 404:
-    print("数据流不存在")
-```
-
-#### 测试数据
-```
-DELETE /api/data-flow/delete-dataflow/1
-```
-
----
-
-### 6. 执行数据流
-
-**接口描述**: 启动数据流执行,支持传入执行参数。
-
-#### 请求信息
-- **HTTP方法**: `POST`
-- **请求路径**: `/execute-dataflow/{dataflow_id}`
-- **请求头**: `Content-Type: application/json`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| params | object | 否 | 执行参数,JSON对象 |
-
-#### 请求体示例
-
-```json
-{
-  "params": {
-    "start_date": "2023-12-01",
-    "end_date": "2023-12-07",
-    "force_refresh": true
-  }
-}
-```
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "数据流执行成功",
-  "data": {
-    "execution_id": "exec_1_1701420000",
-    "dataflow_id": 1,
-    "status": "running",
-    "started_at": "2023-12-01T16:00:00",
-    "params": {
-      "start_date": "2023-12-01",
-      "end_date": "2023-12-07",
-      "force_refresh": true
-    },
-    "progress": 0
-  }
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-dataflow_id = 1
-url = f"http://your-domain/api/data-flow/execute-dataflow/{dataflow_id}"
-data = {
-    "params": {
-        "batch_size": 500,
-        "parallel_workers": 4
-    }
-}
-
-response = requests.post(url, json=data)
-result = response.json()
-
-if result["code"] == 200:
-    execution_id = result["data"]["execution_id"]
-    print(f"执行开始,执行ID: {execution_id}")
-```
-
----
-
-### 7. 获取数据流执行状态
-
-**接口描述**: 查询数据流的当前执行状态和进度。
-
-#### 请求信息
-- **HTTP方法**: `GET`
-- **请求路径**: `/get-dataflow-status/{dataflow_id}`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "dataflow_id": 1,
-    "status": "running",
-    "progress": 45,
-    "started_at": "2023-12-01T16:00:00",
-    "completed_at": null,
-    "error_message": null
-  }
-}
-```
-
-#### 状态说明
-
-| 状态值 | 含义 | 说明 |
-|--------|------|------|
-| pending | 等待中 | 数据流等待执行 |
-| running | 运行中 | 数据流正在执行 |
-| completed | 已完成 | 数据流执行成功完成 |
-| failed | 执行失败 | 数据流执行过程中出现错误 |
-
-#### 示例代码
-
-**JavaScript示例**:
-```javascript
-function checkDataflowStatus(dataflowId) {
-    fetch(`/api/data-flow/get-dataflow-status/${dataflowId}`)
-        .then(response => response.json())
-        .then(data => {
-            if (data.code === 200) {
-                const status = data.data;
-                console.log(`状态: ${status.status}, 进度: ${status.progress}%`);
-                
-                if (status.status === 'running') {
-                    // 如果还在运行,5秒后再次检查
-                    setTimeout(() => checkDataflowStatus(dataflowId), 5000);
-                }
-            }
-        });
-}
-```
-
----
-
-### 8. 获取数据流执行日志
-
-**接口描述**: 获取数据流执行过程中的详细日志信息,支持分页。
-
-#### 请求信息
-- **HTTP方法**: `GET`
-- **请求路径**: `/get-dataflow-logs/{dataflow_id}`
-
-#### 路径参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| dataflow_id | int | 是 | 数据流ID |
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 默认值 | 说明 |
-|--------|------|------|--------|------|
-| page | int | 否 | 1 | 页码 |
-| page_size | int | 否 | 50 | 每页记录数 |
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "success",
-  "data": {
-    "logs": [
-      {
-        "id": 1,
-        "timestamp": "2023-12-01T16:00:00",
-        "level": "INFO",
-        "message": "数据流开始执行",
-        "component": "source"
-      },
-      {
-        "id": 2,
-        "timestamp": "2023-12-01T16:00:30",
-        "level": "INFO",
-        "message": "成功连接到数据源",
-        "component": "source"
-      },
-      {
-        "id": 3,
-        "timestamp": "2023-12-01T16:01:00",
-        "level": "WARNING",
-        "message": "发现重复数据,已自动处理",
-        "component": "transform"
-      }
-    ],
-    "pagination": {
-      "page": 1,
-      "page_size": 50,
-      "total": 100,
-      "total_pages": 2
-    }
-  }
-}
-```
-
-#### 日志级别说明
-
-| 级别 | 含义 | 说明 |
-|------|------|------|
-| INFO | 信息 | 一般性信息日志 |
-| WARNING | 警告 | 警告信息,不影响执行 |
-| ERROR | 错误 | 错误信息,可能导致执行失败 |
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-dataflow_id = 1
-url = f"http://your-domain/api/data-flow/get-dataflow-logs/{dataflow_id}"
-params = {"page": 1, "page_size": 20}
-
-response = requests.get(url, params=params)
-data = response.json()
-
-if data["code"] == 200:
-    logs = data["data"]["logs"]
-    for log in logs:
-        print(f"[{log['timestamp']}] {log['level']}: {log['message']}")
-```
-
----
-
-### 9. 使用AI生成脚本
-
-**接口描述**: 使用Deepseek AI模型根据文本描述生成数据处理脚本。
-
-#### 请求信息
-- **HTTP方法**: `POST`
-- **请求路径**: `/create-script`
-- **请求头**: `Content-Type: application/json`
-
-#### 请求参数
-
-| 参数名 | 类型 | 必填 | 说明 |
-|--------|------|------|------|
-| request_data | string | 是 | 需求描述文本 |
-
-#### 请求体示例
-
-```json
-{
-  "request_data": "请生成一个Python脚本,从MySQL数据库读取用户表数据,清洗无效数据后写入Elasticsearch。需要包含错误处理和日志记录功能。"
-}
-```
-
-#### 响应数据
-
-```json
-{
-  "code": 200,
-  "message": "脚本生成成功",
-  "data": {
-    "script_content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport mysql.connector\nfrom elasticsearch import Elasticsearch\nimport logging\nimport json\nfrom datetime import datetime\n\n# 配置日志\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\ndef main():\n    try:\n        # MySQL连接配置\n        mysql_config = {\n            'host': 'localhost',\n            'user': 'username',\n            'password': 'password',\n            'database': 'database_name'\n        }\n        \n        # Elasticsearch连接配置\n        es = Elasticsearch([{'host': 'localhost', 'port': 9200}])\n        \n        # 连接MySQL\n        conn = mysql.connector.connect(**mysql_config)\n        cursor = conn.cursor(dictionary=True)\n        \n        # 查询用户数据\n        query = \"SELECT * FROM users WHERE status = 'active'\"\n        cursor.execute(query)\n        \n        # 处理数据\n        for row in cursor:\n            # 数据清洗\n            if validate_user_data(row):\n                # 写入Elasticsearch\n                es.index(index='users', body=row)\n                logger.info(f\"用户数据已写入ES: {row['id']}\")\n            else:\n                logger.warning(f\"无效用户数据跳过: {row['id']}\")\n                \n    except Exception as e:\n        logger.error(f\"脚本执行失败: {str(e)}\")\n    finally:\n        if 'conn' in locals():\n            conn.close()\n\ndef validate_user_data(user):\n    \"\"\"验证用户数据有效性\"\"\"\n    return user.get('email') and '@' in user.get('email', '')\n\nif __name__ == '__main__':\n    main()",
-    "format": "txt",
-    "generated_at": "2023-12-01T16:30:00"
-  }
-}
-```
-
-#### 示例代码
-
-**Python示例**:
-```python
-import requests
-
-url = "http://your-domain/api/data-flow/create-script"
-data = {
-    "request_data": "生成一个数据备份脚本,每天凌晨2点自动备份PostgreSQL数据库到云存储"
-}
-
-response = requests.post(url, json=data)
-result = response.json()
-
-if result["code"] == 200:
-    script = result["data"]["script_content"]
-    # 保存脚本到文件
-    with open("backup_script.py", "w", encoding="utf-8") as f:
-        f.write(script)
-    print("脚本生成并保存成功")
-```
-
-#### 测试数据
-```json
-{
-  "request_data": "创建一个简单的CSV文件处理脚本,读取销售数据并计算月度汇总"
-}
-```
-
----
-
-## 错误处理
-
-### 常见错误码
-
-| 错误码 | 错误类型 | 解决方案 |
-|--------|----------|----------|
-| 400 | 请求参数错误 | 检查请求参数格式和必填字段 |
-| 404 | 资源不存在 | 确认资源ID是否正确 |
-| 500 | 服务器内部错误 | 查看服务器日志,联系技术支持 |
-
-### 错误响应示例
-
-```json
-{
-  "code": 400,
-  "message": "参数错误: 缺少必填字段: name",
-  "data": null
-}
-```
-
-### 错误处理最佳实践
-
-1. **检查HTTP状态码**: 首先检查HTTP响应状态码
-2. **解析错误消息**: 根据返回的message字段了解具体错误
-3. **参数验证**: 发送请求前在客户端进行参数验证
-4. **重试机制**: 对于网络错误实现适当的重试机制
-5. **日志记录**: 记录API调用和错误信息便于排查
-
----
-
-## 使用示例
-
-### 完整工作流示例
-
-以下是一个完整的数据流管理工作流示例:
-
-```python
-import requests
-import time
-import json
-
-class DataFlowClient:
-    def __init__(self, base_url):
-        self.base_url = base_url
-    
-    def create_dataflow(self, name, description, config=None):
-        """创建数据流"""
-        url = f"{self.base_url}/add-dataflow"
-        data = {
-            "name": name,
-            "description": description,
-            "status": "active",
-            "config": config or {}
-        }
-        response = requests.post(url, json=data)
-        return response.json()
-    
-    def execute_dataflow(self, dataflow_id, params=None):
-        """执行数据流"""
-        url = f"{self.base_url}/execute-dataflow/{dataflow_id}"
-        data = {"params": params or {}}
-        response = requests.post(url, json=data)
-        return response.json()
-    
-    def wait_for_completion(self, dataflow_id, timeout=300):
-        """等待数据流执行完成"""
-        start_time = time.time()
-        
-        while time.time() - start_time < timeout:
-            url = f"{self.base_url}/get-dataflow-status/{dataflow_id}"
-            response = requests.get(url)
-            data = response.json()
-            
-            if data["code"] == 200:
-                status = data["data"]["status"]
-                progress = data["data"]["progress"]
-                
-                print(f"执行状态: {status}, 进度: {progress}%")
-                
-                if status in ["completed", "failed"]:
-                    return status
-            
-            time.sleep(5)
-        
-        return "timeout"
-
-# 使用示例
-client = DataFlowClient("http://your-domain/api/data-flow")
-
-# 1. 创建数据流
-result = client.create_dataflow(
-    "销售数据分析",
-    "分析每日销售数据并生成报表",
-    {"source": "mysql", "target": "report"}
-)
-
-if result["code"] == 200:
-    dataflow_id = result["data"]["id"]
-    print(f"数据流创建成功,ID: {dataflow_id}")
-    
-    # 2. 执行数据流
-    exec_result = client.execute_dataflow(dataflow_id, {
-        "date_range": "2023-12-01,2023-12-07"
-    })
-    
-    if exec_result["code"] == 200:
-        print("数据流开始执行")
-        
-        # 3. 等待执行完成
-        final_status = client.wait_for_completion(dataflow_id)
-        print(f"最终状态: {final_status}")
-```
-
----
-
-## 注意事项
-
-1. **认证授权**: 生产环境中需要添加适当的认证机制
-2. **限流控制**: 注意API调用频率限制
-3. **数据大小**: 大量数据操作可能需要较长时间
-4. **并发执行**: 同一数据流不能同时执行多个实例
-5. **资源清理**: 及时清理不需要的数据流以节省资源
-
----
-
-## 更新日志
-
-- **v1.0** (2025-06-10): 初始版本,包含基础数据流管理功能
-- **v1.1** (待发布): 计划增加数据流模板功能
-
----
-
-## 技术支持
-
-如有问题请联系技术支持团队或查看详细的开发文档。
-
-**文档版本**: v1.0  
-**最后更新**: 2025-06-10 

+ 0 - 199
docs/meta_data_add_optimization.md

@@ -1,199 +0,0 @@
-# 元数据新增接口优化文档
-
-## 优化时间
-2026-01-12
-
-## 优化目标
-优化 `meta_node_add` 接口的冗余检测和处理逻辑,改进疑似重复元数据的处理流程。
-
-## 优化内容
-
-### 1. 完全匹配处理(保持不变)
-- **行为**:如果检测到完全匹配的元数据(name_zh、name_en、data_type、tag_ids 全部相同)
-- **操作**:直接返回失败提示,不创建任何节点
-- **返回信息**:提示元数据已存在,返回已存在的元数据ID
-
-### 2. 疑似重复处理(优化重点)
-- **旧逻辑**:
-  - 检测到疑似重复 → 立即创建审核记录 → 返回失败,不创建节点
-  - 用户需要使用 `force_create=true` 强制创建
-
-- **新逻辑**:
-  - 检测到疑似重复 → **先创建新元数据节点** → 创建审核记录(包含新节点ID和疑似重复候选) → 返回成功,提示前往审核页面处理
-  - 新元数据节点已创建,用户可以立即使用
-  - 审核记录中包含新创建的节点ID和所有疑似重复的候选元数据
-
-### 3. 无重复处理(保持不变)
-- **行为**:未检测到任何重复
-- **操作**:直接创建新节点,正常返回
-
-## 代码变更
-
-### 1. `app/api/meta_data/routes.py` - `meta_node_add` 函数
-
-#### 变更点 1:冗余检测逻辑调整
-```python
-# 旧代码:检测到疑似重复时直接返回失败
-if redundancy_result["review_created"]:
-    return jsonify(failed("发现疑似重复元数据,已创建审核记录..."))
-
-# 新代码:检测到疑似重复时标记状态,继续创建节点
-if redundancy_result["has_candidates"]:
-    has_suspicious_duplicates = True
-    suspicious_candidates = redundancy_result["candidates"]
-```
-
-#### 变更点 2:节点创建后处理疑似重复
-```python
-# 节点创建成功后
-if has_suspicious_duplicates and suspicious_candidates:
-    # 构建新元数据快照(包含新创建的节点ID)
-    new_meta_snapshot = {
-        "id": node_data["id"],  # 新创建的节点ID
-        "name_zh": node_name_zh,
-        "name_en": node_name_en or "",
-        "data_type": node_type,
-        "tag_ids": tag_ids,
-    }
-    
-    # 写入审核记录
-    write_redundancy_review_record_with_new_id(
-        new_meta=new_meta_snapshot,
-        candidates=suspicious_candidates,
-        source="api",
-    )
-    
-    # 返回成功,但提示疑似重复
-    return jsonify(success(node_data, message="元数据创建成功,但发现疑似重复..."))
-```
-
-### 2. `app/core/meta_data/redundancy_check.py`
-
-#### 变更点 1:`check_redundancy_for_add` 函数
-- **旧逻辑**:检测到疑似重复时自动创建审核记录
-- **新逻辑**:只进行检测,返回候选列表,不创建审核记录
-- **返回值变化**:移除 `review_created` 字段
-
-```python
-# 旧代码
-write_redundancy_review_record(new_meta, candidates, source="api")
-return {
-    "has_exact_match": False,
-    "exact_match_id": None,
-    "has_candidates": True,
-    "candidates": candidates,
-    "review_created": True,  # 已创建审核记录
-}
-
-# 新代码
-return {
-    "has_exact_match": False,
-    "exact_match_id": None,
-    "has_candidates": True,
-    "candidates": candidates,  # 只返回候选列表,不创建审核记录
-}
-```
-
-#### 变更点 2:新增 `write_redundancy_review_record_with_new_id` 函数
-- **用途**:在新元数据节点已创建后写入审核记录
-- **与原函数的区别**:new_meta 参数中包含已创建的节点ID
-
-```python
-def write_redundancy_review_record_with_new_id(
-    new_meta: Dict[str, Any],  # 包含已创建的节点ID
-    candidates: List[Dict[str, Any]],
-    source: str = "api",
-) -> None:
-    """
-    写入疑似冗余审核记录到 PostgreSQL(新元数据已创建,包含ID)
-    """
-    # 实现逻辑与 write_redundancy_review_record 相同
-    # 区别在于 new_meta 中包含 id 字段
-```
-
-## 业务流程对比
-
-### 旧流程
-```
-用户提交新增请求
-  ↓
-冗余检测
-  ↓
-发现疑似重复
-  ↓
-创建审核记录(不包含新节点ID)
-  ↓
-返回失败,提示使用 force_create=true
-  ↓
-用户需要再次提交请求(force_create=true)
-  ↓
-创建节点
-```
-
-### 新流程
-```
-用户提交新增请求
-  ↓
-冗余检测
-  ↓
-发现疑似重复
-  ↓
-创建新元数据节点
-  ↓
-创建审核记录(包含新节点ID + 疑似重复候选)
-  ↓
-返回成功,提示前往审核页面
-  ↓
-用户可以立即使用新节点,同时在审核页面处理重复问题
-```
-
-## 优势分析
-
-### 1. 用户体验改善
-- **旧方式**:需要两次操作(第一次失败 → 强制创建)
-- **新方式**:一次操作即可完成,节点立即可用
-
-### 2. 审核记录更完整
-- **旧方式**:审核记录中 new_meta 没有 ID,无法直接定位新创建的节点
-- **新方式**:审核记录中包含新节点ID,便于后续处理(如合并、删除等)
-
-### 3. 业务连续性
-- **旧方式**:发现疑似重复时,业务流程被中断
-- **新方式**:业务流程不中断,新节点立即可用,审核可以异步处理
-
-### 4. 灵活的后续处理
-审核人员可以根据实际情况选择:
-- 如果确认是重复:删除新节点,使用已有节点
-- 如果确认不重复:标记审核记录为已处理,保留新节点
-- 如果需要合并:合并新旧节点,更新关联关系
-
-## 注意事项
-
-1. **force_create 参数保留**:仍然支持 `force_create=true` 跳过冗余检测
-2. **完全匹配仍然拦截**:如果是完全匹配,仍然不创建新节点
-3. **审核记录格式兼容**:新的审核记录格式与旧格式兼容,只是 new_meta 中多了 id 字段
-4. **数据库事务**:审核记录写入失败不影响节点创建(已在 try-catch 外)
-
-## 测试建议
-
-### 测试场景 1:完全匹配
-- **输入**:与已有元数据完全相同的数据
-- **预期**:返回失败,提示元数据已存在,不创建新节点
-
-### 测试场景 2:疑似重复
-- **输入**:name_zh 相同但其他字段不同的数据
-- **预期**:创建新节点,创建审核记录,返回成功并提示疑似重复
-
-### 测试场景 3:无重复
-- **输入**:完全新的元数据
-- **预期**:创建新节点,不创建审核记录,正常返回
-
-### 测试场景 4:强制创建
-- **输入**:force_create=true
-- **预期**:跳过冗余检测,直接创建节点
-
-## 相关文件
-
-- `app/api/meta_data/routes.py` - API 路由层
-- `app/core/meta_data/redundancy_check.py` - 冗余检测核心逻辑
-- `app/models/metadata_review.py` - 审核记录数据模型

+ 101 - 0
docs/n8n-chat-trigger-api.md

@@ -0,0 +1,101 @@
+# n8n Chat Trigger API 规范(DataMeta 管理智能体)
+
+本文档描述 `https://n8n.citupro.com/webhook/datameta-agent/chat` 的 API 规范与调用示例。
+
+参考 URL:<https://n8n.citupro.com/webhook/datameta-agent/chat>
+
+## 1. Endpoint
+
+- URL: `https://n8n.citupro.com/webhook/datameta-agent/chat`
+- Method: `POST`
+- Content-Type: `application/json`
+
+## 2. 请求体(Request Body)
+
+```json
+{
+  "chatInput": "用户输入内容",
+  "sessionId": "会话ID",
+  "metadata": {
+    "optional_key": "optional_value"
+  }
+}
+```
+
+字段说明:
+
+- `chatInput` (string, 必填): 用户输入内容。
+- `sessionId` (string, 建议必填): 会话唯一标识,用于多轮对话记忆。
+- `metadata` (object, 可选): 额外信息(如用户ID、租户、语言等)。
+
+## 3. 响应体(Response Body)
+
+响应结构取决于 Chat Trigger 的 **Response Mode**:
+
+- When Last Node Finishes: 返回最后节点输出(通常是 agent 回复内容)。
+- Using Response Nodes: 返回 Chat / Respond to Webhook 节点中定义的消息。
+
+常见示例:
+
+```json
+{
+  "output": "这里是智能体的回复内容"
+}
+```
+
+## 4. 调用示例
+
+### 4.1 cURL
+
+```bash
+curl -X POST "https://n8n.citupro.com/webhook/datameta-agent/chat" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "chatInput": "查询ID为60的元数据",
+    "sessionId": "session-001"
+  }'
+```
+
+### 4.2 JavaScript (fetch)
+
+```js
+fetch("https://n8n.citupro.com/webhook/datameta-agent/chat", {
+  method: "POST",
+  headers: { "Content-Type": "application/json" },
+  body: JSON.stringify({
+    chatInput: "查看ID为60的元数据",
+    sessionId: "session-001",
+    metadata: { userId: "u-1001" }
+  }),
+})
+  .then((res) => res.json())
+  .then((data) => console.log(data));
+```
+
+### 4.3 Python (requests)
+
+```python
+import requests
+
+url = "https://n8n.citupro.com/webhook/datameta-agent/chat"
+payload = {
+    "chatInput": "新增元数据,中文名测试字段",
+    "sessionId": "session-001"
+}
+
+resp = requests.post(url, json=payload)
+print(resp.status_code, resp.text)
+```
+
+## 5. 认证与跨域
+
+- 如 Chat Trigger 配置了 Basic Auth 或 n8n User Auth,请在请求中携带认证信息。
+- 浏览器跨域访问需在 Chat Trigger 中配置 Allowed Origin (CORS)。
+
+## 6. 常见问题
+
+**Q: 返回 403?**  
+A: 反向代理未放行 `/webhook/chat/*` 或 workflow 未激活。
+
+**Q: 浏览器报 CORS?**  
+A: 未配置 Allowed Origin (CORS)。

+ 0 - 358
docs/n8n_add_tools_guide.md

@@ -1,358 +0,0 @@
-# Data-governance 工作流添加 Tools 指南
-
-**目标**: 为 Data-governance 工作流添加检查和创建元数据的工具  
-**日期**: 2025-11-04
-
----
-
-## ✅ 已完成的准备工作
-
-### 1. 后端 API 接口已创建
-
-#### 检查元数据接口
-```http
-GET http://192.168.3.143:5000/api/meta/check?name_zh=元数据中文名
-
-Response:
-{
-  "code": 200,
-  "data": {
-    "exists": true/false,
-    "name_zh": "元数据中文名"
-  },
-  "msg": "查询成功"
-}
-```
-
-#### 创建元数据接口
-```http
-POST http://192.168.3.143:5000/api/meta/node/add
-Content-Type: application/json
-
-{
-  "name_zh": "元数据中文名",
-  "data_type": "string",
-  "describe": "描述信息",
-  "source": "data-governance-workflow",
-  "status": true
-}
-
-Response:
-{
-  "code": 200,
-  "data": { ... },
-  "msg": "success"
-}
-```
-
-### 2. AI Agent 系统消息已更新
-
-AI Agent 现在已配置为:
-- 引导用户提供元数据信息
-- 使用 check_metadata 工具检查
-- 使用 create_metadata 工具创建
-
----
-
-## 🛠️ 在 n8n 界面中添加 Tools
-
-### 步骤 1: 登录 n8n
-
-```
-访问: https://n8n.citupro.com
-登录账号
-```
-
-### 步骤 2: 打开 Data-governance 工作流
-
-```
-1. 进入 Workflows 页面
-2. 找到 "Data-governance" 工作流
-3. 点击打开编辑器
-```
-
-### 步骤 3: 添加检查元数据工具
-
-#### 3.1 添加节点
-
-```
-1. 点击 "+" 添加节点
-2. 搜索 "HTTP Request Tool"
-3. 选择 "HTTP Request Tool" (LangChain类别)
-```
-
-#### 3.2 配置节点参数
-
-**基本配置**:
-- **Name**: `check_metadata`
-- **Description**: 
-  ```
-  检查元数据中文名是否已经存在。需要参数:name_zh(元数据中文名)。返回exists字段表示是否存在(true/false)
-  ```
-
-**HTTP Request 配置**:
-- **Method**: `GET`
-- **URL**: `http://192.168.3.143:5000/api/meta/check?name_zh={{ $parameter.name_zh }}`
-- **Authentication**: `None`
-
-**Placeholder Definitions** (参数定义):
-点击 "Add Placeholder" 添加:
-- **Name**: `name_zh`
-- **Description**: `元数据中文名`
-
-#### 3.3 连接到 AI Agent
-
-```
-1. 将 "HTTP Request Tool" 节点拖动到 AI Agent 旁边
-2. 从 "HTTP Request Tool" 的输出点拖线到 "AI Agent"
-3. 在弹出的连接类型选择中,选择 "ai_tool" 连接类型
-```
-
-### 步骤 4: 添加创建元数据工具
-
-#### 4.1 添加节点
-
-```
-1. 再次点击 "+" 添加节点
-2. 搜索 "HTTP Request Tool"
-3. 选择 "HTTP Request Tool" (LangChain类别)
-```
-
-#### 4.2 配置节点参数
-
-**基本配置**:
-- **Name**: `create_metadata`
-- **Description**:
-  ```
-  创建新的元数据。需要参数:name_zh(中文名,必填), data_type(数据类型,默认string), description(描述信息,选填)。返回创建结果
-  ```
-
-**HTTP Request 配置**:
-- **Method**: `POST`
-- **URL**: `http://192.168.3.143:5000/api/meta/node/add`
-- **Authentication**: `None`
-- **Send Body**: ✅ 启用
-- **Body Content Type**: `JSON`
-- **JSON Body**:
-  ```json
-  {
-    "name_zh": "={{ $parameter.name_zh }}",
-    "data_type": "={{ $parameter.data_type || 'string' }}",
-    "describe": "={{ $parameter.description || '' }}",
-    "source": "data-governance-workflow",
-    "status": true
-  }
-  ```
-
-**Placeholder Definitions** (参数定义):
-点击 "Add Placeholder" 三次添加:
-1. **Name**: `name_zh`, **Description**: `元数据中文名(必填)`
-2. **Name**: `data_type`, **Description**: `数据类型(string/int/float等,默认string)`
-3. **Name**: `description`, **Description**: `描述信息(选填)`
-
-#### 4.3 连接到 AI Agent
-
-```
-1. 从 "HTTP Request Tool" 的输出点拖线到 "AI Agent"
-2. 选择 "ai_tool" 连接类型
-```
-
-### 步骤 5: 清理旧节点(可选)
-
-如果工作流中还有这些节点,可以删除它们(它们已经不需要了):
-- "判断用户意图" (IF节点)
-- "调用元数据新增API" (HTTP Request节点)
-- "设置确认消息" (Set节点)
-- "设置拒绝消息" (Set节点)
-
-**注意**: 只保留以下核心节点:
-- Chat Trigger
-- AI Agent
-- DeepSeek Chat Model
-- check_metadata (HTTP Request Tool)
-- create_metadata (HTTP Request Tool)
-
-### 步骤 6: 保存并激活
-
-```
-1. 点击右上角 "Save" 保存工作流
-2. 如果工作流未激活,点击 "Active" 开关激活
-```
-
----
-
-## 🧪 测试工作流
-
-### 测试场景 1: 完整流程
-
-```
-访问: https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-
-用户: "是,我要创建元数据"
-预期: AI 询问元数据信息
-
-用户: "中文名:测试字段001,类型:string,描述:这是一个测试字段"
-预期: 
-1. AI 使用 check_metadata 检查是否存在
-2. 如果不存在,使用 create_metadata 创建
-3. 返回成功消息
-```
-
-### 测试场景 2: 元数据已存在
-
-```
-用户: "是,我要创建元数据"
-AI: "好的!请提供元数据信息..."
-
-用户: "中文名:测试字段001,类型:string"
-预期: AI 提示该元数据已存在
-```
-
-### 测试场景 3: 信息不完整
-
-```
-用户: "是,我要创建元数据"
-AI: "好的!请提供元数据信息..."
-
-用户: "类型是string"
-预期: AI 提示需要提供中文名
-```
-
----
-
-## 📊 工作流最终结构
-
-```
-┌────────────────┐
-│  Chat Trigger  │
-└───────┬────────┘
-        │
-        ↓
-┌────────────────┐      ┌─────────────────────┐
-│   AI Agent     │◄─────│ DeepSeek Chat Model │
-└───────┬────────┘      └─────────────────────┘
-        │
-        │ (ai_tool连接)
-        ├──────────────────────┐
-        │                      │
-        ↓                      ↓
-┌────────────────┐    ┌─────────────────┐
-│ check_metadata │    │ create_metadata │
-│ (HTTP Tool)    │    │ (HTTP Tool)     │
-└────────────────┘    └─────────────────┘
-```
-
-**关键连接**:
-- `Chat Trigger` → `AI Agent` (main 连接)
-- `DeepSeek Chat Model` → `AI Agent` (ai_languageModel 连接)
-- `check_metadata` → `AI Agent` (ai_tool 连接)
-- `create_metadata` → `AI Agent` (ai_tool 连接)
-
----
-
-## ⚠️ 重要注意事项
-
-### 1. 连接类型
-
-**ai_tool 连接** 是特殊的连接类型:
-- 不是普通的 main 连接
-- 表示该节点是 AI Agent 可以使用的工具
-- AI Agent 会根据用户输入自动决定何时调用哪个工具
-
-### 2. Tool Description 很重要
-
-Tool 的 Description 会告诉 AI:
-- 这个工具的功能是什么
-- 需要什么参数
-- 返回什么结果
-
-**AI 根据 Description 来决定是否调用该工具**。
-
-### 3. 参数传递
-
-Tool 的参数 (placeholders) 由 AI Agent 从对话中提取:
-- 用户说:"中文名是用户姓名"
-- AI 提取: `name_zh = "用户姓名"`
-- 调用: `check_metadata(name_zh="用户姓名")`
-
-### 4. JSON Body 中的表达式
-
-使用 n8n 表达式语法:
-- `={{ $parameter.name_zh }}` - 获取参数值
-- `={{ $parameter.data_type || 'string' }}` - 默认值
-- 需要用双花括号 `={{ }}`
-
----
-
-## 🔧 调试技巧
-
-### 查看工具调用日志
-
-```
-1. 在 n8n 界面打开 "Executions"
-2. 找到最近的执行记录
-3. 展开查看每个节点的输入输出
-4. 检查 Tool 是否被正确调用
-```
-
-### 测试单个工具
-
-```
-1. 在编辑器中点击 Tool 节点
-2. 点击 "Test node"
-3. 手动输入参数值
-4. 查看返回结果
-```
-
-### 常见问题
-
-| 问题 | 原因 | 解决 |
-|------|------|------|
-| AI 不调用工具 | Description 不清晰 | 优化 Description,明确说明功能和参数 |
-| 工具调用失败 | API 不可达 | 检查 192.168.3.143 的网络连接 |
-| 参数未传递 | Placeholder 未定义 | 检查 Placeholder Definitions |
-| 返回格式错误 | API 返回不符合预期 | 检查后端 API 返回格式 |
-
----
-
-## ✅ 完成验收
-
-工作流配置完成后,应该满足:
-
-- [ ] 两个 HTTP Request Tool 节点已添加
-- [ ] 工具通过 ai_tool 连接到 AI Agent
-- [ ] check_metadata 工具配置正确
-- [ ] create_metadata 工具配置正确
-- [ ] AI Agent 系统消息已更新
-- [ ] 工作流已保存并激活
-- [ ] 测试场景 1 通过
-- [ ] 测试场景 2 通过
-- [ ] 测试场景 3 通过
-
----
-
-## 📚 相关资源
-
-### n8n 官方文档
-- [HTTP Request Tool](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolhttprequest/)
-- [AI Agent](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/)
-- [LangChain Tools](https://docs.n8n.io/integrations/langchain/tools/)
-
-### 项目文档
-- [工作流改进方案](./n8n_improved_workflow_design.md)
-- [Chat Trigger 错误诊断](./n8n_chat_trigger_error_diagnosis.md)
-
----
-
-## 🎯 下一步
-
-1. **立即执行**: 按照本指南在 n8n 界面中添加 Tools
-2. **测试验证**: 使用测试场景验证功能
-3. **优化提示词**: 根据测试结果优化 AI 系统消息
-4. **监控运行**: 观察工作流执行日志,持续优化
-
-**预计配置时间**: 15-20 分钟  
-**难度**: ⭐⭐⭐ (中等)
-
-祝您配置顺利!🚀
-

+ 0 - 383
docs/n8n_chat_workflow_quickstart.md

@@ -1,383 +0,0 @@
-# n8n 数据治理聊天工作流 - 快速开始指南
-
-## 🎉 工作流已创建成功!
-
-**工作流ID**: `tWfjLZE1FmMfQAIn`  
-**工作流名称**: Data-governance  
-**触发方式**: Chat Trigger(聊天触发器)
-
----
-
-## 📋 快速开始步骤
-
-### 第 1 步:配置 OpenAI API 凭证 ⚠️
-
-这是**必需**的步骤!
-
-1. 登录 n8n 界面
-2. 进入 **Settings** → **Credentials**
-3. 点击 **"Add Credential"** 按钮
-4. 在搜索框中输入 "OpenAI"
-5. 选择 **"OpenAI API"**
-6. 输入您的 OpenAI API Key(格式:`sk-...`)
-7. 点击 **"Save"** 保存
-
-### 第 2 步:连接凭证到工作流
-
-1. 打开 **"Data-governance"** 工作流
-2. 找到 **"OpenAI Chat Model"** 节点(在画布下方)
-3. 点击该节点打开配置面板
-4. 在 **"Credential to connect with"** 字段中
-5. 选择刚才创建的 OpenAI API 凭证
-6. 点击 **"Save"** 或直接关闭面板(自动保存)
-
-### 第 3 步:激活工作流 🚀
-
-1. 在工作流编辑器右上角
-2. 找到 **"Active"** 开关(滑块)
-3. 点击开关,将其切换到 **ON** 状态
-4. 等待几秒钟,工作流激活成功后会显示绿色
-
-### 第 4 步:获取聊天界面 URL 🔗
-
-1. 点击画布上的 **"Chat Trigger"** 节点
-2. 在右侧配置面板中,找到 **"Chat URL"** 或 **"Production URL"**
-3. 复制这个 URL(格式类似:`http://your-n8n-server/chat/tWfjLZE1FmMfQAIn`)
-
-### 第 5 步:测试聊天界面 💬
-
-1. 在浏览器新标签页中打开复制的 URL
-2. 您将看到一个聊天界面,标题为 **"数据治理助手 🤖"**
-3. 界面会自动显示欢迎消息
-4. 在输入框中输入 **"是"** 或 **"否"**
-5. 点击发送按钮,观察 AI 的响应
-
----
-
-## 🎯 测试场景
-
-### 场景 1: 确认元数据管理
-
-```
-用户输入: "是"
-AI 响应: CONFIRM_METADATA
-系统操作: 调用 /api/meta/add 创建元数据
-最终响应: "好的!已为您发起元数据新增工作流程。操作结果:成功创建元数据..."
-```
-
-### 场景 2: 拒绝元数据管理
-
-```
-用户输入: "否"
-AI 响应: REJECT_METADATA
-最终响应: "好的,已取消元数据管理操作。还有其他需要帮助的吗?..."
-```
-
-### 场景 3: 不明确的回答
-
-```
-用户输入: "我不太确定"
-AI 响应: "请明确回答是否需要进行元数据管理(是/否)?"
-```
-
----
-
-## 🔧 常见问题排查
-
-### 问题 1: 无法激活工作流
-
-**错误信息**: "This workflow has no trigger nodes that require activation"
-
-**原因**: 使用了 Manual Trigger 而不是 Chat Trigger
-
-**解决方案**: ✅ 已修复!当前工作流使用 Chat Trigger,可以正常激活
-
----
-
-### 问题 2: AI Agent 节点报错
-
-**错误信息**: "No language model connected"
-
-**原因**: OpenAI Chat Model 节点未连接凭证
-
-**解决方案**:
-1. 检查 OpenAI Chat Model 节点是否已连接到 AI Agent
-2. 确认 OpenAI API 凭证已配置
-3. 在 OpenAI Chat Model 节点中选择凭证
-
----
-
-### 问题 3: 聊天界面无法访问
-
-**错误信息**: 404 Not Found
-
-**原因**: 工作流未激活
-
-**解决方案**:
-1. 确认工作流已激活(Active 开关为 ON)
-2. 重新复制 Chat URL
-3. 如果问题依然存在,重启 n8n 服务
-
----
-
-### 问题 4: OpenAI API 调用失败
-
-**错误信息**: "API request failed" 或 "Invalid API key"
-
-**原因**: API Key 无效或无法访问 OpenAI
-
-**解决方案**:
-
-**方案 A: 检查 API Key**
-```bash
-1. 登录 OpenAI 官网 (https://platform.openai.com)
-2. 进入 API Keys 页面
-3. 验证 API Key 是否有效
-4. 检查账户是否有足够的额度
-```
-
-**方案 B: 使用代理**
-```bash
-1. 在 OpenAI Chat Model 节点配置中
-2. 找到 "Base URL" 选项
-3. 输入代理地址(如:https://api.openai-proxy.com/v1)
-4. 保存并测试
-```
-
-**方案 C: 使用替代模型**
-
-将 OpenAI Chat Model 替换为:
-
-- **Ollama Chat Model**(本地免费)
-  ```bash
-  # 安装 Ollama
-  curl -fsSL https://ollama.com/install.sh | sh
-  
-  # 拉取模型
-  ollama pull llama2
-  
-  # 在 n8n 中配置
-  Base URL: http://localhost:11434
-  Model: llama2
-  ```
-
-- **Groq Chat Model**(快速且有免费额度)
-  ```bash
-  1. 注册 Groq 账号 (https://console.groq.com)
-  2. 获取 API Key
-  3. 在 n8n 中添加 Groq 凭证
-  4. 替换模型节点
-  ```
-
----
-
-### 问题 5: API 调用失败
-
-**错误信息**: "Failed to call /api/meta/add"
-
-**原因**: DataOps 平台 API 不可访问
-
-**解决方案**:
-1. 检查 DataOps 平台是否正常运行
-2. 验证 API 端点 URL 是否正确(当前:`http://localhost:5000`)
-3. 如果 n8n 和 DataOps 不在同一台服务器,需要修改为实际 IP
-4. 测试 API 连通性:
-   ```bash
-   curl -X POST http://localhost:5000/api/meta/add \
-     -H "Content-Type: application/json" \
-     -d '{"name_zh":"测试","data_type":"string"}'
-   ```
-
----
-
-## 📊 工作流监控
-
-### 查看执行历史
-
-1. 在 n8n 界面中,点击左侧菜单的 **"Executions"**
-2. 找到 **"Data-governance"** 工作流的执行记录
-3. 点击任意执行记录查看详情
-4. 可以看到每个节点的输入输出数据
-
-### 调试技巧
-
-**查看节点数据**:
-```bash
-1. 点击工作流画布上的任意节点
-2. 在右侧面板中切换到 "Output" 标签
-3. 查看该节点的输出数据
-```
-
-**测试单个节点**:
-```bash
-1. 点击节点
-2. 点击 "Test step" 按钮
-3. 查看执行结果
-```
-
----
-
-## 🚀 集成到 DataOps 平台
-
-### 方式 1: iframe 嵌入(推荐)
-
-在 DataOps 平台的前端代码中添加:
-
-```html
-<!-- 在页面中添加聊天窗口 -->
-<div id="chat-container" style="position: fixed; bottom: 20px; right: 20px; z-index: 1000;">
-  <iframe 
-    src="http://your-n8n-server/chat/tWfjLZE1FmMfQAIn"
-    width="400"
-    height="600"
-    frameborder="0"
-    style="border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);"
-  ></iframe>
-</div>
-```
-
-### 方式 2: 悬浮按钮
-
-添加一个悬浮按钮,点击后打开聊天窗口:
-
-```html
-<!-- HTML -->
-<button id="chat-btn" class="floating-chat-btn" onclick="openChat()">
-  🤖 数据治理助手
-</button>
-
-<!-- CSS -->
-<style>
-.floating-chat-btn {
-  position: fixed;
-  bottom: 20px;
-  right: 20px;
-  padding: 15px 20px;
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-  color: white;
-  border: none;
-  border-radius: 50px;
-  cursor: pointer;
-  font-size: 16px;
-  font-weight: bold;
-  box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
-  transition: all 0.3s ease;
-  z-index: 1000;
-}
-
-.floating-chat-btn:hover {
-  transform: translateY(-2px);
-  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
-}
-</style>
-
-<!-- JavaScript -->
-<script>
-function openChat() {
-  const chatUrl = 'http://your-n8n-server/chat/tWfjLZE1FmMfQAIn';
-  window.open(
-    chatUrl,
-    'data-governance-chat',
-    'width=450,height=700,resizable=yes,scrollbars=yes'
-  );
-}
-</script>
-```
-
-### 方式 3: React 组件
-
-如果 DataOps 使用 React,可以创建一个组件:
-
-```jsx
-import React, { useState } from 'react';
-
-function DataGovernanceChat() {
-  const [isOpen, setIsOpen] = useState(false);
-  const chatUrl = 'http://your-n8n-server/chat/tWfjLZE1FmMfQAIn';
-
-  return (
-    <>
-      {isOpen && (
-        <div className="chat-modal">
-          <div className="chat-header">
-            <span>🤖 数据治理助手</span>
-            <button onClick={() => setIsOpen(false)}>×</button>
-          </div>
-          <iframe
-            src={chatUrl}
-            width="100%"
-            height="550"
-            frameBorder="0"
-          />
-        </div>
-      )}
-      
-      <button 
-        className="floating-chat-btn"
-        onClick={() => setIsOpen(!isOpen)}
-      >
-        🤖 数据治理助手
-      </button>
-    </>
-  );
-}
-
-export default DataGovernanceChat;
-```
-
----
-
-## 📈 下一步
-
-工作流已成功运行后,您可以:
-
-### 1. 扩展功能
-- ✅ 添加更多数据治理操作(数据质量检查、标准制定等)
-- ✅ 支持多轮对话,收集更多用户输入
-- ✅ 添加数据验证和错误处理
-
-### 2. 优化体验
-- ✅ 自定义聊天界面样式
-- ✅ 添加更丰富的欢迎消息
-- ✅ 支持文件上传和下载
-
-### 3. 监控和分析
-- ✅ 查看执行历史和成功率
-- ✅ 分析用户对话模式
-- ✅ 优化 AI 提示词
-
----
-
-## 📞 获取帮助
-
-如有问题,请参考:
-
-- **n8n 官方文档**: https://docs.n8n.io
-- **LangChain 集成**: https://docs.n8n.io/integrations/langchain/
-- **Chat Trigger 文档**: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.chatTrigger/
-- **项目文档**: `N8N_WORKFLOW_SUMMARY.md`
-
----
-
-## ✅ 检查清单
-
-使用此清单确保一切配置正确:
-
-- [ ] n8n 服务正常运行
-- [ ] OpenAI API 凭证已配置
-- [ ] OpenAI Chat Model 节点已连接凭证
-- [ ] 工作流已激活(Active 开关为 ON)
-- [ ] Chat URL 可以正常访问
-- [ ] 聊天界面显示欢迎消息
-- [ ] 输入"是"后能正常触发元数据创建
-- [ ] DataOps 平台 API 可访问
-- [ ] `/api/meta/add` 接口正常工作
-
-全部完成后,您的数据治理聊天工作流就可以正式使用了!🎉
-
-
-
-
-
-
-

+ 0 - 160
docs/n8n_cohere_credential_setup.md

@@ -1,160 +0,0 @@
-# n8n Cohere API Key 凭证配置指南
-
-**目标**: 在 `n8n.citupro.com` 中配置 Cohere API Key 凭证  
-**API Key**: `4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`  
-**日期**: 2025-01-27
-
----
-
-## ✅ 凭证已创建成功!
-
-**当前凭证 ID**: `Trtn1MtQhDwHYoPO`  
-**凭证名称**: `Cohere API Key`  
-**凭证类型**: `cohereApi`  
-**创建时间**: 2026-01-19T03:55:55.019Z
-
-**注意**: 如果遇到 "Forbidden" 错误,请参考 [故障排除指南](n8n_cohere_credential_troubleshooting.md)
-
-你现在可以在工作流中使用此凭证了!
-
----
-
-## 📋 方法一:通过 Web UI 手动配置(推荐)
-
-### 步骤 1: 登录 n8n 实例
-
-1. 访问 **https://n8n.citupro.com**
-2. 使用你的账号登录
-
-### 步骤 2: 进入凭证管理页面
-
-1. 点击左侧边栏的 **"Settings"**(设置)图标
-2. 在设置菜单中,点击 **"Credentials"**(凭证)
-3. 或者直接访问:`https://n8n.citupro.com/home/credentials`
-
-### 步骤 3: 创建新的 Cohere 凭证
-
-1. 点击页面右上角的 **"Add Credential"**(添加凭证)按钮
-2. 在搜索框中输入 **"Cohere"**
-3. 选择 **"Cohere API"** 凭证类型
-
-### 步骤 4: 填写凭证信息
-
-在凭证配置表单中填写:
-
-- **Name**(名称): `Cohere API Key` 或 `Cohere - Production`(便于识别的名称)
-- **API Key**: `4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`
-
-### 步骤 5: 保存凭证
-
-1. 点击 **"Save"**(保存)按钮
-2. n8n 会自动验证 API Key 是否有效
-3. 如果验证成功,凭证会出现在凭证列表中
-
----
-
-## 🔧 方法二:通过 API 脚本配置(实验性)
-
-如果 n8n API 支持凭证管理,可以使用以下脚本:
-
-### 使用脚本创建凭证
-
-```bash
-python scripts/create_n8n_cohere_credential.py
-```
-
-**注意**: n8n 的凭证管理 API 可能有限制,如果 API 不支持,请使用方法一(Web UI)。
-
----
-
-## ✅ 验证配置
-
-### 在工作流中使用凭证
-
-1. 打开任意包含 **Reranker Cohere** 节点的工作流
-2. 点击 **Reranker Cohere** 节点
-3. 在右侧配置面板中,找到 **"Credential to connect with"** 字段
-4. 从下拉菜单中选择刚才创建的 **"Cohere API Key"** 凭证
-5. 保存节点配置
-
-### 测试凭证是否有效
-
-1. 创建一个简单的测试工作流:
-   - 添加 **Manual Trigger** 节点
-   - 添加 **Reranker Cohere** 节点
-   - 配置模型为 `rerank-multilingual-v3.0`
-   - 连接两个节点
-2. 点击 **"Execute Workflow"** 测试
-3. 如果执行成功,说明凭证配置正确
-
----
-
-## 📝 凭证使用示例
-
-### 在工作流 JSON 中引用凭证
-
-```json
-{
-  "parameters": {
-    "modelName": "rerank-multilingual-v3.0",
-    "topN": 5
-  },
-  "type": "@n8n/n8n-nodes-langchain.rerankerCohere",
-  "name": "Reranker Cohere",
-  "credentials": {
-    "cohereApi": {
-      "id": "凭证ID",
-      "name": "Cohere API Key"
-    }
-  }
-}
-```
-
-**获取凭证 ID**:
-1. 在凭证列表中点击创建的凭证
-2. 从浏览器地址栏获取 ID:`/home/credentials/{凭证ID}`
-
----
-
-## 🔒 安全注意事项
-
-1. **不要公开 API Key**: 在分享工作流 JSON 或截图时,不要包含 API Key
-2. **权限管理**: 确保只有授权用户可以访问凭证
-3. **定期轮换**: 建议定期更换 API Key 以提高安全性
-4. **凭证加密**: n8n 会自动加密存储凭证,不会明文显示
-
----
-
-## 🆘 常见问题
-
-### Q1: 找不到 "Cohere API" 凭证类型?
-
-**A**: 确保你的 n8n 版本支持 Cohere 节点。如果使用的是较旧版本,可能需要:
-- 更新 n8n 到最新版本
-- 或安装 `@n8n/n8n-nodes-langchain` 包
-
-### Q2: API Key 验证失败?
-
-**A**: 检查:
-- API Key 是否正确复制(没有多余空格)
-- Cohere 账户是否有效
-- 网络连接是否正常
-
-### Q3: 如何查看凭证 ID?
-
-**A**: 
-1. 进入 Settings → Credentials
-2. 点击目标凭证
-3. 从浏览器地址栏获取:`/home/credentials/{凭证ID}`
-
----
-
-## 📚 相关文档
-
-- [n8n Cohere 节点文档](https://docs.n8n.io/integrations/builtin/credentials/cohere/)
-- [n8n 凭证管理指南](https://docs.n8n.io/credentials/add-edit-credentials/)
-- [Cohere API 文档](https://docs.cohere.com/)
-
----
-
-**配置完成后,你就可以在工作流中使用 Cohere Reranker 节点了!** 🎉

+ 0 - 188
docs/n8n_cohere_credential_troubleshooting.md

@@ -1,188 +0,0 @@
-# n8n Cohere API Key 凭证故障排除指南
-
-## 🔴 错误信息
-
-```
-Couldn't connect with these settings
-Forbidden - perhaps check your credentials?
-```
-
----
-
-## ✅ 已完成的修复
-
-**新凭证 ID**: `Trtn1MtQhDwHYoPO`  
-**凭证名称**: `Cohere API Key`  
-**创建时间**: 2026-01-19T03:55:55.019Z
-
-旧凭证 `xpFKP9JT4PmYZw61` 已删除,新凭证已创建。
-
----
-
-## 🔍 问题诊断步骤
-
-### 步骤 1: 验证 API Key 本身是否有效
-
-运行测试脚本:
-
-```bash
-python scripts/test_cohere_api_key.py 4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C
-```
-
-**预期结果**: 应该返回 `成功: True` 和 `状态码: 200`
-
-如果测试失败,说明 API Key 本身有问题,需要:
-- 检查 Cohere Dashboard: https://dashboard.cohere.com/
-- 确认 API Key 是否有效
-- 检查账户状态和权限
-
-### 步骤 2: 检查 n8n 凭证配置
-
-1. 访问凭证管理页面: https://n8n.citupro.com/home/credentials
-2. 找到 "Cohere API Key" 凭证(ID: `Trtn1MtQhDwHYoPO`)
-3. 点击打开凭证详情
-4. 检查 API Key 字段:
-   - 确保没有多余的空格
-   - 确保没有换行符
-   - 确保完整复制了所有字符
-
-### 步骤 3: 在工作流中重新选择凭证
-
-1. 打开使用 Cohere Reranker 节点的工作流
-2. 点击 **Reranker Cohere** 节点
-3. 在 **"Credential to connect with"** 字段中:
-   - 先选择 "None"(清除旧凭证)
-   - 保存节点
-   - 再次打开节点
-   - 选择 "Cohere API Key" 凭证
-4. 保存节点配置
-
-### 步骤 4: 测试节点连接
-
-1. 在 Reranker Cohere 节点配置中,点击 **"Test step"** 或 **"Execute Node"**
-2. 观察是否还有 "Forbidden" 错误
-
----
-
-## 🛠️ 常见原因和解决方案
-
-### 原因 1: API Key 格式问题
-
-**症状**: API Key 在复制时可能包含隐藏字符
-
-**解决方案**:
-1. 在 n8n 凭证编辑页面,完全删除 API Key 字段内容
-2. 重新输入 API Key(不要复制粘贴,手动输入)
-3. 或者使用脚本重新创建凭证
-
-### 原因 2: 凭证缓存问题
-
-**症状**: 即使更新了凭证,节点仍然使用旧配置
-
-**解决方案**:
-1. 在工作流中,先断开凭证连接(选择 "None")
-2. 保存工作流
-3. 重新打开节点,选择新凭证
-4. 保存工作流
-
-### 原因 3: Cohere 账户权限问题
-
-**症状**: API Key 有效,但某些功能被限制
-
-**解决方案**:
-1. 检查 Cohere Dashboard 中的账户状态
-2. 确认账户是否有使用 Reranker API 的权限
-3. 检查 API 使用限制和配额
-
-### 原因 4: n8n 版本兼容性问题
-
-**症状**: 凭证类型不被识别
-
-**解决方案**:
-1. 检查 n8n 版本是否支持 Cohere 节点
-2. 确保安装了 `@n8n/n8n-nodes-langchain` 包
-3. 更新 n8n 到最新版本
-
----
-
-## 🔧 手动修复步骤
-
-如果脚本修复不起作用,请手动修复:
-
-### 方法 1: 通过 Web UI 更新凭证
-
-1. 访问: https://n8n.citupro.com/home/credentials
-2. 找到 "Cohere API Key" 凭证
-3. 点击编辑
-4. 在 API Key 字段中:
-   - 完全删除现有内容
-   - 重新输入: `4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`
-   - **注意**: 确保没有前后空格
-5. 点击 "Test connection" 或 "Save"
-6. 如果测试失败,检查错误信息
-
-### 方法 2: 删除并重新创建凭证
-
-1. 访问凭证管理页面
-2. 删除现有的 "Cohere API Key" 凭证
-3. 创建新凭证:
-   - 类型: Cohere API
-   - 名称: Cohere API Key
-   - API Key: `4pLcF0CGE7LeDmAudBQHdvAxGaKwNOKfxUGkHb5C`
-4. 保存并测试
-
-### 方法 3: 使用脚本重新创建
-
-```bash
-# 删除旧凭证并创建新凭证
-python scripts/update_n8n_cohere_credential.py --recreate
-
-# 或者指定要删除的凭证 ID
-python scripts/update_n8n_cohere_credential.py --recreate --credential-id <旧凭证ID>
-```
-
----
-
-## 📋 验证清单
-
-完成修复后,请验证以下项目:
-
-- [ ] API Key 测试脚本返回成功
-- [ ] n8n 凭证配置中 API Key 正确(无空格、无换行)
-- [ ] 工作流节点已选择正确的凭证
-- [ ] 节点测试/执行不再报 "Forbidden" 错误
-- [ ] 可以正常使用 Reranker 功能
-
----
-
-## 🆘 如果问题仍然存在
-
-如果按照以上步骤操作后问题仍然存在,请检查:
-
-1. **网络连接**: 确保 n8n 服务器可以访问 `api.cohere.ai`
-2. **防火墙规则**: 检查是否有防火墙阻止了 API 请求
-3. **Cohere 服务状态**: 检查 Cohere 服务是否正常运行
-4. **n8n 日志**: 查看 n8n 服务器日志获取详细错误信息
-
-### 获取详细错误信息
-
-1. 在 n8n 工作流编辑器中,点击失败的节点
-2. 查看 "Execution Data" 或 "Error" 标签
-3. 复制完整的错误信息
-4. 检查错误堆栈和详细信息
-
----
-
-## 📞 联系支持
-
-如果问题持续存在,请提供以下信息:
-
-1. n8n 版本号
-2. Cohere API Key 测试结果(使用测试脚本)
-3. 完整的错误信息(从 n8n 节点执行结果中复制)
-4. 凭证配置截图(隐藏 API Key 敏感部分)
-
----
-
-**最后更新**: 2026-01-19  
-**凭证 ID**: `Trtn1MtQhDwHYoPO`

+ 0 - 256
docs/n8n_deepseek_upgrade.md

@@ -1,256 +0,0 @@
-# n8n 工作流升级到 DeepSeek - 更新说明
-
-## ✅ 升级完成
-
-**更新时间**: 2025-11-04 06:59:35  
-**工作流 ID**: tWfjLZE1FmMfQAIn  
-**工作流名称**: Data-governance
-
----
-
-## 🔄 更新内容
-
-### 已完成的更改
-
-#### 1. 语言模型替换
-
-**之前**:
-- ❌ OpenAI Chat Model
-- 节点类型: `@n8n/n8n-nodes-langchain.lmChatOpenAi`
-- 模型: gpt-4o-mini
-
-**现在**:
-- ✅ DeepSeek Chat Model
-- 节点类型: `@n8n/n8n-nodes-langchain.lmChatDeepSeek`
-- 模型: deepseek-chat
-
-#### 2. 节点配置
-
-```json
-{
-  "name": "DeepSeek Chat Model",
-  "type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
-  "parameters": {
-    "model": "deepseek-chat",
-    "options": {
-      "temperature": 0.7,
-      "maxTokens": 500
-    }
-  }
-}
-```
-
-#### 3. 连接关系
-
-DeepSeek Chat Model → AI Agent (ai_languageModel 连接)
-
----
-
-## 🎯 DeepSeek 的优势
-
-### 1. 国内可直接访问 ✨
-- ✅ 无需 VPN 或代理
-- ✅ 访问速度快
-- ✅ 稳定性高
-
-### 2. 性能优秀
-- ✅ 响应速度快
-- ✅ 理解能力强
-- ✅ 中文支持优秀
-
-### 3. 成本优势
-- ✅ 价格更实惠
-- ✅ 性价比高
-- ✅ 适合生产环境
-
-### 4. 易于使用
-- ✅ API 兼容 OpenAI
-- ✅ 文档完善
-- ✅ 社区活跃
-
----
-
-## 📋 后续步骤
-
-### 1. 配置 DeepSeek 凭证(如果还没有)
-
-虽然您已经添加了 DeepSeek 凭证,但如果需要更新或重新配置:
-
-```bash
-1. 登录 n8n 界面
-2. 进入 Settings → Credentials
-3. 找到 DeepSeek API 凭证
-4. 验证 API Key 是否正确
-5. 测试连接
-```
-
-### 2. 在工作流中选择凭证
-
-```bash
-1. 打开 Data-governance 工作流
-2. 点击 "DeepSeek Chat Model" 节点
-3. 在 "Credential to connect with" 字段中
-4. 选择您的 DeepSeek API 凭证
-5. 点击保存
-```
-
-### 3. 测试工作流
-
-```bash
-1. 确认工作流已激活(Active 开关 ON)
-2. 访问聊天界面:https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn
-3. 输入"是"或"否"测试 AI 响应
-4. 验证功能是否正常
-```
-
----
-
-## 🧪 测试场景
-
-### 场景 1: 确认元数据管理
-
-**输入**: "是"  
-**预期 AI 响应**: CONFIRM_METADATA  
-**预期结果**: 调用元数据新增 API,返回成功消息
-
-### 场景 2: 拒绝元数据管理
-
-**输入**: "否"  
-**预期 AI 响应**: REJECT_METADATA  
-**预期结果**: 返回取消消息和其他选项
-
-### 场景 3: 模糊回答
-
-**输入**: "我不确定"  
-**预期 AI 响应**: "请明确回答是否需要进行元数据管理(是/否)?"  
-**预期结果**: AI 询问用户明确回答
-
----
-
-## 🔧 故障排查
-
-### 问题 1: DeepSeek API 调用失败
-
-**错误信息**: "API request failed" 或 "Invalid API key"
-
-**解决方案**:
-1. 检查 DeepSeek API Key 是否有效
-2. 登录 DeepSeek 控制台验证
-3. 确认账户有足够的额度
-4. 重新配置凭证
-
-### 问题 2: AI 响应不符合预期
-
-**问题描述**: AI 没有返回 CONFIRM_METADATA 或 REJECT_METADATA
-
-**解决方案**:
-1. 检查 AI Agent 的系统消息配置
-2. 确认 DeepSeek 模型已正确连接
-3. 调整温度参数(建议 0.3-0.7)
-4. 查看执行日志,检查 AI 的实际输出
-
-### 问题 3: 工作流执行缓慢
-
-**问题描述**: AI 响应时间过长
-
-**解决方案**:
-1. 检查网络连接
-2. 减少 maxTokens 参数(当前 500)
-3. 考虑使用更轻量的提示词
-4. 查看 DeepSeek API 状态
-
----
-
-## 📊 性能对比
-
-| 指标 | OpenAI (gpt-4o-mini) | DeepSeek (deepseek-chat) |
-|------|---------------------|-------------------------|
-| 响应速度 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
-| 中文理解 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
-| 价格 | 中等 | 较低 |
-| 可访问性 | 需要代理 | 直接访问 |
-| 稳定性 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
-
----
-
-## 🔄 回滚方案
-
-如果需要切换回 OpenAI,可以使用以下步骤:
-
-### 手动回滚
-
-1. 在 n8n 界面中打开工作流
-2. 删除 "DeepSeek Chat Model" 节点
-3. 添加 "OpenAI Chat Model" 节点
-4. 配置 OpenAI 凭证
-5. 连接到 AI Agent
-6. 保存并测试
-
-### 使用 MCP 工具回滚
-
-```bash
-# 使用 n8n MCP 工具更新工作流
-# 移除 DeepSeek 节点
-# 添加 OpenAI 节点
-# 重新建立连接
-```
-
----
-
-## 📚 相关资源
-
-### DeepSeek 文档
-- 官方网站: https://www.deepseek.com
-- API 文档: https://api-docs.deepseek.com
-- 定价信息: https://api-docs.deepseek.com/quick_start/pricing
-
-### n8n 文档
-- LangChain 节点: https://docs.n8n.io/integrations/langchain/
-- Chat Trigger: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.chatTrigger/
-
-### 项目文档
-- 快速开始: `docs/n8n_chat_workflow_quickstart.md`
-- 详细文档: `docs/n8n_workflow_data_governance.md`
-- 总结文档: `N8N_WORKFLOW_SUMMARY.md`
-
----
-
-## ✅ 验证清单
-
-升级后请验证以下项目:
-
-- [ ] DeepSeek 凭证已配置
-- [ ] DeepSeek Chat Model 节点已连接凭证
-- [ ] AI Agent 已连接到 DeepSeek Chat Model
-- [ ] 工作流已激活
-- [ ] Chat URL 可以正常访问
-- [ ] 输入"是"能触发元数据创建
-- [ ] 输入"否"能返回取消消息
-- [ ] AI 响应符合预期格式
-
----
-
-## 🎊 完成!
-
-✅ **工作流已成功升级到 DeepSeek!**
-
-**当前配置**:
-- 工作流 ID: `tWfjLZE1FmMfQAIn`
-- AI 模型: DeepSeek Chat Model
-- Chat URL: `https://n8n.citupro.com/chat/tWfjLZE1FmMfQAIn`
-- 状态: 已激活
-
-**优势**:
-- ✅ 国内可直接访问
-- ✅ 响应速度更快
-- ✅ 成本更低
-- ✅ 中文支持更好
-
-现在可以开始使用升级后的数据治理聊天工作流了!🚀
-
-
-
-
-
-
-

+ 0 - 467
docs/n8n_improved_workflow_design.md

@@ -1,467 +0,0 @@
-# Data-governance 工作流改进方案
-
-**需求**: 完善元数据管理流程  
-**日期**: 2025-11-04
-
----
-
-## 📋 新需求分析
-
-### 功能需求
-
-1. **用户确认后收集信息** ✅
-   - 当用户回答"是"时
-   - 提示用户输入元数据信息
-   - 需要收集:元数据中文名、数据类型、描述等
-
-2. **检查元数据是否存在** ✅
-   - 调用API检查元数据中文名是否已存在
-   - API地址:`http://192.168.3.143:5000/api/meta/check`
-
-3. **创建新元数据** ✅
-   - 如果不存在,调用API创建
-   - API地址:`http://192.168.3.143:5000/api/meta/add`
-
----
-
-## 🎯 推荐方案:使用 AI Agent Tools
-
-### 方案概述
-
-由于 Chat Trigger + AI Agent 的架构限制(AI Agent 必须是终点),最佳方案是:
-
-**让 AI Agent 使用 Tools(工具)来执行操作**
-
-### 工作流结构
-
-```
-Chat Trigger
-    ↓
-AI Agent (带多个工具)
-    ├─ Tool 1: 收集元数据信息
-    ├─ Tool 2: 检查元数据是否存在
-    └─ Tool 3: 创建元数据
-    ↓
-DeepSeek Chat Model (语言模型)
-```
-
-### AI Agent 的对话流程
-
-```
-用户: "是,我需要创建元数据"
-  ↓
-AI: "好的!请提供以下元数据信息:
-     1. 元数据中文名(必填)
-     2. 数据类型(string/int/float等)
-     3. 描述信息"
-  ↓
-用户: "中文名:用户姓名,类型:string,描述:用户的真实姓名"
-  ↓
-AI 使用 Tool 1: 检查"用户姓名"是否存在
-  ↓
-如果不存在 → 使用 Tool 2: 创建元数据
-  ↓
-AI: "✅ 元数据创建成功!
-     - 中文名:用户姓名
-     - 类型:string
-     - 描述:用户的真实姓名"
-```
-
----
-
-## 🛠️ 实现方案
-
-### 方案 A: 使用 HTTP Request Tools(推荐)⭐⭐⭐
-
-#### 节点配置
-
-##### 1. Chat Trigger
-```json
-{
-  "type": "@n8n/n8n-nodes-langchain.chatTrigger",
-  "parameters": {
-    "public": true,
-    "initialMessages": "您好!我是数据治理助手。\n\n我可以帮助您创建和管理元数据。\n\n请问您需要创建新的元数据吗?",
-    "options": {
-      "title": "数据治理助手 🤖",
-      "subtitle": "元数据管理专家"
-    }
-  }
-}
-```
-
-##### 2. AI Agent
-```json
-{
-  "type": "@n8n/n8n-nodes-langchain.agent",
-  "parameters": {
-    "promptType": "auto",
-    "options": {
-      "systemMessage": `你是一个专业的数据治理助手,负责帮助用户创建元数据。
-
-工作流程:
-1. 当用户表示需要创建元数据时,询问用户提供以下信息:
-   - 元数据中文名(必填)
-   - 数据类型(string/int/float/date等,默认string)
-   - 描述信息(选填)
-
-2. 收集完信息后:
-   - 使用 check_metadata 工具检查元数据是否已存在
-   - 如果不存在,使用 create_metadata 工具创建
-
-3. 返回创建结果给用户
-
-注意:
-- 友好地引导用户提供信息
-- 元数据中文名是必需的
-- 如果元数据已存在,告诉用户并询问是否需要其他帮助`
-    }
-  }
-}
-```
-
-##### 3. HTTP Request Tool - 检查元数据
-```json
-{
-  "name": "check_metadata",
-  "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
-  "parameters": {
-    "name": "check_metadata",
-    "description": "检查元数据中文名是否已经存在。输入:元数据中文名。返回:存在返回true,不存在返回false",
-    "method": "GET",
-    "url": "=http://192.168.3.143:5000/api/meta/check?name_zh={{$parameter.name_zh}}",
-    "authentication": "none"
-  }
-}
-```
-
-##### 4. HTTP Request Tool - 创建元数据
-```json
-{
-  "name": "create_metadata",
-  "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
-  "parameters": {
-    "name": "create_metadata",
-    "description": "创建新的元数据。输入参数:name_zh(中文名), data_type(数据类型), description(描述)。返回创建结果",
-    "method": "POST",
-    "url": "http://192.168.3.143:5000/api/meta/add",
-    "authentication": "none",
-    "sendBody": true,
-    "specifyBody": "json",
-    "jsonBody": {
-      "name_zh": "={{$parameter.name_zh}}",
-      "data_type": "={{$parameter.data_type || 'string'}}",
-      "description": "={{$parameter.description || ''}}",
-      "source": "data-governance-workflow"
-    }
-  }
-}
-```
-
-##### 5. DeepSeek Chat Model
-```json
-{
-  "type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
-  "parameters": {
-    "model": "deepseek-chat",
-    "options": {
-      "temperature": 0.7,
-      "maxTokens": 1000
-    }
-  },
-  "credentials": {
-    "deepSeekApi": "LSdatLFCoTSCGXmn"
-  }
-}
-```
-
-#### 连接关系
-
-```
-Chat Trigger → AI Agent (main)
-DeepSeek → AI Agent (ai_languageModel)
-check_metadata Tool → AI Agent (ai_tool)
-create_metadata Tool → AI Agent (ai_tool)
-```
-
----
-
-### 方案 B: 简化版(如果 Tools 不可用)⭐⭐
-
-如果 HTTP Request Tools 配置复杂,可以使用简化方案:
-
-#### AI Agent 系统消息
-
-```
-你是数据治理助手。
-
-当用户表示需要创建元数据时,请按以下格式收集信息:
-
-📝 请提供元数据信息:
-1. 中文名称(必填)
-2. 数据类型(string/int/float,默认string)
-3. 描述信息(选填)
-
-示例格式:
-中文名:用户姓名
-类型:string
-描述:用户的真实姓名
-
-收集完信息后,我会为您创建元数据。
-
-注意:由于当前系统限制,请确保元数据名称不重复。
-```
-
-然后通过后端API的逻辑来处理检查和创建。
-
----
-
-## 📊 对比分析
-
-| 特性 | 方案A (Tools) | 方案B (简化) |
-|------|--------------|-------------|
-| 功能完整性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
-| 实现复杂度 | 中等 | 简单 |
-| 用户体验 | 很好 | 一般 |
-| 可维护性 | 高 | 中 |
-| 实时反馈 | ✅ | ❌ |
-| 重复检查 | ✅ 自动 | ❌ 需后端处理 |
-
----
-
-## 🚀 实施步骤
-
-### 步骤 1: 准备 API 接口
-
-确保以下API在 192.168.3.143 上可用:
-
-#### 检查元数据接口
-```http
-GET /api/meta/check?name_zh=元数据中文名
-Response: {
-  "code": 200,
-  "data": {
-    "exists": true/false
-  }
-}
-```
-
-#### 创建元数据接口
-```http
-POST /api/meta/add
-Content-Type: application/json
-
-{
-  "name_zh": "元数据中文名",
-  "data_type": "string",
-  "description": "描述信息",
-  "source": "data-governance-workflow"
-}
-
-Response: {
-  "code": 200,
-  "data": {...},
-  "msg": "success"
-}
-```
-
-### 步骤 2: 在 n8n 中配置
-
-#### 方案 A 配置步骤:
-
-1. **添加 HTTP Request Tool 节点** (检查)
-   ```
-   - 节点类型:HTTP Request Tool
-   - Name: check_metadata
-   - Description: 检查元数据是否存在
-   - Method: GET
-   - URL: http://192.168.3.143:5000/api/meta/check
-   ```
-
-2. **添加 HTTP Request Tool 节点** (创建)
-   ```
-   - 节点类型: HTTP Request Tool
-   - Name: create_metadata
-   - Description: 创建新元数据
-   - Method: POST
-   - URL: http://192.168.3.143:5000/api/meta/add
-   ```
-
-3. **连接 Tools 到 AI Agent**
-   ```
-   - check_metadata → AI Agent (ai_tool端口)
-   - create_metadata → AI Agent (ai_tool端口)
-   ```
-
-4. **更新 AI Agent 系统消息**
-   ```
-   使用上面提供的系统消息内容
-   ```
-
-5. **测试工作流**
-   ```
-   输入: "是,我要创建元数据"
-   AI应该: 询问元数据信息
-   输入: "中文名:测试字段,类型:string"
-   AI应该: 检查并创建
-   ```
-
----
-
-## 🧪 测试场景
-
-### 场景 1: 完整流程 - 成功创建
-
-```
-用户: "是,我需要创建元数据"
-AI: "好的!请提供元数据信息..."
-用户: "中文名:用户年龄,类型:int,描述:用户的年龄"
-AI: [使用check_metadata检查]
-AI: [不存在,使用create_metadata创建]
-AI: "✅ 元数据创建成功!"
-```
-
-### 场景 2: 元数据已存在
-
-```
-用户: "是,我需要创建元数据"
-AI: "好的!请提供元数据信息..."
-用户: "中文名:用户姓名,类型:string"
-AI: [使用check_metadata检查]
-AI: "❌ 元数据"用户姓名"已经存在,无需重复创建。需要其他帮助吗?"
-```
-
-### 场景 3: 信息不完整
-
-```
-用户: "是,我需要创建元数据"
-AI: "好的!请提供元数据信息..."
-用户: "类型是string"
-AI: "还需要提供元数据的中文名称哦,这是必填项。"
-```
-
----
-
-## 📄 API 实现示例
-
-### 后端 API 代码(参考)
-
-#### 检查元数据接口
-```python
-@app.route('/api/meta/check', methods=['GET'])
-def check_metadata():
-    name_zh = request.args.get('name_zh')
-    
-    if not name_zh:
-        return jsonify({"code": 400, "message": "缺少name_zh参数"})
-    
-    # 查询数据库
-    exists = check_metadata_exists(name_zh)
-    
-    return jsonify({
-        "code": 200,
-        "data": {"exists": exists},
-        "msg": "success"
-    })
-```
-
-#### 创建元数据接口(已存在)
-```python
-@app.route('/api/meta/add', methods=['POST'])
-def add_metadata():
-    data = request.get_json()
-    name_zh = data.get('name_zh')
-    
-    # 检查是否存在
-    if check_metadata_exists(name_zh):
-        return jsonify({
-            "code": 409,
-            "message": "元数据已存在"
-        })
-    
-    # 创建元数据
-    result = create_metadata(data)
-    
-    return jsonify({
-        "code": 200,
-        "data": result,
-        "msg": "success"
-    })
-```
-
----
-
-## ⚠️ 注意事项
-
-### Chat Trigger 限制
-
-1. **AI Agent 必须是终点节点**
-   - 不能在 AI Agent 后面添加普通节点
-   - 只能通过 Tools 来执行操作
-
-2. **Tools 的参数传递**
-   - AI Agent 会自动从对话中提取参数
-   - 需要在 Tool 的 description 中明确说明参数
-
-3. **错误处理**
-   - Tool 执行失败时,AI Agent 会告诉用户
-   - 需要在系统消息中定义如何处理错误
-
-### API 设计建议
-
-1. **检查接口应该轻量**
-   - 只返回存在性,不返回详细信息
-   - 响应时间 < 100ms
-
-2. **创建接口应该原子性**
-   - 要么成功创建,要么失败
-   - 返回明确的错误信息
-
-3. **统一错误格式**
-   ```json
-   {
-     "code": 错误码,
-     "message": "错误描述",
-     "data": null
-   }
-   ```
-
----
-
-## ✅ 验收标准
-
-工作流完成后应满足:
-
-- [ ] 用户确认创建元数据后,AI 询问详细信息
-- [ ] AI 能正确收集:中文名、类型、描述
-- [ ] AI 自动检查元数据是否已存在
-- [ ] 如果不存在,自动创建
-- [ ] 如果已存在,友好提示用户
-- [ ] 创建成功后给出明确反馈
-- [ ] 整个流程顺畅,无需人工干预
-
----
-
-## 📚 相关文档
-
-- n8n LangChain Tools: https://docs.n8n.io/integrations/langchain/tools/
-- HTTP Request Tool: https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolhttprequest/
-- AI Agent: https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/
-
----
-
-## 🎯 下一步
-
-1. **确认 API 接口** - 在 192.168.3.143 上实现检查接口
-2. **在 n8n 中配置** - 按照方案 A 添加 Tools
-3. **测试工作流** - 使用上述测试场景验证
-4. **优化体验** - 根据测试结果调整 AI 提示词
-
-**推荐从方案 A 开始实施,因为它提供了最完整的功能和最好的用户体验!** 🚀
-
-
-
-
-
-
-

+ 0 - 343
docs/n8n_workflow_data_governance.md

@@ -1,343 +0,0 @@
-# n8n Data-governance 工作流文档
-
-## 工作流概述
-
-**工作流名称**: Data-governance  
-**工作流ID**: rZK08l4aNUGgwmfO  
-**状态**: 未激活(inactive)  
-**创建时间**: 2025-11-04
-
-## 功能描述
-
-这是一个基于 n8n 的数据治理对话工作流,通过交互式对话实现元数据管理功能:
-
-1. 主动询问用户是否需要进行元数据管理
-2. 根据用户的确认或拒绝响应,执行不同的操作:
-   - **确认**:发起元数据新增工作节点,调用 DataOps API
-   - **拒绝**:返回到对话,提供其他数据治理服务选项
-
-## 工作流结构
-
-### 节点说明
-
-#### 1. When chat message received (Manual Trigger)
-- **类型**: Manual Trigger
-- **位置**: (240, 380)
-- **功能**: 手动触发工作流启动
-- **说明**: 在实际使用中,可以替换为 Webhook 或 Chat Trigger 节点
-
-#### 2. Initial Message (Set Node)
-- **类型**: Set Node
-- **位置**: (460, 380)
-- **功能**: 设置初始欢迎消息
-- **消息内容**:
-```
-您好!我是数据治理助手。
-
-我可以帮助您:
-- 进行元数据管理
-- 数据标准制定
-- 数据质量检查
-
-请问您需要进行元数据管理吗?(请回答:是 或 否)
-```
-
-#### 3. Check User Response (IF Node)
-- **类型**: IF 条件判断节点
-- **位置**: (680, 380)
-- **条件**: 检查 `user_response` 字段是否等于 "是"
-- **输出**:
-  - **True分支**: 用户确认,执行元数据新增
-  - **False分支**: 用户拒绝,返回对话
-
-#### 4. Add Metadata (HTTP Request Node)
-- **类型**: HTTP Request
-- **位置**: (920, 280)
-- **方法**: POST
-- **URL**: `http://localhost:5000/api/meta/add`
-- **请求体**:
-```json
-{
-  "name_zh": "新建元数据",
-  "data_type": "string",
-  "description": "通过工作流创建的元数据",
-  "source": "data-governance-workflow"
-}
-```
-- **说明**: 调用 DataOps 平台的元数据新增接口
-
-#### 5. Confirm Message (Set Node)
-- **类型**: Set Node
-- **位置**: (1140, 280)
-- **功能**: 返回确认消息
-- **消息内容**:
-```
-好的!已为您发起元数据新增工作流程。
-
-操作结果:{{ $json.statusMessage || '处理中...' }}
-
-如需继续其他数据治理操作,请告诉我。
-```
-
-#### 6. Reject Message (Set Node)
-- **类型**: Set Node
-- **位置**: (920, 480)
-- **功能**: 返回拒绝消息
-- **消息内容**:
-```
-好的,已取消元数据管理操作。
-
-还有其他需要帮助的吗?
-- 数据标准制定
-- 数据质量检查
-- 其他数据治理服务
-```
-
-## 工作流程图
-
-```
-┌─────────────────────┐
-│ When chat message   │
-│   received          │
-│  (Manual Trigger)   │
-└──────────┬──────────┘
-           │
-           v
-┌─────────────────────┐
-│  Initial Message    │
-│   (显示欢迎信息)    │
-└──────────┬──────────┘
-           │
-           v
-┌─────────────────────┐
-│ Check User Response │
-│  (判断用户回答)     │
-└──────┬──────────┬───┘
-       │          │
-   是  │          │ 否
-       v          v
-┌─────────────┐  ┌──────────────┐
-│Add Metadata │  │Reject Message│
-│(调用API)    │  │ (返回对话)   │
-└──────┬──────┘  └──────────────┘
-       │
-       v
-┌─────────────┐
-│Confirm Msg  │
-│(确认消息)   │
-└─────────────┘
-```
-
-## 数据流
-
-### 输入数据格式
-
-```json
-{
-  "user_response": "是"  // 或 "否"
-}
-```
-
-### 输出数据格式
-
-**确认路径输出**:
-```json
-{
-  "response": "好的!已为您发起元数据新增工作流程。\n\n操作结果:成功\n\n如需继续其他数据治理操作,请告诉我。"
-}
-```
-
-**拒绝路径输出**:
-```json
-{
-  "response": "好的,已取消元数据管理操作。\n\n还有其他需要帮助的吗?\n- 数据标准制定\n- 数据质量检查\n- 其他数据治理服务"
-}
-```
-
-## 配置说明
-
-### 环境变量
-
-工作流中使用的 API 端点:
-- **DataOps API**: `http://localhost:5000`
-- **元数据新增接口**: `/api/meta/add`
-
-### 时区设置
-
-- **时区**: Asia/Shanghai (中国标准时间)
-
-### 执行数据保存
-
-- **成功执行**: 保存所有数据
-- **失败执行**: 保存所有数据
-- **执行顺序**: v1
-
-## 使用方法
-
-### 1. 激活工作流
-
-在 n8n 界面中找到 "Data-governance" 工作流,点击激活按钮。
-
-### 2. 测试工作流
-
-1. 点击 "Execute Workflow" 按钮
-2. 在弹出的对话框中输入用户响应
-3. 观察工作流执行结果
-
-### 3. 集成到实际应用
-
-#### 方式 1: 使用 Webhook 触发
-
-将 Manual Trigger 节点替换为 Webhook 节点:
-
-```json
-{
-  "type": "n8n-nodes-base.webhook",
-  "parameters": {
-    "httpMethod": "POST",
-    "path": "data-governance-chat",
-    "responseMode": "responseNode"
-  }
-}
-```
-
-#### 方式 2: 使用 Chat Trigger
-
-将 Manual Trigger 节点替换为 Chat Trigger 节点(需要 @n8n/n8n-nodes-langchain 包):
-
-```json
-{
-  "type": "@n8n/n8n-nodes-langchain.chatTrigger",
-  "parameters": {
-    "options": {
-      "public": true,
-      "welcomeMessage": "您好!我是数据治理助手..."
-    }
-  }
-}
-```
-
-## 扩展建议
-
-### 1. 添加 AI 智能理解
-
-可以在 "Check User Response" 节点之前添加 AI 节点,使用 LLM 理解用户意图:
-
-```json
-{
-  "type": "@n8n/n8n-nodes-langchain.agent",
-  "parameters": {
-    "promptType": "define",
-    "text": "判断用户是否确认进行元数据管理,返回 CONFIRM 或 REJECT"
-  }
-}
-```
-
-### 2. 支持更多数据治理功能
-
-在拒绝分支添加 Switch 节点,根据用户选择执行不同的数据治理操作:
-- 数据标准制定
-- 数据质量检查
-- 数据标签管理
-
-### 3. 添加数据验证
-
-在调用 API 之前,添加数据验证节点,确保请求数据的完整性:
-
-```json
-{
-  "type": "n8n-nodes-base.function",
-  "parameters": {
-    "functionCode": "// 验证必填字段\nif (!$json.name_zh) {\n  throw new Error('元数据名称不能为空');\n}\nreturn $json;"
-  }
-}
-```
-
-### 4. 添加错误处理
-
-为 HTTP Request 节点添加错误处理分支:
-
-```json
-{
-  "type": "n8n-nodes-base.if",
-  "parameters": {
-    "conditions": {
-      "conditions": [{
-        "leftValue": "={{ $json.code }}",
-        "rightValue": 200,
-        "operator": {"type": "number", "operation": "equals"}
-      }]
-    }
-  }
-}
-```
-
-## 故障排查
-
-### 常见问题
-
-1. **API 调用失败**
-   - 检查 DataOps 平台是否正常运行
-   - 验证 API 端点 URL 是否正确
-   - 查看 n8n 日志获取详细错误信息
-
-2. **用户响应未正确识别**
-   - 检查 IF 节点的条件配置
-   - 确认 user_response 字段名称正确
-   - 考虑使用不区分大小写的比较
-
-3. **消息格式问题**
-   - 检查 Set 节点中的表达式语法
-   - 确认换行符 `\n` 正确显示
-
-## 相关接口
-
-### DataOps 元数据新增接口
-
-**端点**: `POST /api/meta/add`
-
-**请求参数**:
-```json
-{
-  "name_zh": "元数据中文名称",
-  "data_type": "数据类型",
-  "description": "描述信息",
-  "source": "数据来源"
-}
-```
-
-**响应格式**:
-```json
-{
-  "code": 200,
-  "data": {},
-  "msg": "success"
-}
-```
-
-## 版本历史
-
-- **v1.0** (2025-11-04): 初始版本
-  - 实现基本的对话流程
-  - 支持确认/拒绝两种路径
-  - 集成元数据新增 API
-
-## 后续规划
-
-- [ ] 集成 LLM 提升对话理解能力
-- [ ] 支持更多数据治理操作
-- [ ] 添加用户会话管理
-- [ ] 实现多轮对话能力
-- [ ] 添加数据验证和错误处理
-- [ ] 支持批量操作
-
-## 联系方式
-
-如有问题或建议,请联系 DataOps 平台开发团队。
-
-
-
-
-
-
-

+ 0 - 362
docs/script_template_with_table_check.py

@@ -1,362 +0,0 @@
-#!/usr/bin/env python
-"""
-数据流任务脚本模板(包含目标表检测和自动创建功能)
-
-重要要求:
-1. 所有数据流脚本必须包含目标表检测功能
-2. 如果目标表不存在,脚本必须自动创建目标表
-3. 表创建应基于任务描述中的 DDL 定义
-4. 表创建应在数据加载之前执行
-
-创建时间: 2026-01-16
-"""
-
-from __future__ import annotations
-
-import os
-import sys
-from datetime import datetime
-from typing import Any
-
-import pandas as pd
-import psycopg2
-from loguru import logger
-
-# 添加项目根目录到Python路径
-PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-sys.path.insert(0, PROJECT_ROOT)
-
-from app.config.config import config, current_env
-
-# 获取配置
-app_config = config[current_env]
-
-
-def get_source_connection() -> psycopg2.extensions.connection:
-    """
-    获取源数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    # 根据任务描述配置源数据库连接
-    # TODO: 根据实际任务描述配置
-    conn = psycopg2.connect(
-        host="192.168.3.143",
-        port=5432,
-        database="dataops",
-        user="postgres",
-        password="dataOps",
-    )
-    logger.info("源数据库连接成功")
-    return conn
-
-
-def get_target_connection() -> psycopg2.extensions.connection:
-    """
-    获取目标数据库连接
-
-    Returns:
-        psycopg2 连接对象
-    """
-    # 根据任务描述配置目标数据库连接
-    # TODO: 根据实际任务描述配置
-    conn = psycopg2.connect(
-        host="192.168.3.143",
-        port=5432,
-        database="dataops",
-        user="postgres",
-        password="dataOps",
-        options="-c search_path=dags,public",  # 根据实际 schema 配置
-    )
-    logger.info("目标数据库连接成功")
-    return conn
-
-
-def ensure_target_table_exists(conn: psycopg2.extensions.connection) -> None:
-    """
-    确保目标表存在,如果不存在则创建
-
-    重要:此函数必须根据任务描述中的目标表 DDL 来实现
-
-    Args:
-        conn: 目标数据库连接
-    """
-    cursor = conn.cursor()
-    target_table = "target_table_name"  # TODO: 替换为实际表名
-    target_schema = "public"  # TODO: 替换为实际 schema(如 dags, public 等)
-
-    try:
-        # 检查表是否存在
-        cursor.execute(
-            """
-            SELECT EXISTS(
-                SELECT 1 FROM information_schema.tables
-                WHERE table_schema = %s
-                AND table_name = %s
-            )
-        """,
-            (target_schema, target_table),
-        )
-        result = cursor.fetchone()
-        exists = result[0] if result else False
-
-        if not exists:
-            logger.info(f"目标表不存在,正在创建 {target_schema}.{target_table}...")
-
-            # TODO: 根据任务描述中的 DDL 创建表
-            # 示例:根据任务描述中的 CREATE TABLE 语句创建
-            create_table_sql = f"""
-            CREATE TABLE IF NOT EXISTS {target_schema}.{target_table} (
-                id SERIAL PRIMARY KEY,
-                -- TODO: 根据任务描述中的 DDL 添加所有列
-                create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-            );
-            """
-            cursor.execute(create_table_sql)
-
-            # 添加表注释
-            cursor.execute(
-                f"COMMENT ON TABLE {target_schema}.{target_table} IS '表注释'"
-            )
-
-            # 添加列注释(根据任务描述中的 COMMENT)
-            # TODO: 根据任务描述添加列注释
-
-            conn.commit()
-            logger.info(f"目标表 {target_schema}.{target_table} 创建成功")
-        else:
-            logger.info(f"目标表 {target_schema}.{target_table} 已存在")
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"创建目标表失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def extract_source_data(conn: psycopg2.extensions.connection) -> pd.DataFrame:
-    """
-    从源表提取数据
-
-    Args:
-        conn: 源数据库连接
-
-    Returns:
-        包含源数据的DataFrame
-    """
-    # TODO: 根据任务描述编写提取数据的 SQL
-    query = """
-    SELECT *
-    FROM source_table
-    """
-
-    logger.info("正在从源表提取数据...")
-
-    try:
-        df = pd.read_sql(query, conn)
-        logger.info(f"成功提取 {len(df)} 条记录")
-        return df
-    except Exception as e:
-        logger.error(f"提取源数据失败: {e}")
-        raise
-
-
-def transform_data(df: pd.DataFrame) -> pd.DataFrame:
-    """
-    数据转换处理
-
-    Args:
-        df: 源数据DataFrame
-
-    Returns:
-        转换后的DataFrame
-    """
-    logger.info("正在执行数据转换...")
-
-    # TODO: 根据任务描述中的 rule 实现数据转换逻辑
-
-    logger.info(f"数据转换完成,共 {len(df)} 条记录")
-    return df
-
-
-def load_to_target(
-    df: pd.DataFrame,
-    conn: psycopg2.extensions.connection,
-    update_mode: str = "append",
-    batch_size: int = 1000,
-) -> int:
-    """
-    将数据加载到目标表
-
-    Args:
-        df: 要加载的DataFrame
-        conn: 目标数据库连接
-        update_mode: 更新模式(append 或 full)
-        batch_size: 批量插入大小
-
-    Returns:
-        插入的记录数
-    """
-    if df.empty:
-        logger.warning("没有数据需要加载")
-        return 0
-
-    logger.info(f"正在将 {len(df)} 条记录加载到目标表...")
-
-    target_table = "schema.target_table_name"  # TODO: 替换为实际表名(包含 schema)
-
-    cursor = conn.cursor()
-    inserted_count = 0
-
-    try:
-        # 全量更新模式:先清空目标表
-        if update_mode.lower() == "full":
-            logger.info("全量更新模式:清空目标表...")
-            cursor.execute(f"TRUNCATE TABLE {target_table}")
-            logger.info("目标表已清空")
-
-        # TODO: 根据目标表结构准备插入的列
-        columns = ["col1", "col2", "col3"]  # TODO: 替换为实际列名
-
-        # 构建插入SQL
-        placeholders = ", ".join(["%s"] * len(columns))
-        column_names = ", ".join(columns)
-        insert_sql = (
-            f"INSERT INTO {target_table} ({column_names}) VALUES ({placeholders})"
-        )
-
-        # 批量插入
-        for i in range(0, len(df), batch_size):
-            batch_df = df.iloc[i : i + batch_size]
-            records = []
-            for _, row in batch_df.iterrows():
-                record = tuple(
-                    row[col] if col in row.index else None for col in columns
-                )
-                records.append(record)
-
-            cursor.executemany(insert_sql, records)
-            inserted_count += len(records)
-            logger.debug(f"已插入 {inserted_count}/{len(df)} 条记录")
-
-        conn.commit()
-        logger.info(f"成功加载 {inserted_count} 条记录到 {target_table}")
-        return inserted_count
-
-    except Exception as e:
-        conn.rollback()
-        logger.error(f"数据加载失败: {e}")
-        raise
-    finally:
-        cursor.close()
-
-
-def main() -> dict[str, Any]:
-    """
-    主函数:执行ETL流程
-
-    Returns:
-        执行结果字典
-    """
-    result = {
-        "task_id": 0,  # TODO: 替换为实际任务ID
-        "task_name": "任务名称",  # TODO: 替换为实际任务名称
-        "status": "failed",
-        "records_extracted": 0,
-        "records_loaded": 0,
-        "error_message": None,
-        "execution_time": None,
-    }
-
-    start_time = datetime.now()
-    source_conn = None
-    target_conn = None
-
-    try:
-        logger.info("=" * 60)
-        logger.info("任务开始: 任务名称")  # TODO: 替换为实际任务名称
-        logger.info("=" * 60)
-
-        # 步骤1: 建立数据库连接
-        logger.info("[Step 1/5] 建立数据库连接...")
-        source_conn = get_source_connection()
-        target_conn = get_target_connection()
-
-        # 步骤2: 确保目标表存在(重要:必须在数据加载前执行)
-        logger.info("[Step 2/5] 检查/创建目标表...")
-        ensure_target_table_exists(target_conn)
-
-        # 步骤3: 从源表提取数据
-        logger.info("[Step 3/5] 提取源数据...")
-        df = extract_source_data(source_conn)
-        result["records_extracted"] = len(df)
-
-        # 步骤4: 数据转换
-        logger.info("[Step 4/5] 数据转换...")
-        df_transformed = transform_data(df)
-
-        # 步骤5: 加载到目标表
-        logger.info("[Step 5/5] 加载数据到目标表...")
-        records_loaded = load_to_target(
-            df_transformed, target_conn, update_mode="append"
-        )
-        result["records_loaded"] = records_loaded
-
-        result["status"] = "success"
-        logger.info("=" * 60)
-        logger.info(
-            f"任务完成! 提取: {result['records_extracted']}, 加载: {result['records_loaded']}"
-        )
-        logger.info("=" * 60)
-
-    except Exception as e:
-        result["status"] = "failed"
-        result["error_message"] = str(e)
-        logger.error(f"任务执行失败: {e}")
-        raise
-
-    finally:
-        # 关闭数据库连接
-        if source_conn:
-            source_conn.close()
-            logger.debug("源数据库连接已关闭")
-        if target_conn:
-            target_conn.close()
-            logger.debug("目标数据库连接已关闭")
-
-        result["execution_time"] = str(datetime.now() - start_time)
-
-    return result
-
-
-if __name__ == "__main__":
-    # 配置日志
-    # 重要:日志输出到 stdout 而非 stderr,以便 n8n 工作流正确解析输出
-    # n8n SSH 节点的成功响应使用 $json.stdout,失败响应使用 $json.stderr
-    logger.remove()
-    logger.add(
-        sys.stdout,
-        level="INFO",
-        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
-    )
-    logger.add(
-        os.path.join(
-            PROJECT_ROOT, "logs", "task_xxx.log"
-        ),  # TODO: 替换为实际日志文件名
-        level="DEBUG",
-        rotation="10 MB",
-        retention="7 days",
-        encoding="utf-8",
-    )
-
-    try:
-        result = main()
-        if result["status"] == "success":
-            sys.exit(0)
-        else:
-            sys.exit(1)
-    except Exception as e:
-        logger.exception(f"脚本执行异常: {e}")
-        sys.exit(1)

+ 0 - 201
docs/timezone_fix_summary.md

@@ -1,201 +0,0 @@
-# 时区修正总结
-
-## 修正目标
-
-将 `data_orders` 表的三个 timestamp 字段(`created_at`、`updated_at`、`processed_at`)以及其他相关表的时间字段,从使用 UTC 时间改为使用东八区(Asia/Shanghai)时间。
-
-## 修改内容
-
-### 1. 创建时区工具模块
-
-**文件**: `app/core/common/timezone_utils.py`
-
-创建了统一的时区处理工具模块,提供以下功能:
-
-- `now_china()`: 获取当前东八区时间(带时区信息)
-- `now_china_naive()`: 获取当前东八区时间(不带时区信息,用于数据库存储)
-- `to_china_time()`: 将任意时区的时间转换为东八区时间
-- `utc_to_china_naive()`: 将 UTC 时间转换为东八区时间(不带时区信息)
-
-### 2. 修正数据模型
-
-#### 2.1 `app/models/data_product.py`
-
-**DataProduct 模型**:
-- `created_at`: `datetime.utcnow` → `now_china_naive`
-- `updated_at`: `datetime.utcnow` → `now_china_naive`
-- `mark_as_viewed()`: 使用 `now_china_naive()`
-- `update_data_stats()`: 使用 `now_china_naive()`
-
-**DataOrder 模型**:
-- `created_at`: `datetime.utcnow` → `now_china_naive`
-- `updated_at`: `datetime.utcnow` → `now_china_naive`
-- `processed_at`: 默认值保持 None,但在更新时使用 `now_china_naive()`
-- `update_status()`: 使用 `now_china_naive()`
-- `set_extraction_result()`: 使用 `now_china_naive()`
-- `set_graph_analysis()`: 使用 `now_china_naive()`
-- `set_result()`: 使用 `now_china_naive()`
-- `reject()`: 使用 `now_china_naive()`
-
-#### 2.2 `app/models/metadata_review.py`
-
-**MetadataReviewRecord 模型**:
-- `created_at`: `datetime.utcnow` → `now_china_naive`
-- `updated_at`: `datetime.utcnow` → `now_china_naive`
-
-**MetadataVersionHistory 模型**:
-- `created_at`: `datetime.utcnow` → `now_china_naive`
-
-**update_review_record_resolution 函数**:
-- `resolved_at`: 使用 `now_china_naive()`
-- `updated_at`: 使用 `now_china_naive()`
-
-### 3. 修正服务层
-
-#### 3.1 `app/core/data_service/data_product_service.py`
-
-修正了所有使用 `datetime.utcnow()` 的地方:
-- `register_product()`: 更新现有产品和创建新产品时使用 `now_china_naive()`
-- `update_product_stats()`: 使用 `now_china_naive()`
-- `refresh_product_stats()`: 使用 `now_china_naive()`
-- `update_order()`: 使用 `now_china_naive()`
-
-#### 3.2 `app/core/meta_data/redundancy_check.py`
-
-修正了创建审核记录时的时间字段:
-- `created_at`: 使用 `now_china_naive()`
-- `updated_at`: 使用 `now_china_naive()`
-
-#### 3.3 `app/core/business_domain/business_domain.py`
-
-修正了创建审核记录时的时间字段:
-- `created_at`: 使用 `now_china_naive()`
-- `updated_at`: 使用 `now_china_naive()`
-
-## 技术实现
-
-### 使用 zoneinfo 而非 pytz
-
-本次修正使用了 `zoneinfo` 模块,而不是第三方库 `pytz`。理由如下:
-
-1. **标准库支持**: Python 3.9+ 内置 `zoneinfo`,Python 3.8 使用 `backports.zoneinfo`
-2. **更简洁的 API**: 相比 `pytz`,`zoneinfo` 的 API 更符合 Python 的 datetime 使用习惯
-3. **自动更新**: 使用系统的 IANA 时区数据库,自动获取最新的时区规则
-
-### Python 版本兼容性
-
-代码已兼容 Python 3.8 和 3.9+:
-
-```python
-try:
-    # Python 3.9+
-    from zoneinfo import ZoneInfo
-except ImportError:
-    # Python 3.8 使用 backports
-    from backports.zoneinfo import ZoneInfo
-```
-
-**依赖要求**:
-- Python 3.8: 需要安装 `backports.zoneinfo` 包
-- Python 3.9+: 使用标准库,无需额外安装
-- 所有版本: 需要系统安装 `tzdata` 包
-
-### 数据库存储策略
-
-采用 **naive datetime** 存储策略:
-- 数据库中存储不带时区信息的 datetime(naive datetime)
-- 所有时间统一使用东八区时间
-- 在应用层确保时间的一致性
-
-这种策略的优点:
-1. 数据库字段类型简单(TIMESTAMP 而非 TIMESTAMP WITH TIME ZONE)
-2. 避免时区转换的复杂性
-3. 符合项目现有的数据库设计
-
-## 影响范围
-
-### 直接影响的表
-
-1. **data_orders**: `created_at`, `updated_at`, `processed_at`
-2. **data_products**: `created_at`, `updated_at`, `last_updated_at`, `last_viewed_at`
-3. **metadata_review_records**: `created_at`, `updated_at`, `resolved_at`
-4. **metadata_version_history**: `created_at`
-
-### 间接影响
-
-所有依赖这些时间字段的业务逻辑都将使用东八区时间,包括:
-- 数据订单的创建、更新、处理流程
-- 数据产品的注册、更新、查看记录
-- 元数据审核记录的创建和解决
-
-## 向后兼容性
-
-### 已有数据
-
-数据库中已存在的记录可能使用的是 UTC 时间。建议:
-
-1. **新旧数据混合期**: 在一段时间内,数据库中会同时存在 UTC 时间和东八区时间的记录
-2. **数据迁移**(可选): 如果需要统一历史数据,可以执行以下 SQL:
-
-```sql
--- 将 UTC 时间转换为东八区时间(+8小时)
-UPDATE data_orders 
-SET created_at = created_at + INTERVAL '8 hours',
-    updated_at = updated_at + INTERVAL '8 hours',
-    processed_at = processed_at + INTERVAL '8 hours'
-WHERE created_at < '2026-01-12 00:00:00';  -- 修正前的数据
-
-UPDATE data_products
-SET created_at = created_at + INTERVAL '8 hours',
-    updated_at = updated_at + INTERVAL '8 hours',
-    last_updated_at = last_updated_at + INTERVAL '8 hours',
-    last_viewed_at = last_viewed_at + INTERVAL '8 hours'
-WHERE created_at < '2026-01-12 00:00:00';
-
-UPDATE metadata_review_records
-SET created_at = created_at + INTERVAL '8 hours',
-    updated_at = updated_at + INTERVAL '8 hours',
-    resolved_at = resolved_at + INTERVAL '8 hours'
-WHERE created_at < '2026-01-12 00:00:00';
-
-UPDATE metadata_version_history
-SET created_at = created_at + INTERVAL '8 hours'
-WHERE created_at < '2026-01-12 00:00:00';
-```
-
-### API 响应
-
-所有 API 返回的时间字段(ISO 8601 格式)现在表示的是东八区时间,而不是 UTC 时间。
-
-## 测试建议
-
-1. **创建新订单**: 验证 `created_at` 字段是否为东八区当前时间
-2. **更新订单状态**: 验证 `updated_at` 和 `processed_at` 是否正确
-3. **注册数据产品**: 验证时间字段是否正确
-4. **查看数据产品**: 验证 `last_viewed_at` 是否更新为东八区时间
-5. **元数据审核**: 验证审核记录的时间字段
-
-## 符合业务规则
-
-本次修正符合 `BUSINESS_RULES.md` 中的规定:
-
-```python
-# Use East Asia timezone for all timestamps
-from datetime import datetime
-import pytz
-```
-
-虽然业务规则中提到了 `pytz`,但我们使用了更现代的 `zoneinfo` 标准库,功能等价且更优。
-
-## 总结
-
-✅ 所有相关的时间字段已统一使用东八区时间  
-✅ 创建了统一的时区工具模块,便于后续维护  
-✅ 修正了 6 个文件,涉及模型层和服务层  
-✅ 代码通过了 linter 检查,无语法错误  
-✅ 符合项目的业务规则要求  
-
----
-
-**修正日期**: 2026-01-12  
-**修正人**: AI Assistant

BIN
release/20251118.rar


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels