
---

## 3. main.py（核心代码）
```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
财务台账自动生成器 - 核心脚本
用法：
    python main.py --template 模板.xlsx --data 数据.xlsx --output 结果.xlsx [--mapping mapping.json]
"""

import argparse
import json
import sys
import os
from pathlib import Path

import pandas as pd
from fuzzywuzzy import fuzz

# 尝试导入 openpyxl（用于 .xlsx 读写）
try:
    import openpyxl
except ImportError:
    pass


def load_file(file_path: str) -> pd.DataFrame:
    """根据文件扩展名加载数据为 DataFrame"""
    ext = Path(file_path).suffix.lower()
    if ext == '.csv':
        return pd.read_csv(file_path, encoding='utf-8-sig')
    elif ext in ['.xlsx', '.xls']:
        return pd.read_excel(file_path, engine='openpyxl' if ext == '.xlsx' else 'xlrd')
    else:
        raise ValueError(f"不支持的文件格式: {ext}")


def auto_match_columns(template_cols: list, data_cols: list, threshold: int = 60) -> dict:
    """
    基于模糊匹配自动映射列
    返回: {模板列名: 数据列名}
    """
    mapping = {}
    used_data_cols = set()

    for t_col in template_cols:
        best_match = None
        best_score = 0
        for d_col in data_cols:
            if d_col in used_data_cols:
                continue
            score = fuzz.ratio(str(t_col).lower(), str(d_col).lower())
            if score > best_score and score >= threshold:
                best_score = score
                best_match = d_col
        if best_match:
            mapping[t_col] = best_match
            used_data_cols.add(best_match)
    return mapping


def apply_mapping(template_df: pd.DataFrame, data_df: pd.DataFrame, mapping: dict) -> pd.DataFrame:
    """
    根据映射关系填充模板 DataFrame
    """
    result = template_df.copy()
    # 如果模板是空的，保留表头；否则保留原数据（可能已有部分行）
    # 我们默认生成一个新的空行（仅表头）然后填充一行？实际上可能需要多行。
    # 这里设计为：结果保留模板的所有列，每行数据来自数据行。
    # 更合理：清空模板行，用数据行填充（如果模板有多行，只保留表头）
    if len(result) > 0:
        # 若模板有示例数据，清空行，只保留列
        result = pd.DataFrame(columns=template_df.columns)

    # 对每一行数据，创建一行填充
    for idx, data_row in data_df.iterrows():
        new_row = {}
        for t_col, d_col in mapping.items():
            if d_col in data_row:
                new_row[t_col] = data_row[d_col]
            else:
                new_row[t_col] = None
        # 对于未映射的模板列，保留空
        for t_col in template_df.columns:
            if t_col not in new_row:
                new_row[t_col] = None
        result = pd.concat([result, pd.DataFrame([new_row])], ignore_index=True)

    return result


def main():
    parser = argparse.ArgumentParser(description="财务台账自动生成器")
    parser.add_argument('--template', required=True, help='台账模板文件路径')
    parser.add_argument('--data', required=True, help='原始数据文件路径')
    parser.add_argument('--output', default='output_台账.xlsx', help='输出文件路径')
    parser.add_argument('--mapping', help='自定义列映射 JSON 文件路径（可选）')
    args = parser.parse_args()

    # 1. 加载模板和数据
    try:
        template_df = load_file(args.template)
        data_df = load_file(args.data)
    except Exception as e:
        print(f"文件加载失败: {e}")
        sys.exit(1)

    # 2. 获取列名
    template_cols = list(template_df.columns)
    data_cols = list(data_df.columns)

    # 3. 确定映射关系
    if args.mapping and os.path.exists(args.mapping):
        with open(args.mapping, 'r', encoding='utf-8') as f:
            user_mapping = json.load(f)
        # 验证用户映射中的列是否存在
        valid_mapping = {}
        for t_col, d_col in user_mapping.items():
            if t_col in template_cols and d_col in data_cols:
                valid_mapping[t_col] = d_col
            else:
                print(f"警告: 映射中的列 '{t_col}' 或 '{d_col}' 在文件中不存在，已跳过")
        mapping = valid_mapping
        # 补充未映射的模板列使用自动匹配
        auto_mapping = auto_match_columns(
            [c for c in template_cols if c not in mapping],
            [c for c in data_cols if c not in mapping.values()]
        )
        mapping.update(auto_mapping)
    else:
        mapping = auto_match_columns(template_cols, data_cols)

    # 4. 填充数据
    result_df = apply_mapping(template_df, data_df, mapping)

    # 5. 保存结果
    try:
        result_df.to_excel(args.output, index=False, engine='openpyxl')
        print(f"✅ 台账生成成功！文件保存为: {args.output}")
        print(f"📊 共填充 {len(result_df)} 行数据")
        print(f"🔗 列映射关系: {json.dumps(mapping, ensure_ascii=False, indent=2)}")
    except Exception as e:
        print(f"保存失败: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()