Parcourir la source

data_resource修复bug
data_model修复bug

maxiaolong il y a 3 jours
Parent
commit
b950f19b1a

+ 154 - 20
app/api/data_model/routes.py

@@ -58,16 +58,52 @@ def data_model_save():
     receiver = request.get_json()
     data_model = receiver['name_zh']
     id_list = receiver['id_list']
+    data_source = receiver.get('data_source')  # 获取data_source节点ID
+    
     # resource_id和meta_id构成json格式
     result = json.dumps(id_list, ensure_ascii=False)
     try:
-        # 从DDL中选取保存数据模型
+        # 从DDL中选取保存数据模型(支持data_source参数)
         result_list = [receiver['name_en']]
         id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
         model_functions.handle_no_meta_data_model(id_list, receiver, data_model_node)
         model_functions.calculate_model_level(id)
 
-        res = success({}, "success")
+        # 查询节点的实际属性(data_model_node 可能只是整数ID)
+        from app.services.neo4j_driver import neo4j_driver
+        with neo4j_driver.get_session() as session:
+            node_query = """
+            MATCH (n:DataModel) WHERE id(n) = $node_id
+            RETURN n.name_zh as name_zh, n.name_en as name_en, n.description as description,
+                   n.category as category, n.create_time as create_time, n.level as level,
+                   n.tag as tag, n.leader as leader, n.origin as origin, n.frequency as frequency,
+                   n.organization as organization, n.data_sensitivity as data_sensitivity, n.status as status
+            """
+            node_result = session.run(node_query, node_id=int(id))
+            node_record = node_result.single()
+        
+        # 构建响应数据 - data_model包装格式
+        response_data = {
+            "data_model": {
+                "id": id,
+                "name_zh": node_record['name_zh'] if node_record else None,
+                "name_en": node_record['name_en'] if node_record else None,
+                "description": node_record['description'] if node_record else None,
+                "category": node_record['category'] if node_record else None,
+                "create_time": node_record['create_time'] if node_record else None,
+                "level": node_record['level'] if node_record else None,
+                "tag": node_record['tag'] if node_record else None,
+                "leader": node_record['leader'] if node_record else None,
+                "origin": node_record['origin'] if node_record else None,
+                "frequency": node_record['frequency'] if node_record else None,
+                "organization": node_record['organization'] if node_record else None,
+                "data_sensitivity": node_record['data_sensitivity'] if node_record else None,
+                "status": node_record['status'] if node_record else None,
+                "data_source": data_source  # 数据源节点ID
+            }
+        }
+
+        res = success(response_data, "success")
         return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
 
     except Exception as e:
@@ -83,9 +119,12 @@ def data_model_search():
     receiver = request.get_json()
     data_model = receiver['name_zh']
     id_list = receiver['id_list']
+    data_source = receiver.get('data_source')  # 获取data_source节点ID
+    
     # resource_id和meta_id构成json格式
     result = json.dumps(id_list, ensure_ascii=False)
     try:
+        # 从数据资源中选取保存数据模型(支持data_source参数)
         from app.core.meta_data import translate_and_parse
         result_list = translate_and_parse(data_model)
         id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
@@ -93,7 +132,41 @@ def data_model_search():
             model_functions.resource_handle_meta_data_model(id_list, id)
         model_functions.calculate_model_level(id)
 
