fix: 智能导入——匹配不上的财务报表科目自动创建KPI定义
- 清理科目前缀(一、/减:/加:)后多级匹配 - ⑤仍未匹配→自动创建KPI(PL_001/CF_001/BS_001) - 避免205条全部跳过的场景
This commit is contained in:
+47
-13
@@ -190,10 +190,16 @@ async def import_excel_smart(file: UploadFile = File(...), db: Session = Depends
|
||||
for code in known_codes:
|
||||
clean = re.sub(r'[\s\-_()()]', '', code).lower()
|
||||
alias_map[clean] = code
|
||||
# 中文名映射("营业收入"→F_REVENUE)
|
||||
name_map: dict[str, str] = {}
|
||||
for code, kpi_obj in kpis.items():
|
||||
name_map[kpi_obj.kpi_name] = code
|
||||
|
||||
# 8. 遍历导入
|
||||
# 8. 遍历导入(匹配不上的自动创建KPI)
|
||||
stype_prefix = {"PL": "PL_", "CF": "CF_", "BS": "BS_"}.get(stype or "", "EXT_")
|
||||
batch = hashlib.md5(str(datetime.now().timestamp()).encode()).hexdigest()[:12]
|
||||
imported = 0
|
||||
created_kpis = 0
|
||||
skipped_rows = []
|
||||
|
||||
for idx, row in df.iterrows():
|
||||
@@ -208,24 +214,50 @@ async def import_excel_smart(file: UploadFile = File(...), db: Session = Depends
|
||||
skipped_rows.append(f"第{idx+2}行: 无法确定期间")
|
||||
continue
|
||||
|
||||
# 智能匹配KPI编码
|
||||
# 清理科目名(去掉"一、""减:""加:"等前缀)
|
||||
clean_name = re.sub(r'^[一二三四五六七八九十、\s\+]+', '', raw_kpi)
|
||||
clean_name = re.sub(r'^[减加]?[::]\s*', '', clean_name).strip()
|
||||
if not clean_name:
|
||||
clean_name = raw_kpi
|
||||
|
||||
# 匹配KPI
|
||||
kpi_code = None
|
||||
|
||||
# ① 精确编码匹配(极少情况)
|
||||
if raw_kpi in known_codes:
|
||||
kpi_code = raw_kpi
|
||||
else:
|
||||
# 别名匹配
|
||||
# ② 别名匹配(去符号小写)
|
||||
if not kpi_code:
|
||||
clean_key = re.sub(r'[\s\-_()()]', '', raw_kpi).lower()
|
||||
kpi_code = alias_map.get(clean_key)
|
||||
# 模糊匹配(中文科目名→KPI编码)
|
||||
if not kpi_code:
|
||||
for code, kpi_obj in kpis.items():
|
||||
if raw_kpi in kpi_obj.kpi_name or kpi_obj.kpi_name in raw_kpi:
|
||||
kpi_code = code
|
||||
break
|
||||
|
||||
# ③ 中文名精确匹配
|
||||
if not kpi_code:
|
||||
skipped_rows.append(f"第{idx+2}行: 「{raw_kpi}」未匹配到KPI")
|
||||
continue
|
||||
kpi_code = name_map.get(clean_name)
|
||||
# ④ 中文名模糊匹配
|
||||
if not kpi_code:
|
||||
for code, kpi_obj in kpis.items():
|
||||
if clean_name in kpi_obj.kpi_name or kpi_obj.kpi_name in clean_name:
|
||||
kpi_code = code
|
||||
break
|
||||
|
||||
# ⑤ 仍未匹配 → 自动创建KPI
|
||||
if not kpi_code:
|
||||
new_code = f"{stype_prefix}{len(kpis) + created_kpis + 1:03d}"
|
||||
new_kpi = KPIDefinition(
|
||||
kpi_code=new_code,
|
||||
kpi_name=clean_name,
|
||||
dimension="finance",
|
||||
category="financial_report",
|
||||
data_source_type="excel",
|
||||
status="active",
|
||||
)
|
||||
db.add(new_kpi)
|
||||
db.flush()
|
||||
kpis[new_code] = new_kpi
|
||||
known_codes.add(new_code)
|
||||
name_map[clean_name] = new_code
|
||||
kpi_code = new_code
|
||||
created_kpis += 1
|
||||
|
||||
try:
|
||||
val = float(raw_val)
|
||||
@@ -248,6 +280,8 @@ async def import_excel_smart(file: UploadFile = File(...), db: Session = Depends
|
||||
# 9. 返回汇总
|
||||
stype_label = {"PL": "利润表", "CF": "现金流量表", "BS": "资产负债表"}.get(stype or "", "数据表")
|
||||
msg = f"✅ {stype_label}识别成功,导入{imported}条"
|
||||
if created_kpis:
|
||||
msg += f",自动创建{created_kpis}个新KPI"
|
||||
if skipped_rows:
|
||||
msg += f",{len(skipped_rows)}条跳过:\n" + "\n".join(skipped_rows[:8])
|
||||
if len(skipped_rows) > 8:
|
||||
|
||||
Reference in New Issue
Block a user