Files
InternalAuditInterprise/backend/app/api/datahub.py
T
2026-06-16 00:38:57 +08:00

65 lines
2.0 KiB
Python

"""数据中台统一穿透查询 API(P1.2.5)。
作为各引擎与审计场景访问知识图谱的共同入口,对上层屏蔽底层是关系表还是图库。
对应需求 R2。
"""
from __future__ import annotations
import uuid
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.api.schemas import (
EntityOut,
PenetrateRequest,
PenetrateResponse,
RelatedEntityOut,
)
from app.datahub.graph_repo import find_related_entities
from app.datahub.models import Entity
from app.db import get_session
router = APIRouter(prefix="/datahub", tags=["datahub"])
@router.get("/entities/{entity_id}", response_model=EntityOut)
def get_entity(entity_id: uuid.UUID, session: Session = Depends(get_session)) -> Entity:
entity = session.get(Entity, entity_id)
if entity is None:
raise HTTPException(status_code=404, detail="实体不存在")
return entity
@router.post("/penetrate", response_model=PenetrateResponse)
def penetrate(
req: PenetrateRequest, session: Session = Depends(get_session)
) -> PenetrateResponse:
"""多跳穿透:返回与起点实体连通的关联实体(用于实控人/关联方/马甲识别)。"""
start = session.get(Entity, req.start_entity_id)
if start is None:
raise HTTPException(status_code=404, detail="起点实体不存在")
related_raw = find_related_entities(session, req.start_entity_id, max_depth=req.max_depth)
# 批量取出关联实体详情,组装可解释结果
id_to_depth = {rid: depth for rid, depth in related_raw}
entities = (
session.query(Entity).filter(Entity.id.in_(list(id_to_depth.keys()))).all()
if id_to_depth
else []
)
related = [
RelatedEntityOut(entity=EntityOut.model_validate(e), depth=id_to_depth[e.id])
for e in entities
]
related.sort(key=lambda r: r.depth)
return PenetrateResponse(
start_entity_id=req.start_entity_id,
max_depth=req.max_depth,
related_count=len(related),
related=related,
)