Files
cma-management/backend/app/api/data.py
T
Hermes CI Fix 3dddd36866 init: 管理会计OS初始代码
包含前后端完整代码:
- 前端:Vue3+Vite+ElementPlus
- 后端:FastAPI+SQLAlchemy
- 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动
- 当前版本:v1.0.0
2026-05-28 17:32:22 +08:00

101 lines
3.8 KiB
Python

"""数据对接 API"""
import pandas as pd
import io, json, hashlib
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import Optional
from app.database import get_db
from app.auth_middleware import require_auth, require_role
from app.models import KPIValue, DataSourceConfig, OperationLog
router = APIRouter(prefix="/api/cma/data", tags=["数据对接"],
dependencies=[Depends(require_role("ceo", "finance", "it"))],
)
@router.post("/import-excel")
async def import_excel(file: UploadFile = File(...), db: Session = Depends(get_db)):
content = await file.read()
df = pd.read_excel(io.BytesIO(content))
required = ["kpi_code", "period", "actual_value"]
if not all(c in df.columns for c in required):
raise HTTPException(400, f"Excel必须包含列: {required}")
batch = hashlib.md5(str(datetime.now().timestamp()).encode()).hexdigest()[:12]
count = 0
for _, row in df.iterrows():
kpi_code = str(row.get("kpi_code", ""))
period = str(row.get("period", ""))
value = row.get("actual_value")
if not kpi_code or not period or pd.isna(value):
continue
from app.models import KPIDefinition
kpi = db.query(KPIDefinition).filter(KPIDefinition.kpi_code == kpi_code).first()
if not kpi:
continue
kv = KPIValue(
kpi_id=kpi.id,
period=period,
actual_value=float(value),
source_type="excel",
source_batch=batch,
data_status="pending",
)
db.add(kv)
count += 1
db.commit()
return {"message": f"导入成功 {count} 条数据", "batch": batch}
@router.get("/sources")
def list_sources(db: Session = Depends(get_db)):
sources = db.query(DataSourceConfig).all()
return {"data": [{c.name: getattr(s, c.name) for c in DataSourceConfig.__table__.columns} for s in sources]}
@router.post("/sources")
def create_source(data: dict, db: Session = Depends(get_db)):
source = DataSourceConfig(
name=data.get("name", ""),
source_type=data.get("source_type", "manual"),
api_endpoint=data.get("api_endpoint"),
api_key=data.get("api_key"),
query_sql=data.get("query_sql"),
sync_type=data.get("sync_type", "manual"),
status="active",
)
db.add(source)
db.commit()
db.refresh(source)
# 操作日志
db.add(OperationLog(action="create_source", target_type="source", detail=source.name))
db.commit()
return {"data": {c.name: getattr(source, c.name) for c in DataSourceConfig.__table__.columns}}
@router.put("/sources/{source_id}")
def update_source(source_id: int, data: dict, db: Session = Depends(get_db)):
source = db.query(DataSourceConfig).filter(DataSourceConfig.id == source_id).first()
if not source:
raise HTTPException(404, "数据源不存在")
for key in ["name", "source_type", "api_endpoint", "api_key", "query_sql", "sync_type", "status"]:
if key in data:
setattr(source, key, data[key])
db.commit()
db.refresh(source)
db.add(OperationLog(action="update_source", target_type="source", detail=source.name))
db.commit()
return {"data": {c.name: getattr(source, c.name) for c in DataSourceConfig.__table__.columns}}
@router.delete("/sources/{source_id}")
def delete_source(source_id: int, db: Session = Depends(get_db)):
source = db.query(DataSourceConfig).filter(DataSourceConfig.id == source_id).first()
if not source:
raise HTTPException(404, "数据源不存在")
db.add(OperationLog(action="delete_source", target_type="source", detail=source.name))
db.delete(source)
db.commit()
return {"message": "删除成功"}