150 lines
5.2 KiB
Python
150 lines
5.2 KiB
Python
"""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 → MetricEvent(IDC 机柜出租率/电力时序)。"""
|
||
|
||
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
|