feat: P0/P1/P2全部功能 — 四层泳道/视角切换/KPI看板/预警/差异反打/预算/知识面板/回顾会/情景预测/Excel导入/角色权限
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Run everything inline by importing directly
|
||||
import sys, os
|
||||
|
||||
# Step 1: Setup path and working directory
|
||||
backend_dir = '/root/cma-management/backend'
|
||||
sys.path.insert(0, backend_dir)
|
||||
os.chdir(backend_dir)
|
||||
|
||||
# Step 2: Load env
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
# Step 3: Import required modules
|
||||
from app.database import get_session_local
|
||||
from app.models import DataSourceConfig, KPIValue, KPIDefinition
|
||||
from openpyxl import Workbook
|
||||
import urllib.request, json, logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
|
||||
|
||||
# ====================================================================
|
||||
# Sub-task 1: Insert data source
|
||||
# ====================================================================
|
||||
print("=" * 70)
|
||||
print("【子任务1】插入数据源记录到 data_source_config")
|
||||
print("=" * 70)
|
||||
|
||||
db = get_session_local()()
|
||||
try:
|
||||
existing = db.query(DataSourceConfig).filter(DataSourceConfig.name == 'ERP系统 - 博海网络').first()
|
||||
if existing:
|
||||
print(f" [OK] 数据源已存在: id={existing.id}, name={existing.name}")
|
||||
else:
|
||||
source = DataSourceConfig(
|
||||
name='ERP系统 - 博海网络',
|
||||
source_type='erp',
|
||||
api_endpoint='http://127.0.0.1:8300/api/v1',
|
||||
api_key='erp-gateway-key-bhwl-2026',
|
||||
sync_type='batch',
|
||||
status='active',
|
||||
)
|
||||
db.add(source)
|
||||
db.commit()
|
||||
db.refresh(source)
|
||||
print(f" [OK] 数据源插入成功: id={source.id}")
|
||||
|
||||
print(" data_source_config 表当前记录:")
|
||||
for s in db.query(DataSourceConfig).all():
|
||||
print(f" id={s.id}, name={s.name}, type={s.source_type}, status={s.status}")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# ====================================================================
|
||||
# Sub-task 4: Create Excel template
|
||||
# ====================================================================
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("【子任务4】创建Excel导入模板")
|
||||
print("=" * 70)
|
||||
|
||||
template_dir = os.path.join(backend_dir, 'templates')
|
||||
os.makedirs(template_dir, exist_ok=True)
|
||||
print(f" [OK] 确保目录存在: {template_dir}")
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = 'KPI导入模板'
|
||||
|
||||
headers = ['kpi_code', 'kpi_name', 'period', 'actual_value', 'unit', 'dimension']
|
||||
for col, h in enumerate(headers, 1):
|
||||
ws.cell(row=1, column=col, value=h)
|
||||
|
||||
sample_data = [
|
||||
['F_REVENUE', '营业收入', '2026-06', 500000, '万元', 'finance'],
|
||||
['F_PROFIT_RATE', '销售毛利率', '2026-06', 28.5, '%', 'finance'],
|
||||
]
|
||||
for row_idx, row_data in enumerate(sample_data, 2):
|
||||
for col_idx, value in enumerate(row_data, 1):
|
||||
ws.cell(row=row_idx, column=col_idx, value=value)
|
||||
|
||||
output_path = os.path.join(template_dir, 'kpi_import_template.xlsx')
|
||||
wb.save(output_path)
|
||||
|
||||
print(f" [OK] 模板已创建: {output_path}")
|
||||
print(f" [OK] 文件大小: {os.path.getsize(output_path)} bytes")
|
||||
|
||||
# Verify content
|
||||
from openpyxl import load_workbook
|
||||
wb2 = load_workbook(output_path)
|
||||
ws2 = wb2.active
|
||||
print(" [VERIFY] 模板内容:")
|
||||
for row in ws2.iter_rows(min_row=1, max_row=3, values_only=True):
|
||||
print(f" {list(row)}")
|
||||
|
||||
# ====================================================================
|
||||
# Sub-task 2: ERP sync verification
|
||||
# ====================================================================
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("【子任务2】验证ERP同步全链路")
|
||||
print("=" * 70)
|
||||
|
||||
# Check if backend is running
|
||||
print(" 检查后端服务状态...")
|
||||
try:
|
||||
req = urllib.request.Request('http://127.0.0.1:8010/health')
|
||||
resp = urllib.request.urlopen(req, timeout=5)
|
||||
status = json.loads(resp.read().decode())
|
||||
print(f" [OK] 后端服务运行中: {status}")
|
||||
except Exception as e:
|
||||
print(f" [WARN] 后端服务未运行: {e}")
|
||||
print(" [INFO] erp_sync 直接使用数据库,不依赖后端HTTP服务")
|
||||
|
||||
# Run dry-run
|
||||
print("\n --- 执行 erp_sync.py --dry-run ---")
|
||||
from scripts.erp_sync import run_sync
|
||||
try:
|
||||
run_sync(dry_run=True)
|
||||
print(" [OK] dry-run 完成")
|
||||
except Exception as e:
|
||||
print(f" [INFO] dry-run 输出: {e}")
|
||||
|
||||
# Run actual sync
|
||||
print("\n --- 执行 erp_sync.py (实际同步) ---")
|
||||
try:
|
||||
run_sync(dry_run=False)
|
||||
print(" [OK] 实际同步完成")
|
||||
except Exception as e:
|
||||
print(f" [INFO] 同步输出: {e}")
|
||||
|
||||
# Verify results in kpi_values
|
||||
print("\n --- 验证 kpi_values 表 ---")
|
||||
db = get_session_local()()
|
||||
try:
|
||||
erp_values = db.query(KPIValue).filter(KPIValue.source_type == 'erp').all()
|
||||
print(f" source_type='erp' 的记录数: {len(erp_values)}")
|
||||
for v in erp_values:
|
||||
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == v.kpi_id).first()
|
||||
kpi_code = kpi.kpi_code if kpi else 'N/A'
|
||||
print(f" kpi={kpi_code}, period={v.period}, value={v.actual_value}, status={v.data_status}, remark={v.remark}")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# ====================================================================
|
||||
# Sub-task 3: Set up cron
|
||||
# ====================================================================
|
||||
print()
|
||||
print("=" * 70)
|
||||
print("【子任务3】设定定时同步 (crontab)")
|
||||
print("=" * 70)
|
||||
|
||||
cron_entry = "0 1 * * * cd /root/cma-management/backend && /usr/bin/python3 scripts/erp_sync.py >> /var/log/cma-erp-sync.log 2>&1"
|
||||
|
||||
# Try to add to crontab
|
||||
try:
|
||||
import subprocess
|
||||
# Get existing crontab
|
||||
proc = subprocess.run(['crontab', '-l'], capture_output=True, text=True, timeout=10)
|
||||
existing = proc.stdout if proc.returncode == 0 else ''
|
||||
|
||||
if 'erp_sync' in existing:
|
||||
print(f" [OK] cron任务已存在:")
|
||||
for line in existing.split('\n'):
|
||||
if 'erp_sync' in line:
|
||||
print(f" {line}")
|
||||
else:
|
||||
new_cron = existing.strip() + '\n' + cron_entry + '\n'
|
||||
proc2 = subprocess.run(['crontab'], input=new_cron, capture_output=True, text=True, timeout=10)
|
||||
if proc2.returncode == 0:
|
||||
print(f" [OK] cron任务已添加:")
|
||||
print(f" {cron_entry}")
|
||||
else:
|
||||
print(f" [WARN] crontab写入失败: {proc2.stderr}")
|
||||
print(f" [INFO] 请手动运行:")
|
||||
print(f" echo '{cron_entry}' | crontab -")
|
||||
except FileNotFoundError:
|
||||
print(f" [WARN] crontab命令不可用")
|
||||
print(f" [INFO] 请手动添加cron:")
|
||||
print(f" {cron_entry}")
|
||||
except Exception as e:
|
||||
print(f" [WARN] cron设置异常: {e}")
|
||||
print(f" [INFO] 请手动添加:")
|
||||
print(f" (crontab -l 2>/dev/null; echo '{cron_entry}') | crontab -")
|
||||
|
||||
print()
|
||||
print("=" * 50)
|
||||
print("所有子任务执行完毕")
|
||||
print("=" * 50)
|
||||
Reference in New Issue
Block a user