-        res = success({}, "success")
+        # 查询节点的实际属性(data_model_node 可能只是整数ID)
+        from app.services.neo4j_driver import neo4j_driver
+        with neo4j_driver.get_session() as session:
+            node_query = """
+            MATCH (n:DataModel) WHERE id(n) = $node_id
+            RETURN n.name_zh as name_zh, n.name_en as name_en, n.description as description,
+                   n.category as category, n.create_time as create_time, n.level as level,
+                   n.tag as tag, n.leader as leader, n.origin as origin, n.frequency as frequency,
+                   n.organization as organization, n.data_sensitivity as data_sensitivity, n.status as status
+            """
+            node_result = session.run(node_query, node_id=int(id))
+            node_record = node_result.single()
+        
+        # 构建响应数据 - data_model包装格式
+        response_data = {
+            "data_model": {
+                "id": id,
+                "name_zh": node_record['name_zh'] if node_record else None,
+                "name_en": node_record['name_en'] if node_record else None,
+                "description": node_record['description'] if node_record else None,
+                "category": node_record['category'] if node_record else None,
+                "create_time": node_record['create_time'] if node_record else None,
+                "level": node_record['level'] if node_record else None,
+                "tag": node_record['tag'] if node_record else None,
+                "leader": node_record['leader'] if node_record else None,
+                "origin": node_record['origin'] if node_record else None,
+                "frequency": node_record['frequency'] if node_record else None,
+                "organization": node_record['organization'] if node_record else None,
+                "data_sensitivity": node_record['data_sensitivity'] if node_record else None,
+                "status": node_record['status'] if node_record else None,
+                "data_source": data_source  # 数据源节点ID
+            }
+        }
+
+        res = success(response_data, "success")
         return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
 
     except Exception as e:
@@ -108,32 +181,51 @@ def data_model_model_add():
     receiver = request.get_json()
     data_model = receiver['name_zh']
     id_list = receiver['id_list']
+    data_source = receiver.get('data_source')  # 获取data_source节点ID
+    
     # model_id和meta_id构成json格式
     result = json.dumps(id_list, ensure_ascii=False)
     try:
+        # 从数据模型中选取保存数据模型
+        # handle_data_model 已经处理了 data_source 关系创建(支持 int/dict/string 格式)
         from app.core.meta_data import translate_and_parse
         result_list = translate_and_parse(data_model)
         node_id, data_model_node = model_functions.handle_data_model(data_model, result_list, result, receiver)
         model_functions.model_handle_meta_data_model(id_list, node_id)
         model_functions.calculate_model_level(node_id)
 
-        # 构建响应数据
+        # 查询节点的实际属性(data_model_node 可能只是整数ID)
+        from app.services.neo4j_driver import neo4j_driver
+        with neo4j_driver.get_session() as session:
+            node_query = """
+            MATCH (n:DataModel) WHERE id(n) = $node_id
+            RETURN n.name_zh as name_zh, n.name_en as name_en, n.description as description,
+                   n.category as category, n.create_time as create_time, n.level as level,
+                   n.tag as tag, n.leader as leader, n.origin as origin, n.frequency as frequency,
+                   n.organization as organization, n.data_sensitivity as data_sensitivity, n.status as status
+            """
+            node_result = session.run(node_query, node_id=int(node_id))
+            node_record = node_result.single()
+        
+        # 构建响应数据 - data_model包装格式
         response_data = {
-            "id": node_id,
-            "name_zh": data_model_node.get('name_zh'),
-            "name_en": data_model_node.get('name_en'),
-            "description": data_model_node.get('description'),
-            "category": data_model_node.get('category'),
-            "time": data_model_node.get('time'),
-            "level": data_model_node.get('level'),
-            "tag": data_model_node.get('tag'),
-            "childrenId": data_model_node.get('childrenId', []),
-            "leader": data_model_node.get('leader'),
-            "origin": data_model_node.get('origin'),
-            "frequency": data_model_node.get('frequency'),
-            "organization": data_model_node.get('organization'),
-            "data_sensitivity": data_model_node.get('data_sensitivity'),
-            "status": data_model_node.get('status')
+            "data_model": {
+                "id": node_id,
+                "name_zh": node_record['name_zh'] if node_record else None,
+                "name_en": node_record['name_en'] if node_record else None,
+                "description": node_record['description'] if node_record else None,
+                "category": node_record['category'] if node_record else None,
+                "create_time": node_record['create_time'] if node_record else None,
+                "level": node_record['level'] if node_record else None,
+                "tag": node_record['tag'] if node_record else None,
+                "leader": node_record['leader'] if node_record else None,
+                "origin": node_record['origin'] if node_record else None,
+                "frequency": node_record['frequency'] if node_record else None,
+                "organization": node_record['organization'] if node_record else None,
+                "data_sensitivity": node_record['data_sensitivity'] if node_record else None,
+                "status": node_record['status'] if node_record else None,
+                "data_source": data_source  # 数据源节点ID,与name_zh在同一级别
+            }
         }
 
         res = success(response_data, "success")
