diff --git a/backend/app/api/data.py b/backend/app/api/data.py index e8efdfd7..8125eb1d 100644 --- a/backend/app/api/data.py +++ b/backend/app/api/data.py @@ -80,11 +80,11 @@ async def import_excel(file: UploadFile = File(...), # ── 智能导入(BOT自动识别,无需手动映射) ── _SMART_MAP = { - # KPI编码列匹配模式 → 标准kpi_code + # KPI名称/编码列匹配模式(顺序重要:名称类列优先,避免科目编码被当名称) "kpi_code_patterns": [ - re.compile(r'^(kpi_?code|指标编码|编码)$', re.I), - re.compile(r'^(科目|项目|账户|报表项目|项目名称)$'), - re.compile(r'^(指标名称?|kpi名称?|name)$', re.I), + re.compile(r'^(科目名称|项目名称|指标名称?|kpi名称?|name|名称)$', re.I), + re.compile(r'^(科目编码|科目代码|kpi_?code|指标编码|编码)$', re.I), + re.compile(r'^(科目|项目|账户|报表项目)$'), ], # 期间列匹配 "period_patterns": [ @@ -218,11 +218,19 @@ async def import_excel_smart( skipped_rows = [] for idx, row in df.iterrows(): - raw_kpi = str(row.get(kpi_col, "")).strip() + raw_kpi_raw = row.get(kpi_col, "") + raw_kpi = str(raw_kpi_raw).strip() raw_val = row.get(value_col) raw_period = str(row.get(period_col, period or "")).strip() if period_col else (period or "") - if not raw_kpi or pd.isna(raw_val): + # 名称防护:NaN/空/None 或 纯数字(疑似科目编码被误当名称)→ 跳过,避免创建垃圾KPI + if raw_kpi.lower() in ("nan", "none") or not raw_kpi: + skipped_rows.append(f"第{idx+2}行: KPI名称为空") + continue + if re.fullmatch(r"\d+(\.\d+)?", raw_kpi): + skipped_rows.append(f"第{idx+2}行: KPI名称疑似科目编码「{raw_kpi}」,跳过") + continue + if pd.isna(raw_val): skipped_rows.append(f"第{idx+2}行: 缺数据") continue if not raw_period: