148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
"""R13 适配器:互联互通与网间结算。
|
|
|
|
源明细:SrcCdr / SrcInterconnectSettlement
|
|
映射到:Entity(MSISDN, SETTLEMENT) + 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 SrcCdr, SrcInterconnectSettlement
|
|
from app.ingest.base import BaseAdapter, IngestResult
|
|
from app.ingest.registry import register_adapter
|
|
|
|
|
|
@register_adapter
|
|
class CdrAdapter(BaseAdapter):
|
|
"""SrcCdr → Entity(MSISDN) + MetricEvent(话务时序)。"""
|
|
|
|
source_system = "SIGNAL"
|
|
staging_table = "src_cdr"
|
|
|
|
def ingest(
|
|
self,
|
|
session: Session,
|
|
data_version_id: uuid.UUID | None = None,
|
|
batch_size: int = 1000,
|
|
) -> IngestResult:
|
|
result = IngestResult()
|
|
query = session.query(SrcCdr)
|
|
if data_version_id:
|
|
query = query.filter(SrcCdr.data_version_id == data_version_id)
|
|
rows = query.limit(batch_size).all()
|
|
|
|
for row in rows:
|
|
try:
|
|
# 确保主被叫号码实体存在
|
|
upsert_entity(
|
|
session,
|
|
entity_type=EntityType.MSISDN,
|
|
business_key=row.caller,
|
|
display_name=row.caller,
|
|
data_version_id=data_version_id,
|
|
)
|
|
upsert_entity(
|
|
session,
|
|
entity_type=EntityType.MSISDN,
|
|
business_key=row.callee,
|
|
display_name=row.callee,
|
|
data_version_id=data_version_id,
|
|
)
|
|
|
|
# 话务事件
|
|
event = MetricEvent(
|
|
event_time=row.start_time,
|
|
subject_type="msisdn",
|
|
subject_key=row.caller,
|
|
metric_name="cdr_duration",
|
|
metric_value=float(row.duration_sec),
|
|
attributes={
|
|
"callee": row.callee,
|
|
"call_type": row.call_type,
|
|
"peer_operator": row.peer_operator,
|
|
"route_info": row.route_info,
|
|
},
|
|
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 InterconnectSettlementAdapter(BaseAdapter):
|
|
"""SrcInterconnectSettlement → Entity(SETTLEMENT) + MetricEvent。"""
|
|
|
|
source_system = "FIN"
|
|
staging_table = "src_interconnect_settlement"
|
|
|
|
def ingest(
|
|
self,
|
|
session: Session,
|
|
data_version_id: uuid.UUID | None = None,
|
|
batch_size: int = 1000,
|
|
) -> IngestResult:
|
|
result = IngestResult()
|
|
query = session.query(SrcInterconnectSettlement)
|
|
if data_version_id:
|
|
query = query.filter(SrcInterconnectSettlement.data_version_id == data_version_id)
|
|
rows = query.limit(batch_size).all()
|
|
|
|
for row in rows:
|
|
try:
|
|
# 结算单实体
|
|
settle_entity = upsert_entity(
|
|
session,
|
|
entity_type=EntityType.SETTLEMENT,
|
|
business_key=row.settlement_no,
|
|
display_name=f"网间结算-{row.settlement_no}",
|
|
attributes={
|
|
"peer_operator": row.peer_operator,
|
|
"settle_type": row.settle_type,
|
|
},
|
|
data_version_id=data_version_id,
|
|
)
|
|
result.entities.append(settle_entity)
|
|
|
|
# 结算时序事件
|
|
try:
|
|
event_time = dt.datetime.strptime(
|
|
row.settle_period, "%Y-%m"
|
|
).replace(tzinfo=dt.timezone.utc)
|
|
except ValueError:
|
|
event_time = dt.datetime.now(dt.timezone.utc)
|
|
|
|
event = MetricEvent(
|
|
event_time=event_time,
|
|
subject_type="settlement",
|
|
subject_key=row.settlement_no,
|
|
metric_name="interconnect_settle",
|
|
metric_value=row.settle_amount,
|
|
attributes={
|
|
"peer_operator": row.peer_operator,
|
|
"settle_type": row.settle_type,
|
|
"volume": row.volume,
|
|
"unit_price": row.unit_price,
|
|
"sms_delivery_rate": row.sms_delivery_rate,
|
|
},
|
|
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
|