feat: 数据治理 — 入库约束+元数据卡片+编码清洗+审计看板

This commit is contained in:
Hermes CI Fix
2026-07-22 12:06:52 +08:00
parent ec6af751a5
commit 748c2da43f
8 changed files with 452 additions and 2 deletions
+35
View File
@@ -406,12 +406,43 @@ def get_kpi(kpi_id: int, db: Session = Depends(get_db)):
return kpi_to_dict(kpi)
def _validate_kpi_data(data: dict, is_update: bool = False):
"""数据治理:入库必检 + 元数据校验"""
errors = []
# 规则1: target_value 必填
tv = data.get("target_value")
if tv is None or (isinstance(tv, (int, float)) and tv < 0 and not is_update):
if not is_update or "target_value" in data:
if tv is None:
errors.append("目标值(target_value)不能为空")
# 规则1: unit 必填
unit = data.get("unit")
if not unit or (isinstance(unit, str) and unit.strip() == ""):
if not is_update or "unit" in data:
errors.append("单位(unit)不能为空")
# 规则2: 元数据必填 — formula/data_source/data_owner
for field, label in [("formula", "计算公式"), ("data_source", "数据来源"), ("data_owner", "数据责任人")]:
val = data.get(field)
if not val or (isinstance(val, str) and val.strip() == ""):
if not is_update or field in data:
errors.append(f"元数据字段'{label}'({field})不能为空")
return errors
@router.post("")
def create_kpi(data: dict, db: Session = Depends(get_db), user=WRITE_ROLES):
# 检查编码唯一性
existing = db.query(KPIDefinition).filter(KPIDefinition.kpi_code == data.get("kpi_code", "")).first()
if existing:
raise HTTPException(400, f"KPI编码 {data['kpi_code']} 已存在")
# 数据治理校验
errs = _validate_kpi_data(data, is_update=False)
if errs:
raise HTTPException(422, detail={"message": "数据校验不通过", "errors": errs})
kpi = KPIDefinition(**data)
db.add(kpi)
db.commit()
@@ -425,6 +456,10 @@ def update_kpi(kpi_id: int, data: dict, db: Session = Depends(get_db), user=WRIT
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == kpi_id).first()
if not kpi:
raise HTTPException(404, "KPI不存在")
# 数据治理校验(更新时只检查传了但为空的字段)
errs = _validate_kpi_data(data, is_update=True)
if errs:
raise HTTPException(422, detail={"message": "数据校验不通过", "errors": errs})
for k, v in data.items():
if hasattr(kpi, k) and v is not None:
setattr(kpi, k, v)