init: 管理会计OS初始代码
包含前后端完整代码: - 前端:Vue3+Vite+ElementPlus - 后端:FastAPI+SQLAlchemy - 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动 - 当前版本:v1.0.0
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"""改善行动计划 API — 管理会计OS"""
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.auth_middleware import require_role, require_auth
|
||||
from app.models import ActionPlan, KPIAlert, KPIDefinition, User
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("cma.action_plans")
|
||||
|
||||
router = APIRouter(prefix="/api/cma/action-plans", tags=["改善行动"],
|
||||
dependencies=[Depends(require_role("ceo", "finance", "business", "it"))],
|
||||
)
|
||||
|
||||
|
||||
def plan_to_dict(p: ActionPlan) -> dict:
|
||||
return {
|
||||
"id": p.id,
|
||||
"alert_id": p.alert_id,
|
||||
"kpi_id": p.kpi_id,
|
||||
"title": p.title,
|
||||
"description": p.description,
|
||||
"assignee": p.assignee,
|
||||
"priority": p.priority,
|
||||
"due_date": p.due_date.isoformat() if p.due_date else None,
|
||||
"status": p.status,
|
||||
"progress": p.progress or 0,
|
||||
"result": p.result,
|
||||
"created_by": p.created_by,
|
||||
"created_at": p.created_at.isoformat() if p.created_at else None,
|
||||
"updated_at": p.updated_at.isoformat() if p.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_plans(
|
||||
status: Optional[str] = None,
|
||||
kpi_id: Optional[int] = None,
|
||||
alert_id: Optional[int] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_auth),
|
||||
):
|
||||
"""获取行动计划列表"""
|
||||
query = db.query(ActionPlan).order_by(ActionPlan.created_at.desc())
|
||||
|
||||
if status:
|
||||
query = query.filter(ActionPlan.status == status)
|
||||
if kpi_id:
|
||||
query = query.filter(ActionPlan.kpi_id == kpi_id)
|
||||
if alert_id:
|
||||
query = query.filter(ActionPlan.alert_id == alert_id)
|
||||
|
||||
# business角色只看自己的
|
||||
if current_user.role == "business":
|
||||
query = query.filter(
|
||||
(ActionPlan.assignee == current_user.username) |
|
||||
(ActionPlan.assignee == current_user.name)
|
||||
)
|
||||
|
||||
plans = query.all()
|
||||
result = []
|
||||
for p in plans:
|
||||
item = plan_to_dict(p)
|
||||
# 附带KPI名称
|
||||
kpi = db.query(KPIDefinition).filter(KPIDefinition.id == p.kpi_id).first()
|
||||
item["kpi_name"] = kpi.kpi_name if kpi else "未知KPI"
|
||||
result.append(item)
|
||||
|
||||
return {"data": result}
|
||||
|
||||
|
||||
@router.post("")
|
||||
def create_plan(
|
||||
data: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_auth),
|
||||
):
|
||||
"""创建改善行动计划"""
|
||||
required = ["title", "kpi_id"]
|
||||
for field in required:
|
||||
if field not in data:
|
||||
raise HTTPException(400, f"缺少必填字段: {field}")
|
||||
|
||||
plan = ActionPlan(
|
||||
alert_id=data.get("alert_id"),
|
||||
kpi_id=data["kpi_id"],
|
||||
title=data["title"],
|
||||
description=data.get("description"),
|
||||
assignee=data.get("assignee"),
|
||||
priority=data.get("priority", "medium"),
|
||||
due_date=datetime.fromisoformat(data["due_date"]) if data.get("due_date") else None,
|
||||
status="pending",
|
||||
progress=0,
|
||||
created_by=current_user.name or current_user.username,
|
||||
)
|
||||
db.add(plan)
|
||||
db.commit()
|
||||
db.refresh(plan)
|
||||
return plan_to_dict(plan)
|
||||
|
||||
|
||||
@router.put("/{plan_id}")
|
||||
def update_plan(
|
||||
plan_id: int,
|
||||
data: dict,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""更新行动计划"""
|
||||
plan = db.query(ActionPlan).filter(ActionPlan.id == plan_id).first()
|
||||
if not plan:
|
||||
raise HTTPException(404, "计划不存在")
|
||||
|
||||
if "title" in data:
|
||||
plan.title = data["title"]
|
||||
if "description" in data:
|
||||
plan.description = data["description"]
|
||||
if "assignee" in data:
|
||||
plan.assignee = data["assignee"]
|
||||
if "priority" in data:
|
||||
plan.priority = data["priority"]
|
||||
if "due_date" in data:
|
||||
plan.due_date = datetime.fromisoformat(data["due_date"]) if data["due_date"] else None
|
||||
if "status" in data:
|
||||
plan.status = data["status"]
|
||||
if "progress" in data:
|
||||
plan.progress = max(0, min(100, data["progress"]))
|
||||
if "result" in data:
|
||||
plan.result = data["result"]
|
||||
|
||||
db.commit()
|
||||
db.refresh(plan)
|
||||
return plan_to_dict(plan)
|
||||
|
||||
|
||||
@router.delete("/{plan_id}")
|
||||
def delete_plan(plan_id: int, db: Session = Depends(get_db)):
|
||||
"""删除行动计划"""
|
||||
plan = db.query(ActionPlan).filter(ActionPlan.id == plan_id).first()
|
||||
if not plan:
|
||||
raise HTTPException(404, "计划不存在")
|
||||
db.delete(plan)
|
||||
db.commit()
|
||||
return {"message": "已删除"}
|
||||
Reference in New Issue
Block a user