@@ -154,7 +246,49 @@ def data_model_detail():
         id = receiver.get('id')
         print(f"Received id from frontend: {id}")
 
-        response_data = model_functions.handle_id_model(id)
+        result_data = model_functions.handle_id_model(id)
+        
+        # handle_id_model 返回的数据格式是 {"data_model": {...}}
+        # 提取内部的 data_model 数据
+        model_data = result_data.get("data_model", {})
+        
+        # 查询关联的数据源信息
+        from app.services.neo4j_driver import neo4j_driver
+        data_source_id = None
+        try:
+            model_id = int(id)
+            with neo4j_driver.get_session() as session:
+                # 查询数据模型关联的数据源节点
+                ds_cypher = """
+                MATCH (m:DataModel)-[:COME_FROM]->(ds:DataSource)
+                WHERE id(m) = $model_id
+                RETURN id(ds) as ds_id
+                """
+                ds_result = session.run(ds_cypher, model_id=model_id)
+                ds_record = ds_result.single()
+                
+                if ds_record:
+                    # 如果存在数据源关联,只返回ID
+                    data_source_id = ds_record['ds_id']
+                    logger.info(f"找到数据模型关联的数据源: model_id={model_id}, data_source_id={data_source_id}")
+                else:
+                    logger.info(f"数据模型未关联数据源: model_id={model_id}")
+        except Exception as e:
+            # 数据源查询失败不应该影响主流程
+            logger.error(f"查询数据源关联失败(不中断主流程): {str(e)}")
+        
+        # 删除 childrenId 字段(如果存在)
+        if 'childrenId' in model_data:
+            del model_data['childrenId']
+        
+        # 添加 data_source 字段
+        model_data['data_source'] = data_source_id
+        
+        # 构建响应数据 - data_model包装格式
+        response_data = {
+            "data_model": model_data
+        }
+        
         res = success(response_data, "success")
         return json.dumps(res, ensure_ascii=False, cls=MyEncoder)
 

+ 25 - 35
app/api/data_resource/routes.py

@@ -161,10 +161,10 @@ def data_resource_save():
     try:
         # 获取表单数据
         receiver = request.get_json()
-        # 检查receiver是否存在
         if not receiver:
             return jsonify(failed("参数不完整:缺少receiver"))
-        # 检查url是否存在
+        
+        # 检查url(允许为空)
         if 'url' not in receiver or not receiver['url']:
             logger.debug(f"url 为空")
 
@@ -172,41 +172,30 @@ def data_resource_save():
         if not additional_info:
             return jsonify(failed("参数不完整: 缺少additional_info"))
                       
-        head_data = additional_info.get('head_data') 
-
-        file_extension = receiver['url'].split('.')[-1] if receiver.get('url') else ""
+        head_data = additional_info.get('head_data')
         
+        # 获取 storage_location 和 data_source
+        storage_location = receiver.get('storage_location', '').strip()
         
-        if file_extension in ['xlsx', 'xls', 'csv']:
-            # Excel/CSV文件必须有storage_location
-            storage_location = receiver.get('storage_location', '')
-            if not storage_location:
-                return jsonify(failed("参数不完整:缺少storage_location或storage_location为空"))
-            
-            # 调用业务逻辑处理数据资源创建,设置resource_type为structure
-            resource_id = handle_node(receiver, head_data, data_source=None, resource_type='structure')
-            
-        elif file_extension == 'sql' or file_extension == "" or not file_extension:
+        # 向后兼容:data_source 可能在 receiver 顶层(新客户端)或 additional_info 内(旧客户端)
+        # 使用显式 None 检查以支持 0 作为有效的节点ID
+        data_source = receiver.get('data_source')
+        if data_source is None:
             data_source = additional_info.get('data_source', '')
