feat: 添加线索引擎、NLQ、场景检测、前端界面等核心功能模块
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user