feat: 添加线索引擎、NLQ、场景检测、前端界面等核心功能模块

This commit is contained in:
freedakgmail
2026-06-16 08:15:15 +08:00
parent 7b1e2b10a8
commit 48340f6011
62 changed files with 6772 additions and 65 deletions
+149
View File
@@ -0,0 +1,149 @@
"""R14 适配器:云业务 / IDC 与新兴业务。
源明细:SrcCloudUsage / SrcIdcCabinet
映射到:Entity(CONTRACT, CUSTOMER) + MetricEvent
"""
from __future__ import annotations
import datetime as dt
import uuid
from sqlalchemy.orm import Session
from app.datahub.graph_repo import upsert_entity
from app.datahub.models import MetricEvent
from app.datahub.ontology import EntityType
from app.datahub.staging import SrcCloudUsage, SrcIdcCabinet
from app.ingest.base import BaseAdapter, IngestResult
from app.ingest.registry import register_adapter
@register_adapter
class CloudUsageAdapter(BaseAdapter):
"""SrcCloudUsage → Entity(CONTRACT) + MetricEvent(云资源用量时序)。"""
source_system = "BSS"
staging_table = "src_cloud_usage"
def ingest(
self,
session: Session,
data_version_id: uuid.UUID | None = None,
batch_size: int = 1000,
) -> IngestResult:
result = IngestResult()
query = session.query(SrcCloudUsage)
if data_version_id:
query = query.filter(SrcCloudUsage.data_version_id == data_version_id)
rows = query.limit(batch_size).all()
for row in rows:
try:
# 合同实体
upsert_entity(
session,
entity_type=EntityType.CONTRACT,
business_key=row.contract_no,
data_version_id=data_version_id,
)
# 客户实体(如有)
if row.customer_key:
upsert_entity(
session,
entity_type=EntityType.CUSTOMER,
business_key=row.customer_key,
data_version_id=data_version_id,
)
# 云资源用量事件
if row.usage_date:
event_time = dt.datetime.combine(
row.usage_date, dt.time.min, tzinfo=dt.timezone.utc
)
event = MetricEvent(
event_time=event_time,
subject_type="contract",
subject_key=row.contract_no,
metric_name="cloud_usage",
metric_value=row.actual_usage,
attributes={
"resource_type": row.resource_type,
"contracted_quota": row.contracted_quota,
"billed_usage": row.billed_usage,
"unit": row.unit,
"customer_key": row.customer_key,
},
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 IdcCabinetAdapter(BaseAdapter):
"""SrcIdcCabinet → MetricEventIDC 机柜出租率/电力时序)。"""
source_system = "OSS"
staging_table = "src_idc_cabinet"
def ingest(
self,
session: Session,
data_version_id: uuid.UUID | None = None,
batch_size: int = 1000,
) -> IngestResult:
result = IngestResult()
query = session.query(SrcIdcCabinet)
if data_version_id:
query = query.filter(SrcIdcCabinet.data_version_id == data_version_id)
rows = query.limit(batch_size).all()
for row in rows:
try:
# 合同实体(如有)
if row.contract_no:
upsert_entity(
session,
entity_type=EntityType.CONTRACT,
business_key=row.contract_no,
data_version_id=data_version_id,
)
# IDC 出租/电力事件
try:
event_time = dt.datetime.strptime(
row.report_month, "%Y-%m"
).replace(tzinfo=dt.timezone.utc) if row.report_month else dt.datetime.now(dt.timezone.utc)
except ValueError:
event_time = dt.datetime.now(dt.timezone.utc)
event = MetricEvent(
event_time=event_time,
subject_type="contract",
subject_key=row.contract_no or row.cabinet_id,
metric_name="idc_cabinet",
metric_value=row.occupancy_rate or 0.0,
attributes={
"cabinet_id": row.cabinet_id,
"customer_key": row.customer_key,
"power_kwh": row.power_kwh,
"revenue_amount": row.revenue_amount,
"acceptance_date": str(row.acceptance_date) if row.acceptance_date else None,
},
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