-            storage_location = receiver.get('storage_location', '')
-
-            # 如果有storage_location,按结构化数据处理
-            if storage_location:
-                resource_id = handle_node(receiver, head_data, data_source=None, resource_type='structure')
-            
-            # 如果有data_source,按DDL处理
-            elif data_source:
-                # 检查data_source格式
-                if not isinstance(data_source, dict) or not data_source.get("name_en"):
-                    return jsonify(failed("数据源信息不完整或无效"))
-                resource_id = handle_node(receiver, head_data, data_source, resource_type='ddl')
-            
-            # 两者都没有
-            else:
-                return jsonify(failed("SQL文件处理需要提供storage_location或有效的data_source信息"))
-                
-        else:
-            return jsonify(failed("文件格式错误"))
+        
+        # 验证:至少需要 storage_location 或 data_source 之一
+        # 使用显式检查以支持 data_source=0(有效的节点ID)
+        if not storage_location and data_source in (None, ''):
+            return jsonify(failed("参数不完整:至少需要提供 storage_location 或 data_source"))
+        
+        # 获取资源类型(直接从前端上传的type字段获取)
+        resource_type = receiver.get('type')
+        if not resource_type:
+            return jsonify(failed("参数不完整:缺少type字段"))
+        
+        # 调用业务逻辑创建数据资源
+        # 只在 data_source 为 None 或空字符串时传 None,保留 0 作为有效值
+        resource_id = handle_node(receiver, head_data, data_source=data_source if data_source not in (None, '') else None, resource_type=resource_type)
     
         return jsonify(success({"id": resource_id}))
     except Exception as e:
@@ -791,7 +780,8 @@ def data_resource_detail():
             "status": resource_data.get("status", True),
             "id": resource_data.get("id"),
             "keywords": resource_data.get("keywords", []),
-            "describe": resource_data.get("describe", "")
+            "describe": resource_data.get("describe", ""),
+            "data_source": resource_data.get("data_source")  # 新增:数据源节点ID
         }
         
         # 记录最终返回的数据

+ 1 - 1
app/config/config.py

@@ -112,7 +112,7 @@ class ProductionConfig(BaseConfig):
     PORT = 80
     
     # 生产环境 MinIO 配置
-    MINIO_HOST = 'company.citupro.com:9000'
+    MINIO_HOST = '192.168.3.143:9000'
     MINIO_USER = 'citu-dataops-acc-key'
     MINIO_PASSWORD = 'citu-dataops-secret-key'
     MINIO_SECURE = False

+ 98 - 0
app/core/data_model/model.py

@@ -201,6 +201,104 @@ def handle_data_model(data_model, result_list, result, receiver):
                             )
                         )
 
+        # 处理数据源关系 - 创建COME_FROM关系
+        data_source = receiver.get('data_source')
+        if data_source:
+            try:
+                # 获取数据源的标识(支持多种格式)
+                data_source_id = None
+                data_source_name_en = None
+                
+                # 1. 如果是数字(节点ID)
+                if isinstance(data_source, (int, float)) or (isinstance(data_source, str) and data_source.isdigit()):
+                    data_source_id = int(data_source)
+                    logger.info(f"data_source 为节点ID: {data_source_id}")
+                # 2. 如果是字典且包含name_en
+                elif isinstance(data_source, dict) and data_source.get('name_en'):
+                    data_source_name_en = data_source['name_en']
+                    logger.info(f"data_source 为字典,提取name_en: {data_source_name_en}")
+                # 3. 如果是字符串(name_en)
+                elif isinstance(data_source, str):
+                    data_source_name_en = data_source
+                    logger.info(f"data_source 为字符串name_en: {data_source_name_en}")
+                
+                # 创建数据模型与数据源的关系
+                with connect_graph().session() as session:
+                    if data_source_id is not None:
+                        # 使用节点ID创建关系
+                        # 首先检查数据源节点是否存在
+                        check_ds_cypher = "MATCH (b:DataSource) WHERE id(b) = $ds_id RETURN b"
+                        check_ds_result = session.run(check_ds_cypher, ds_id=data_source_id)
+                        
+                        if not check_ds_result.single():
+                            logger.warning(f"数据源节点不存在: ID={data_source_id},跳过关系创建")
+                        else:
+                            # 检查关系是否已存在
+                            rel_check_query = """
+                            MATCH (a:DataModel)-[r:COME_FROM]->(b:DataSource)
+                            WHERE id(a) = $model_id AND id(b) = $ds_id
+                            RETURN count(r) > 0 as exists
+                            """
+                            rel_check_result = session.run(rel_check_query,
+                                                          model_id=int(node_id),
+                                                          ds_id=data_source_id).single()
+                            
+                            # 如果关系不存在,则创建COME_FROM关系
+                            if not (rel_check_result and rel_check_result["exists"]):
+                                create_rel_cypher = """
+                                MATCH (a:DataModel), (b:DataSource)
+                                WHERE id(a) = $model_id AND id(b) = $ds_id
+                                CREATE (a)-[r:COME_FROM]->(b)
+                                RETURN r
+                                """
+                                session.run(create_rel_cypher,
+                                          model_id=int(node_id),
+                                          ds_id=data_source_id)
+                                logger.info(f"已创建数据模型与数据源的COME_FROM关系: model_id={node_id} -> data_source_id={data_source_id}")
+                            else:
+                                logger.info(f"数据模型与数据源的COME_FROM关系已存在: model_id={node_id} -> data_source_id={data_source_id}")
+                                
+                    elif data_source_name_en:
+                        # 使用name_en创建关系(兼容旧方式)
+                        # 首先检查数据源节点是否存在
+                        check_ds_cypher = "MATCH (b:DataSource) WHERE b.name_en = $ds_name_en RETURN b"
+                        check_ds_result = session.run(check_ds_cypher, ds_name_en=data_source_name_en)
+                        
+                        if not check_ds_result.single():
+                            logger.warning(f"数据源节点不存在: name_en={data_source_name_en},跳过关系创建")
+                        else:
+                            # 检查关系是否已存在
+                            rel_check_query = """
+                            MATCH (a:DataModel)-[r:COME_FROM]->(b:DataSource)
+                            WHERE id(a) = $model_id AND b.name_en = $ds_name_en
+                            RETURN count(r) > 0 as exists
+                            """
+                            rel_check_result = session.run(rel_check_query,
+                                                          model_id=int(node_id),
+                                                          ds_name_en=data_source_name_en).single()
+                            
+                            # 如果关系不存在,则创建COME_FROM关系
+                            if not (rel_check_result and rel_check_result["exists"]):
+                                create_rel_cypher = """
+                                MATCH (a:DataModel), (b:DataSource)
+                                WHERE id(a) = $model_id AND b.name_en = $ds_name_en
+                                CREATE (a)-[r:COME_FROM]->(b)
+                                RETURN r
+                                """
+                                session.run(create_rel_cypher,
+                                          model_id=int(node_id),
+                                          ds_name_en=data_source_name_en)
+                                logger.info(f"已创建数据模型与数据源的COME_FROM关系: model_id={node_id} -> name_en={data_source_name_en}")
+                            else:
+                                logger.info(f"数据模型与数据源的COME_FROM关系已存在: model_id={node_id} -> name_en={data_source_name_en}")
+                    else:
+                        logger.warning(f"data_source参数无效,无法识别格式: {data_source}")
+                    
+            except Exception as e:
+                # 数据源关系创建失败不应该中断主流程
+                logger.error(f"处理数据源关系失败(不中断主流程): {str(e)}")
+                # 不再抛出异常,允许主流程继续
+
         return node_id, data_model_node
     except Exception as e:
         logging.error(f"Error in handle_data_model: {str(e)}")

+ 96 - 45
app/core/data_resource/resource.py

@@ -134,17 +134,15 @@ def update_or_create_node(label, **properties):
 def handle_node(receiver, head_data, data_source=None, resource_type=None):
     """处理数据资源节点创建和关系建立"""
     try:
