Files
SBrainCO/db/pipeline/run_all.py
T
freedakgmail 2fc1b96f44 feat: 新增db/pipeline数据导入管线,适配5月数据格式
- 整合所有导入脚本到db/pipeline/目录
- config.py: 按月份动态配置数据源路径,支持4月/5月不同目录结构
- import_bill_records_may.py: 5月专用账单导入(51列全渠道订单明细→bill_records + 50列品项销售明细→dish_sales_details)
- import_salary_attendance.py: 添加--salary-file/--attendance-file参数支持动态文件路径
- import_may_data.sh: 5月一键导入脚本(12步全流程)
- run_all.py: 一键全量导入+物化视图刷新编排
- refresh_materialized_views.py: 按依赖顺序刷新38个物化视图
- verify.py/verify_import.py: 数据一致性验证
- README.md: 管线文档
2026-08-18 22:17:26 +08:00

196 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
一键全量导入 + 物化视图刷新
用法: python3 run_all.py --month 2026-04-01 --db bill_query_test
python3 run_all.py --month 2026-04-01 --db bill_query_test --skip-import # 仅刷新物化
python3 run_all.py --month 2026-04-01 --db bill_query_test --skip-views # 仅导入
python3 run_all.py --month 2026-04-01 --db bill_query_test --only bill_records # 仅导入某类
"""
import argparse
import os
import subprocess
import sys
import time
from config import DB_CONFIG, get_data_sources, PIPELINE_DIR
def run_script(name, cmd, cwd=None):
"""运行子进程并实时输出"""
print(f"\n{'='*60}")
print(f" 执行: {name}")
print(f" 命令: {' '.join(cmd)}")
print(f"{'='*60}")
t0 = time.time()
result = subprocess.run(cmd, cwd=cwd or PIPELINE_DIR)
elapsed = time.time() - t0
status = "成功" if result.returncode == 0 else f"失败(rc={result.returncode})"
print(f"{name}: {status} ({elapsed:.1f}s)")
return result.returncode == 0
def main():
parser = argparse.ArgumentParser(description='全量数据导入与物化刷新流水线')
parser.add_argument('--month', required=True, help='报告月份 (YYYY-MM-01)')
parser.add_argument('--db', default='bill_query_test', help='目标数据库')
parser.add_argument('--skip-import', action='store_true', help='跳过数据导入,仅刷新物化视图')
parser.add_argument('--skip-views', action='store_true', help='跳过物化视图刷新')
parser.add_argument('--only', default=None,
help='仅导入指定数据集: bill_records, monthly, salary, store_location, derived')
parser.add_argument('--diagnosis-date', default=None, help='诊断日期 (默认今天)')
args = parser.parse_args()
sources = get_data_sources(args.month)
if not sources:
print(f"错误: 无法找到月份 {args.month} 的数据源")
sys.exit(1)
print(f"\n{'#'*60}")
print(f" 数据导入与物化刷新流水线")
print(f" 月份: {args.month}")
print(f" 数据库: {args.db}")
print(f" 数据源根: {sources.get('bill_records', 'N/A')}")
print(f"{'#'*60}")
py = sys.executable
results = []
# ============================================================
# Step 1: 数据导入
# ============================================================
if not args.skip_import:
target = args.only
# 1.1 薪资+考勤
if target in (None, 'salary'):
cmd = [py, 'import_salary_attendance.py', '--month', args.month, '--db', args.db]
sal_file = sources.get('salary')
att_file = sources.get('attendance')
if sal_file and os.path.isfile(sal_file):
cmd.extend(['--salary-file', sal_file])
if att_file and os.path.isfile(att_file):
cmd.extend(['--attendance-file', att_file])
ok = run_script("薪资+考勤导入", cmd)
results.append(('薪资+考勤', ok))
# 1.2 账单查询原始数据(bill_records + bill_columns
if target in (None, 'bill_records'):
bill_dir = sources.get('bill_records')
if bill_dir and os.path.exists(bill_dir):
ok = run_script("账单查询导入(bill_records)", [
py, 'import_bill_records.py',
'--month', args.month,
'--file', bill_dir,
'--db', args.db,
])
results.append(('bill_records', ok))
else:
print(f"\n 跳过 bill_records: 路径不存在 {bill_dir}")
results.append(('bill_records', False))
# 1.3 月度业务数据(库存/销售/成本/费用/中央厨房/配送)
if target in (None, 'monthly'):
monthly_datasets = [
('inventory', 'inventory', None),
('dish_sales', 'dish_sales', None),
('dish_cost', 'dish_cost', None),
('operating_expense', 'operating_expense', None),
('central_kitchen_finished', 'central_kitchen', 'finished'),
('central_kitchen_recipe', 'central_kitchen', 'recipe'),
('central_kitchen_material', 'central_kitchen', 'material'),
('central_kitchen_processing', 'central_kitchen', 'processing'),
('distribution', 'distribution', None),
('distribution_2', 'distribution', None),
]
for src_key, dataset, ck_type in monthly_datasets:
filepath = sources.get(src_key)
if not filepath or not os.path.exists(filepath):
print(f"\n 跳过 {dataset}/{ck_type or ''}: 文件不存在 {filepath}")
results.append((f'{dataset}/{ck_type or src_key}', False))
continue
cmd = [
py, 'import_monthly_data.py',
'--dataset', dataset,
'--month', args.month,
'--file', filepath,
'--db', args.db,
]
if ck_type:
cmd.extend(['--ck-type', ck_type])
label = f"月度数据-{dataset}" + (f"-{ck_type}" if ck_type else f"({os.path.basename(filepath)})")
ok = run_script(label, cmd)
results.append((label, ok))
# 1.4 门店位置与映射表
if target in (None, 'store_location'):
store_file = sources.get('store_location')
if store_file and os.path.exists(store_file):
ok = run_script("门店位置与映射表导入", [
py, 'import_store_location.py',
'--file', store_file,
'--db', args.db,
])
results.append(('门店位置', ok))
else:
print(f"\n 跳过门店位置: 文件不存在 {store_file}")
results.append(('门店位置', False))
# 1.5 派生分析数据
if target in (None, 'derived'):
diag_date = args.diagnosis_date or ''
cmd = [
py, 'import_derived_data.py',
'--month', args.month,
'--db', args.db,
]
if diag_date:
cmd.extend(['--diagnosis-date', diag_date])
ok = run_script("派生分析数据生成", cmd)
results.append(('派生数据', ok))
# ============================================================
# Step 2: 物化视图刷新
# ============================================================
if not args.skip_views:
ok = run_script("物化视图刷新", [
py, 'refresh_materialized_views.py',
'--db', args.db,
], cwd=PIPELINE_DIR)
results.append(('物化视图', ok))
# ============================================================
# Step 3: 验证
# ============================================================
ok = run_script("数据一致性验证", [
py, 'verify.py',
'--db', args.db,
], cwd=PIPELINE_DIR)
results.append(('验证', ok))
# ============================================================
# 汇总
# ============================================================
print(f"\n{'#'*60}")
print(f" 流水线执行汇总")
print(f"{'#'*60}")
all_ok = True
for name, ok in results:
status = "✓ 成功" if ok else "✗ 失败"
print(f" {status} {name}")
if not ok:
all_ok = False
if all_ok:
print(f"\n 全部完成!")
sys.exit(0)
else:
print(f"\n 部分失败,请检查上方日志")
sys.exit(1)
if __name__ == '__main__':
main()