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
+147
View File
@@ -0,0 +1,147 @@
"""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