65 lines
2.0 KiB
Python
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,
|
|
)
|