-        # 根据resource_type设置type属性的值
-        if resource_type == 'ddl':
-            type_value = 'ddl'
-        else:
-            type_value = 'structure'
+        # 验证必要参数
+        if not resource_type:
+            raise ValueError("resource_type参数不能为空")
             
         # 更新属性
         update_attributes = {
             'name_en': receiver.get('name_en', receiver.get('name_zh', '')),
             'create_time': get_formatted_time(),
-            'type': type_value  # 根据资源类型设置不同的type
+            'type': resource_type  # 直接使用传入的资源类型
         }
         
         # 记录describe字段是否存在于创建数据中
@@ -268,53 +266,89 @@ def handle_node(receiver, head_data, data_source=None, resource_type=None):
                     else:
                         logger.error(f"未能创建或获取元数据节点: {item['name_zh']}")
             
-            # 处理数据源关系
-            if data_source and resource_type == 'ddl':
+            # 处理数据源关系 - 支持所有资源类型
+            if data_source:
                 try:
-                    # 创建或获取数据源节点
-                #    data_source_name_en = handle_data_source(data_source)
-                    data_source_name_en = data_source['name_en']
+                    # 获取数据源节点的标识(支持多种格式)
+                    data_source_id = None
+                    data_source_name_en = None
+                    
+                    # 1. 如果是数字(节点ID)
+                    if isinstance(data_source, (int, float)) or (isinstance(data_source, str) and data_source.isdigit()):
+                        data_source_id = int(data_source)
+                        logger.info(f"data_source 为节点ID: {data_source_id}")
+                    # 2. 如果是字典且包含name_en
+                    elif isinstance(data_source, dict) and data_source.get('name_en'):
+                        data_source_name_en = data_source['name_en']
+                        logger.info(f"data_source 为字典,提取name_en: {data_source_name_en}")
+                    # 3. 如果是字符串(name_en)
+                    elif isinstance(data_source, str):
+                        data_source_name_en = data_source
+                        logger.info(f"data_source 为字符串name_en: {data_source_name_en}")
                     
                     # 创建数据资源与数据源的关系
-                    if data_source_name_en:
-                        # 创建 originates_from 关系
-                        rel_data_source_cypher = """
-                        MATCH (a:DataResource), (b:DataSource)
-                        WHERE id(a) = $resource_id AND b.name_en = $ds_name_en
-                        MERGE (a)-[r:originates_from]->(b)
-                        RETURN r
-                        """
-                        rel_result = session.run(
-                            rel_data_source_cypher,
-                            resource_id=resource_id,
-                            ds_name_en=data_source_name_en
-                        )
-                        rel_record = rel_result.single()
+                    if data_source_id is not None:
+                        # 使用节点ID创建关系
+                        # 首先检查数据源节点是否存在
+                        check_ds_cypher = "MATCH (b:DataSource) WHERE id(b) = $ds_id RETURN b"
+                        check_ds_result = session.run(check_ds_cypher, ds_id=data_source_id)
                         
-                        if rel_record:
-                            logger.info(f"已创建数据资源与数据源的关系: {resource_id} -> {data_source_name_en}")
+                        if not check_ds_result.single():
+                            logger.warning(f"数据源节点不存在: ID={data_source_id},跳过关系创建")
                         else:
-                            # 添加严重错误日志
-                            error_msg = f"创建数据资源与数据源的关系失败: {resource_id} -> {data_source_name_en}"
-                            logger.error(error_msg)
-                            
-                            # 检查数据源节点是否存在
-                            check_ds_cypher = "MATCH (b:DataSource) WHERE b.name_en = $ds_name_en RETURN b"
-                            check_ds_result = session.run(check_ds_cypher, ds_name_en=data_source_name_en)
-                            if not check_ds_result.single():
-                                logger.error(f"数据源节点不存在: name_en={data_source_name_en}")
+                            # 创建 COME_FROM 关系
+                            rel_data_source_cypher = """
+                            MATCH (a:DataResource), (b:DataSource)
+                            WHERE id(a) = $resource_id AND id(b) = $ds_id
+                            MERGE (a)-[r:COME_FROM]->(b)
+                            RETURN r
+                            """
+                            rel_result = session.run(
+                                rel_data_source_cypher,
+                                resource_id=resource_id,
+                                ds_id=data_source_id
+                            )
+                            rel_record = rel_result.single()
                             
