Files
tp-ems/core/backend/app/api/v1/management.py

386 lines
14 KiB
Python
Raw Normal View History

from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from pydantic import BaseModel
from app.core.database import get_db
from app.core.deps import get_current_user, require_roles
from app.models.management import Regulation, Standard, ProcessDoc, EmergencyPlan
from app.models.user import User
router = APIRouter(prefix="/management", tags=["管理体系"])
# ── Pydantic Schemas ──────────────────────────────────────────────────
class RegulationCreate(BaseModel):
title: str
category: str | None = None
content: str | None = None
effective_date: datetime | None = None
status: str = "active"
attachment_url: str | None = None
class StandardCreate(BaseModel):
name: str
code: str | None = None
type: str | None = None
description: str | None = None
compliance_status: str = "pending"
review_date: datetime | None = None
class ProcessDocCreate(BaseModel):
title: str
category: str | None = None
content: str | None = None
version: str = "1.0"
approved_by: str | None = None
effective_date: datetime | None = None
class EmergencyPlanCreate(BaseModel):
title: str
scenario: str | None = None
steps: list[dict] | None = None
responsible_person: str | None = None
review_date: datetime | None = None
is_active: bool = True
# ── Regulations (规章制度) ────────────────────────────────────────────
@router.get("/regulations")
async def list_regulations(
category: str | None = None,
status: str | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
query = select(Regulation)
if category:
query = query.where(Regulation.category == category)
if status:
query = query.where(Regulation.status == status)
count_q = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_q)).scalar()
query = query.order_by(Regulation.id.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(query)
return {
"total": total,
"items": [_regulation_to_dict(r) for r in result.scalars().all()],
}
@router.post("/regulations")
async def create_regulation(
data: RegulationCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
reg = Regulation(**data.model_dump(), created_by=user.id)
db.add(reg)
await db.flush()
return _regulation_to_dict(reg)
@router.put("/regulations/{reg_id}")
async def update_regulation(
reg_id: int,
data: RegulationCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(Regulation).where(Regulation.id == reg_id))
reg = result.scalar_one_or_none()
if not reg:
raise HTTPException(status_code=404, detail="规章制度不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(reg, k, v)
return _regulation_to_dict(reg)
@router.delete("/regulations/{reg_id}")
async def delete_regulation(
reg_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(Regulation).where(Regulation.id == reg_id))
reg = result.scalar_one_or_none()
if not reg:
raise HTTPException(status_code=404, detail="规章制度不存在")
await db.delete(reg)
return {"message": "已删除"}
# ── Standards (标准规范) ──────────────────────────────────────────────
@router.get("/standards")
async def list_standards(
type: str | None = None,
compliance_status: str | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
query = select(Standard)
if type:
query = query.where(Standard.type == type)
if compliance_status:
query = query.where(Standard.compliance_status == compliance_status)
count_q = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_q)).scalar()
query = query.order_by(Standard.id.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(query)
return {
"total": total,
"items": [_standard_to_dict(s) for s in result.scalars().all()],
}
@router.post("/standards")
async def create_standard(
data: StandardCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
std = Standard(**data.model_dump())
db.add(std)
await db.flush()
return _standard_to_dict(std)
@router.put("/standards/{std_id}")
async def update_standard(
std_id: int,
data: StandardCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(Standard).where(Standard.id == std_id))
std = result.scalar_one_or_none()
if not std:
raise HTTPException(status_code=404, detail="标准规范不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(std, k, v)
return _standard_to_dict(std)
@router.delete("/standards/{std_id}")
async def delete_standard(
std_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(Standard).where(Standard.id == std_id))
std = result.scalar_one_or_none()
if not std:
raise HTTPException(status_code=404, detail="标准规范不存在")
await db.delete(std)
return {"message": "已删除"}
# ── Process Docs (管理流程) ───────────────────────────────────────────
@router.get("/process-docs")
async def list_process_docs(
category: str | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
query = select(ProcessDoc)
if category:
query = query.where(ProcessDoc.category == category)
count_q = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_q)).scalar()
query = query.order_by(ProcessDoc.id.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(query)
return {
"total": total,
"items": [_process_doc_to_dict(d) for d in result.scalars().all()],
}
@router.post("/process-docs")
async def create_process_doc(
data: ProcessDocCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
doc = ProcessDoc(**data.model_dump())
db.add(doc)
await db.flush()
return _process_doc_to_dict(doc)
@router.put("/process-docs/{doc_id}")
async def update_process_doc(
doc_id: int,
data: ProcessDocCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(ProcessDoc).where(ProcessDoc.id == doc_id))
doc = result.scalar_one_or_none()
if not doc:
raise HTTPException(status_code=404, detail="管理流程文档不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(doc, k, v)
return _process_doc_to_dict(doc)
@router.delete("/process-docs/{doc_id}")
async def delete_process_doc(
doc_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(ProcessDoc).where(ProcessDoc.id == doc_id))
doc = result.scalar_one_or_none()
if not doc:
raise HTTPException(status_code=404, detail="管理流程文档不存在")
await db.delete(doc)
return {"message": "已删除"}
# ── Emergency Plans (应急预案) ────────────────────────────────────────
@router.get("/emergency-plans")
async def list_emergency_plans(
scenario: str | None = None,
is_active: bool | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
query = select(EmergencyPlan)
if scenario:
query = query.where(EmergencyPlan.scenario == scenario)
if is_active is not None:
query = query.where(EmergencyPlan.is_active == is_active)
count_q = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_q)).scalar()
query = query.order_by(EmergencyPlan.id.desc()).offset((page - 1) * page_size).limit(page_size)
result = await db.execute(query)
return {
"total": total,
"items": [_emergency_plan_to_dict(p) for p in result.scalars().all()],
}
@router.post("/emergency-plans")
async def create_emergency_plan(
data: EmergencyPlanCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
plan = EmergencyPlan(**data.model_dump())
db.add(plan)
await db.flush()
return _emergency_plan_to_dict(plan)
@router.put("/emergency-plans/{plan_id}")
async def update_emergency_plan(
plan_id: int,
data: EmergencyPlanCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(EmergencyPlan).where(EmergencyPlan.id == plan_id))
plan = result.scalar_one_or_none()
if not plan:
raise HTTPException(status_code=404, detail="应急预案不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(plan, k, v)
return _emergency_plan_to_dict(plan)
@router.delete("/emergency-plans/{plan_id}")
async def delete_emergency_plan(
plan_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_roles("admin", "energy_manager")),
):
result = await db.execute(select(EmergencyPlan).where(EmergencyPlan.id == plan_id))
plan = result.scalar_one_or_none()
if not plan:
raise HTTPException(status_code=404, detail="应急预案不存在")
await db.delete(plan)
return {"message": "已删除"}
# ── Compliance Overview ───────────────────────────────────────────────
@router.get("/compliance")
async def compliance_overview(
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
"""合规概览 - count by compliance_status for standards"""
result = await db.execute(
select(Standard.compliance_status, func.count(Standard.id))
.group_by(Standard.compliance_status)
)
stats = {row[0]: row[1] for row in result.all()}
return {
"compliant": stats.get("compliant", 0),
"non_compliant": stats.get("non_compliant", 0),
"pending": stats.get("pending", 0),
"in_progress": stats.get("in_progress", 0),
"total": sum(stats.values()),
}
# ── Serializers ───────────────────────────────────────────────────────
def _regulation_to_dict(r: Regulation) -> dict:
return {
"id": r.id, "title": r.title, "category": r.category,
"content": r.content, "effective_date": str(r.effective_date) if r.effective_date else None,
"status": r.status, "attachment_url": r.attachment_url,
"created_by": r.created_by,
"created_at": str(r.created_at) if r.created_at else None,
"updated_at": str(r.updated_at) if r.updated_at else None,
}
def _standard_to_dict(s: Standard) -> dict:
return {
"id": s.id, "name": s.name, "code": s.code, "type": s.type,
"description": s.description, "compliance_status": s.compliance_status,
"review_date": str(s.review_date) if s.review_date else None,
"created_at": str(s.created_at) if s.created_at else None,
"updated_at": str(s.updated_at) if s.updated_at else None,
}
def _process_doc_to_dict(d: ProcessDoc) -> dict:
return {
"id": d.id, "title": d.title, "category": d.category,
"content": d.content, "version": d.version,
"approved_by": d.approved_by,
"effective_date": str(d.effective_date) if d.effective_date else None,
"created_at": str(d.created_at) if d.created_at else None,
"updated_at": str(d.updated_at) if d.updated_at else None,
}
def _emergency_plan_to_dict(p: EmergencyPlan) -> dict:
return {
"id": p.id, "title": p.title, "scenario": p.scenario,
"steps": p.steps, "responsible_person": p.responsible_person,
"review_date": str(p.review_date) if p.review_date else None,
"is_active": p.is_active,
"created_at": str(p.created_at) if p.created_at else None,
"updated_at": str(p.updated_at) if p.updated_at else None,
}