"""R12 适配器:网络建设与工程采购。 源明细:SrcBidding / SrcProjectSignoff 映射到:Entity(SUPPLIER, WORK_ORDER) + 关系(BIDS_FOR, SUPPLIES) + MetricEvent """ from __future__ import annotations import datetime as dt import uuid from sqlalchemy.orm import Session from app.datahub.graph_repo import add_relationship, upsert_entity from app.datahub.models import MetricEvent from app.datahub.ontology import EntityType, RelationshipType from app.datahub.staging import SrcBidding, SrcProjectSignoff from app.ingest.base import BaseAdapter, IngestResult from app.ingest.registry import register_adapter @register_adapter class BiddingAdapter(BaseAdapter): """SrcBidding → Entity(SUPPLIER, WORK_ORDER) + 关系(BIDS_FOR) + MetricEvent。""" source_system = "ERP" staging_table = "src_bidding" def ingest( self, session: Session, data_version_id: uuid.UUID | None = None, batch_size: int = 1000, ) -> IngestResult: result = IngestResult() query = session.query(SrcBidding) if data_version_id: query = query.filter(SrcBidding.data_version_id == data_version_id) rows = query.limit(batch_size).all() for row in rows: try: # 供应商(投标人)实体 supplier_entity = upsert_entity( session, entity_type=EntityType.SUPPLIER, business_key=row.bidder_key, display_name=row.bidder_name, attributes={ "legal_person": row.legal_person, "shareholder_info": row.shareholder_info, }, data_version_id=data_version_id, ) result.entities.append(supplier_entity) # 工单/项目实体 wo_entity = upsert_entity( session, entity_type=EntityType.WORK_ORDER, business_key=row.project_no, display_name=row.project_name, data_version_id=data_version_id, ) result.entities.append(wo_entity) # 投标关系 rel = add_relationship( session, RelationshipType.BIDS_FOR, supplier_entity, wo_entity, attributes={ "bid_amount": row.bid_amount, "win_flag": row.win_flag, "technical_score": row.technical_score, }, data_version_id=data_version_id, ) result.relationships.append(rel) # 中标 → 补充 SUPPLIES 关系 if row.win_flag and row.win_flag.upper() == "Y": rel2 = add_relationship( session, RelationshipType.SUPPLIES, supplier_entity, wo_entity, data_version_id=data_version_id, ) result.relationships.append(rel2) # 法人实体 if row.legal_person: lp_entity = upsert_entity( session, entity_type=EntityType.LEGAL_PERSON, business_key=row.legal_person, display_name=row.legal_person, data_version_id=data_version_id, ) add_relationship( session, RelationshipType.LEGAL_REP_OF, lp_entity, supplier_entity, data_version_id=data_version_id, ) # 投标事件 if row.bid_time: event = MetricEvent( event_time=row.bid_time, subject_type="work_order", subject_key=row.project_no, metric_name="bid_submitted", metric_value=row.bid_amount or 0.0, attributes={ "bidder_key": row.bidder_key, "bidder_name": row.bidder_name, "win_flag": row.win_flag, "technical_score": row.technical_score, }, data_version_id=data_version_id, ) session.add(event) result.metric_events.append(event) result.row_count += 1 except Exception: result.error_count += 1 return result @register_adapter class ProjectSignoffAdapter(BaseAdapter): """SrcProjectSignoff → MetricEvent(工程签证/巡检时序)。""" source_system = "WO" staging_table = "src_project_signoff" def ingest( self, session: Session, data_version_id: uuid.UUID | None = None, batch_size: int = 1000, ) -> IngestResult: result = IngestResult() query = session.query(SrcProjectSignoff) if data_version_id: query = query.filter(SrcProjectSignoff.data_version_id == data_version_id) rows = query.limit(batch_size).all() for row in rows: try: # 确保工单实体存在 upsert_entity( session, entity_type=EntityType.WORK_ORDER, business_key=row.project_no, data_version_id=data_version_id, ) # 签证事件 if row.signoff_date: event_time = dt.datetime.combine( row.signoff_date, dt.time.min, tzinfo=dt.timezone.utc ) event = MetricEvent( event_time=event_time, subject_type="work_order", subject_key=row.project_no, metric_name="signoff_quantity", metric_value=row.signoff_quantity or 0.0, attributes={ "work_order_no": row.work_order_no, "unit": row.unit, "resource_consumed": row.resource_consumed, "contractor_key": row.contractor_key, }, data_version_id=data_version_id, ) session.add(event) result.metric_events.append(event) # 巡检 GPS 事件 if row.inspection_time and row.inspection_lat: event2 = MetricEvent( event_time=row.inspection_time, subject_type="work_order", subject_key=row.project_no, metric_name="inspection", metric_value=1.0, attributes={ "lat": row.inspection_lat, "lng": row.inspection_lng, "work_order_no": row.work_order_no, }, data_version_id=data_version_id, ) session.add(event2) result.metric_events.append(event2) result.row_count += 1 except Exception: result.error_count += 1 return result