-                            # 检查数据资源节点是否存在
-                            check_res_cypher = "MATCH (a:DataResource) WHERE id(a) = $resource_id RETURN a"
-                            check_res_result = session.run(check_res_cypher, resource_id=resource_id)
-                            if not check_res_result.single():
-                                logger.error(f"数据资源节点不存在: id={resource_id}")
+                            if rel_record:
+                                logger.info(f"已创建数据资源与数据源的COME_FROM关系: resource_id={resource_id} -> data_source_id={data_source_id}")
+                            else:
+                                logger.warning(f"创建COME_FROM关系失败,但不中断主流程: {resource_id} -> {data_source_id}")
                                 
-                            # 严重错误应该抛出异常
-                            raise RuntimeError(error_msg)
+                    elif data_source_name_en:
+                        # 使用name_en创建关系(兼容旧方式)
+                        # 首先检查数据源节点是否存在
+                        check_ds_cypher = "MATCH (b:DataSource) WHERE b.name_en = $ds_name_en RETURN b"
+                        check_ds_result = session.run(check_ds_cypher, ds_name_en=data_source_name_en)
+                        
+                        if not check_ds_result.single():
+                            logger.warning(f"数据源节点不存在: name_en={data_source_name_en},跳过关系创建")
+                        else:
+                            # 创建 COME_FROM 关系
+                            rel_data_source_cypher = """
+                            MATCH (a:DataResource), (b:DataSource)
+                            WHERE id(a) = $resource_id AND b.name_en = $ds_name_en
+                            MERGE (a)-[r:COME_FROM]->(b)
+                            RETURN r
+                            """
+                            rel_result = session.run(
+                                rel_data_source_cypher,
+                                resource_id=resource_id,
+                                ds_name_en=data_source_name_en
+                            )
+                            rel_record = rel_result.single()
+                            
+                            if rel_record:
+                                logger.info(f"已创建数据资源与数据源的COME_FROM关系: resource_id={resource_id} -> name_en={data_source_name_en}")
+                            else:
+                                logger.warning(f"创建COME_FROM关系失败,但不中断主流程: {resource_id} -> {data_source_name_en}")
+                    else:
+                        logger.warning(f"data_source参数无效,无法识别格式: {data_source}")
+                        
                 except Exception as e:
-                    logger.error(f"处理数据源关系失败: {str(e)}")
-                    raise RuntimeError(f"处理数据源关系失败: {str(e)}")
+                    # 数据源关系创建失败不应该中断主流程
+                    logger.error(f"处理数据源关系失败(不中断主流程): {str(e)}")
+                    # 不再抛出异常,允许主流程继续
             
             return resource_id
     except Exception as e:
@@ -384,6 +418,23 @@ def handle_id_resource(resource_id):
                 }
             data_resource["tag"] = tag
             
+            # 查询关联的数据源(COME_FROM关系)
+            data_source_cypher = """
+            MATCH (n:DataResource)-[r:COME_FROM]->(ds:DataSource)
+            WHERE id(n) = $resource_id
+            RETURN ds
+            """
+            data_source_result = session.run(data_source_cypher, resource_id=resource_id_int)
+            data_source_record = data_source_result.single()
+            
+            # 设置数据源信息
+            if data_source_record:
+                data_resource["data_source"] = data_source_record["ds"].id
+                logger.info(f"找到关联的数据源,ID: {data_source_record['ds'].id}")
+            else:
+                data_resource["data_source"] = None
+                logger.info(f"未找到关联的数据源")
+            
             # 查询关联的元数据 - 支持meta_data和Metadata两种标签
             meta_cypher = """
             MATCH (n:DataResource)-[:INCLUDES]->(m)

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

@@ -0,0 +1,80 @@
+{
+  "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
+      }
+   }
+}