From 22ec40052da8afbc5cb4372faa096f140dee9817 Mon Sep 17 00:00:00 2001 From: wecom-fullstack Date: Thu, 1 Oct 2026 15:58:40 +0800 Subject: [PATCH] fix(alignment): /tree accept and apply map_id filter for KPI selection GET /api/cma/alignment/tree ignored the map_id query param (FastAPI silently dropped it) so the board always rendered the full active-KPI set of the account. Add map_id: Optional[int] = Query(None) and filter KPIDefinition.map_id when provided; no param keeps the previous behaviour. Entity scoping unchanged (entity_id from token) - cross-account map_id yields an empty tree, no other-account KPI is returned. Only the KPI selection set is filtered; vertical/horizontal/bsc_chain builders untouched. --- backend/app/api/alignment.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/backend/app/api/alignment.py b/backend/app/api/alignment.py index 63f7b495..2e68bf7e 100644 --- a/backend/app/api/alignment.py +++ b/backend/app/api/alignment.py @@ -129,6 +129,7 @@ def set_alignment_config( @router.get("/tree") def get_alignment_tree( kpi_id: Optional[int] = Query(None), + map_id: Optional[int] = Query(None), entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db), user = Depends(require_auth), @@ -143,6 +144,13 @@ def get_alignment_tree( 只取 entity_id 归属本账套的 KPI(entity_id 来自会话 token 绑定,写法对齐 growth-quality / predict.accuracy)。三种模式共用本列表 → 三模式同时隔离。 跨实体显式传参(?entity_id=<他人账套>)由 get_entity_id 返回 403。 + + 地图筛选(2026-10-01 project-20261001-align-tree-map-filter-fullstack): + `map_id` 参与**KPI 选择集**过滤——给定 map_id 时只保留 + `KPIDefinition.map_id == map_id` 的本账套 active KPI;不传时维持改前行为(全量)。 + 跨账套 map_id:KPI 选择集本身已按 entity_id 隔离(且关联写入端点 + `PUT /kpis/{id}/associate-map` 亦强制 KPI↔地图同账套)⇒ 返回空树,不泄漏他账套 KPI。 + 过滤只作用于 KPI 选择集,三种模式的构建函数(vertical/horizontal/bsc_chain)不变。 """ # 获取当前账套的对齐模式(B3(a) 拆账套 2026-09-22:只读 alignment_config.) config_perm = _get_alignment_perm(db, entity_id) @@ -151,10 +159,13 @@ def get_alignment_tree( raise HTTPException(400, "系统未配置对齐模式,请先在系统设置中初始化") # 获取本账套所有KPI(账套隔离:仅当前 entity_id,不再返回全实体 KPI) - kpis = (db.query(KPIDefinition) - .filter(KPIDefinition.entity_id == entity_id, KPIDefinition.status == "active") - .order_by(KPIDefinition.kpi_code) - .all()) + kpi_query = (db.query(KPIDefinition) + .filter(KPIDefinition.entity_id == entity_id, + KPIDefinition.status == "active")) + # 地图筛选:只缩小 KPI 选择集,不改动任何构建逻辑 + if map_id is not None: + kpi_query = kpi_query.filter(KPIDefinition.map_id == map_id) + kpis = kpi_query.order_by(KPIDefinition.kpi_code).all() kpi_map = {k.id: k for k in kpis} # 构建父子关系