feat(cma): audit write-side entity closure + entity-dimension data dictionary
task dama-entity-isolation-sweep-20260923 (sections 2-2 4/5, merged by projectBot 2026-09-23 18:2x)
4) operation_logs write side: every audit write carries the account (no DDL, no column added)
- new app/utils/oplog.py::log_op() single wrapper: sets detail.entity_id; scope=global for
by-design non-entity writes (platform dict / role-level); scope=batch + OPLOG_BATCH_NO_ENTITY
sentinel for cross-account batches; no context -> OPLOG_NO_ENTITY sentinel (never guess)
- 16 remaining construction points converted (data/alert_rules/bi_reports/kpi_causality/
bot_bridge x2/budget_generate/driver_budget/kpis _log x3/templates _log x2/erp_sync);
pre-4 state had 20 sites without entity_id, now 0 outside the wrapper
- read side untouched (knowledge_service still user_id fallback); history not backfilled
5) docs/entity-dimension-data-dictionary.md: 8-table three-state registry
(kpi_templates/driver_factor_templates/scenario_suggestions/bot_bridge_config = non-entity;
kpi_subject_map = inherited via kpi_id; analysis_results/subjects = account dimension,
legacy rows still NULL -> other task; operation_logs = non-entity column + detail.entity_id)
Evidence: RED 2 failed/7 passed (reverted call sites) -> GREEN full 1428 passed; 9 new tests
including two read-back rows; app md5 restore verified identical; no cross-account writes
LOCAL commit only - push is gated (ahead contains the P1 entity-dim batch commit fb4609b)
This commit is contained in:
+13
-10
@@ -23,6 +23,7 @@ load_dotenv()
|
||||
|
||||
from app.database import get_engine, get_session_local
|
||||
from app.models import KPIDefinition, KPIValue, OperationLog, DataSourceConfig
|
||||
from app.utils.oplog import log_op, SCOPE_BATCH
|
||||
|
||||
logger = logging.getLogger("erp_sync")
|
||||
|
||||
@@ -421,16 +422,18 @@ def run_sync(dry_run: bool = False, kpi_codes: list = None, use_api: bool = True
|
||||
fail += 1
|
||||
|
||||
if not dry_run:
|
||||
log = OperationLog(
|
||||
action="erp_sync",
|
||||
target_type="kpi",
|
||||
detail=json.dumps({
|
||||
"total": len(kpis), "success": success,
|
||||
"failed": fail, "api_mode": use_api,
|
||||
"period": datetime.now().strftime("%Y-%m"),
|
||||
}, ensure_ascii=False),
|
||||
)
|
||||
db.add(log)
|
||||
# 2026-09-23 ④:审计带账套。本脚本一次遍历**全库 active ERP KPI(可能跨账套)**:
|
||||
# 全部同账套 ⇒ 写该账套;跨账套 ⇒ 留空 + OPLOG_BATCH_NO_ENTITY 哨兵(不猜账套)
|
||||
_entity_ids = sorted({getattr(k, "entity_id", None) for k in kpis} - {None})
|
||||
log_op(db, "erp_sync", target_type="kpi",
|
||||
detail={
|
||||
"total": len(kpis), "success": success,
|
||||
"failed": fail, "api_mode": use_api,
|
||||
"period": datetime.now().strftime("%Y-%m"),
|
||||
"kpi_entity_ids": _entity_ids,
|
||||
},
|
||||
entity_id=(_entity_ids[0] if len(_entity_ids) == 1 else None),
|
||||
scope=(SCOPE_BATCH if len(_entity_ids) != 1 else None))
|
||||
db.commit()
|
||||
|
||||
logger.info(f"同步完成: {success}成功 / {fail}失败 / {len(kpis)}总计")
|
||||
|
||||
Reference in New Issue
Block a user