fix(cma): 工作台跨实体污染治本 — 全局键拆账套 + 缺列收口 + 预警归属兜底
任务 dama-workbench-entity-isolation-fullstack-20260922(DAMA 诊断 / 项目Bot batch5 §B / 任总 2026-09-22「都批」) B1 现金基线拆账套(零 DDL) - cash_forecast_engine: 键 cash.current_balance → cash.current_balance.<entity_id>; 读只读本账套键、不做旧键回退;set_* 签名 entity_id 必传(漏传 TypeError) - cash.py: POST /balance 挂 get_entity_id;网银流水联动写回带账套 B3(a) alignment_config 拆账套(零 DDL) - alignment.py: GET/POST /config、GET /tree 只读 alignment_config.<entity_id>;未配置=configured:false B3(b) 预警 KPI 归属兜底 - bot_bridge_v2: 去掉 kpi_id or 1 兜底;本账套无 KPI 定义 → 跳过建预警 + OperationLog 留痕 (kpi_alerts.kpi_id NOT NULL ⇒ 方案A 置 NULL 不可行,走方案B 跳过+审计) B2 缺列五表分表处置 - notification_logs: 经 alert_id → kpi_alerts.entity_id join 归属(无父键行不返回) - notification_channels / okr_templates: 集团共享口径写入 docstring(有意不过滤) - system_configs: 由 B1/B3(a) 拆键覆盖;kpi_data_quality_logs 沿用 batch5 的 join 收口 P0 语义收敛 - tenant.py: current_tenant 收敛为项目Bot A2A 状态记录,GET 仅 it/ceo;POST 补 operation_logs 审计 证据:新增/改写用例 22 条;全量 1370 passed(基线 1348)
This commit is contained in:
@@ -20,7 +20,30 @@ router = APIRouter(prefix="/api/cma/alignment", tags=["KPI目标对齐"],
|
||||
dependencies=[Depends(require_auth_401)],
|
||||
)
|
||||
|
||||
# 三种对齐模式定义
|
||||
# ── B3(a) 对齐模式拆账套(2026-09-22 dama-workbench-entity-isolation-fullstack-20260922)──
|
||||
# 背景:`role_permissions.key` 为 unique 单行键 → `alignment_config` 全库一行,
|
||||
# 任一账套的 ceo/it 改对齐模式 → **全账套生效**(跨账套配置污染)。
|
||||
# 治本(方案 A,零 DDL):键名带账套后缀 → `alignment_config.<entity_id>`。
|
||||
# · 读:只读本账套键;未配置 → configured=false(/tree 提示先初始化),
|
||||
# **不回退旧全局键**(回退即恢复跨账套生效)。
|
||||
# · 写:只写本账套键(entity 来自会话 token)。
|
||||
# · 旧值搬运:脚本 scripts/migrate_cash_balance_key_entity_20260922.py --check/--apply
|
||||
# (同一 500 口径:原全局值落到「当前生产账套」名下;--down 删新键并写回旧键)。
|
||||
ALIGNMENT_KEY_PREFIX = "alignment_config"
|
||||
|
||||
|
||||
def alignment_key(entity_id: int) -> str:
|
||||
"""账套维度的对齐模式键名:alignment_config.<entity_id>"""
|
||||
return f"{ALIGNMENT_KEY_PREFIX}.{int(entity_id)}"
|
||||
|
||||
|
||||
def _get_alignment_perm(db: Session, entity_id: int):
|
||||
"""读本账套的对齐模式配置行(不存在 → None)"""
|
||||
return db.query(RolePermission).filter(
|
||||
RolePermission.key == alignment_key(entity_id)
|
||||
).first()
|
||||
|
||||
|
||||
ALIGNMENT_MODES = [
|
||||
{
|
||||
"key": "vertical_decomposition",
|
||||
@@ -50,18 +73,25 @@ def list_modes():
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
def get_alignment_config(db: Session = Depends(get_db)):
|
||||
"""获取当前系统对齐模式配置(公开接口,无需认证)"""
|
||||
perm = db.query(RolePermission).filter(RolePermission.key == "alignment_config").first()
|
||||
def get_alignment_config(entity_id: int = Depends(get_entity_id),
|
||||
db: Session = Depends(get_db)):
|
||||
"""获取**当前账套**的对齐模式配置(B3(a) 拆账套,2026-09-22)
|
||||
|
||||
修复前:读全局唯一键 `alignment_config` → 任一账套改模式全平台生效。
|
||||
修复后:只读 `alignment_config.<entity_id>`;未配置 → configured=false。
|
||||
"""
|
||||
perm = _get_alignment_perm(db, entity_id)
|
||||
if not perm:
|
||||
return {
|
||||
"mode": None,
|
||||
"configured": False,
|
||||
"entity_id": entity_id,
|
||||
"modes": ALIGNMENT_MODES,
|
||||
}
|
||||
return {
|
||||
"mode": perm.value,
|
||||
"configured": True,
|
||||
"entity_id": entity_id,
|
||||
"modes": ALIGNMENT_MODES,
|
||||
}
|
||||
|
||||
@@ -69,23 +99,31 @@ def get_alignment_config(db: Session = Depends(get_db)):
|
||||
@router.post("/config")
|
||||
def set_alignment_config(
|
||||
data: dict,
|
||||
entity_id: int = Depends(get_entity_id),
|
||||
db: Session = Depends(get_db),
|
||||
user = Depends(require_role("ceo", "it")),
|
||||
):
|
||||
"""初始化/修改系统对齐模式(CEO/IT权限)"""
|
||||
"""初始化/修改**当前账套**的对齐模式(CEO/IT权限)
|
||||
|
||||
B3(a)(2026-09-22):只写 `alignment_config.<entity_id>`,不覆盖他账套;
|
||||
entity 来自会话 token,显式传他账套 entity_id → get_entity_id 403。
|
||||
"""
|
||||
mode_key = data.get("mode")
|
||||
if mode_key not in [m["key"] for m in ALIGNMENT_MODES]:
|
||||
raise HTTPException(400, f"无效的对齐模式: {mode_key}")
|
||||
|
||||
perm = db.query(RolePermission).filter(RolePermission.key == "alignment_config").first()
|
||||
perm = _get_alignment_perm(db, entity_id)
|
||||
if perm:
|
||||
perm.value = {"mode": mode_key, "set_at": datetime.now().isoformat()}
|
||||
perm.value = {"mode": mode_key, "set_at": datetime.now().isoformat(),
|
||||
"entity_id": entity_id}
|
||||
else:
|
||||
perm = RolePermission(key="alignment_config", value={"mode": mode_key, "set_at": datetime.now().isoformat()})
|
||||
perm = RolePermission(key=alignment_key(entity_id),
|
||||
value={"mode": mode_key, "set_at": datetime.now().isoformat(),
|
||||
"entity_id": entity_id})
|
||||
db.add(perm)
|
||||
db.commit()
|
||||
|
||||
return {"message": f"对齐模式已设置为: {mode_key}", "mode": mode_key}
|
||||
return {"message": f"对齐模式已设置为: {mode_key}", "mode": mode_key, "entity_id": entity_id}
|
||||
|
||||
|
||||
@router.get("/tree")
|
||||
@@ -106,8 +144,8 @@ def get_alignment_tree(
|
||||
growth-quality / predict.accuracy)。三种模式共用本列表 → 三模式同时隔离。
|
||||
跨实体显式传参(?entity_id=<他人账套>)由 get_entity_id 返回 403。
|
||||
"""
|
||||
# 获取当前模式
|
||||
config_perm = db.query(RolePermission).filter(RolePermission.key == "alignment_config").first()
|
||||
# 获取当前账套的对齐模式(B3(a) 拆账套 2026-09-22:只读 alignment_config.<entity_id>)
|
||||
config_perm = _get_alignment_perm(db, entity_id)
|
||||
mode = config_perm.value.get("mode") if config_perm else None
|
||||
if not mode:
|
||||
raise HTTPException(400, "系统未配置对齐模式,请先在系统设置中初始化")
|
||||
|
||||
Reference in New Issue
Block a user