from datetime import datetime, timedelta from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, select from sqlalchemy.orm import Session from app.db.session import get_db from app.models.operations import Equipment, MaintenanceOrder, WorkCenter from app.schemas.operations import ( EquipmentCreate, EquipmentRead, MaintenanceOrderCreate, MaintenanceOrderRead, ) from app.services.auth import require_authenticated_user from app.services.operations import get_equipment_query, get_maintenance_orders_query, to_decimal from app.services.system_permissions import ensure_employee_has_permission router = APIRouter(dependencies=[Depends(require_authenticated_user)]) def _code_token(value: str | None, fallback: str, max_length: int = 12) -> str: token = "".join(ch for ch in str(value or "").upper() if ch.isalnum()) return (token or fallback)[-max_length:] def _next_doc_no(db: Session, model, column_name: str, prefix: str) -> str: column = getattr(model, column_name) count = db.scalar(select(func.count(model.id)).where(column.like(f"{prefix}-%"))) or 0 return f"{prefix}-{count + 1:03d}" def build_equipment_code(db: Session, work_center: WorkCenter, equipment_name: str | None) -> str: date_part = datetime.now().strftime("%Y%m%d") center_part = _code_token(work_center.center_code or work_center.center_name, f"WC{work_center.id:04d}", 10) equipment_part = _code_token(equipment_name, "EQ", 10) return _next_doc_no(db, Equipment, "equipment_code", f"EQ-NB-{date_part}-{center_part}-{equipment_part}") def build_maintenance_order_no(db: Session, equipment: Equipment, maintenance_type: str | None) -> str: date_part = datetime.now().strftime("%Y%m%d") equipment_part = _code_token(equipment.equipment_code or equipment.equipment_name, f"EQ{equipment.id:04d}", 14) type_part = _code_token(maintenance_type, "MTN", 8) return _next_doc_no(db, MaintenanceOrder, "order_no", f"MTN-NB-{date_part}-{equipment_part}-{type_part}") @router.get("/assets", response_model=list[EquipmentRead]) def list_equipment_assets( limit: int = Query(default=100, ge=1, le=500), db: Session = Depends(get_db), ) -> list[EquipmentRead]: rows = db.execute(get_equipment_query(limit=limit)).mappings().all() return [EquipmentRead.model_validate(dict(row)) for row in rows] @router.post("/assets", response_model=EquipmentRead) def create_equipment_asset(payload: EquipmentCreate, db: Session = Depends(get_db)) -> EquipmentRead: work_center = db.get(WorkCenter, payload.work_center_id) if not work_center: raise HTTPException(status_code=404, detail="工作中心不存在") equipment = Equipment( equipment_code=build_equipment_code(db, work_center, payload.equipment_name), equipment_name=payload.equipment_name, category_name=payload.category_name, work_center_id=payload.work_center_id, manufacturer=payload.manufacturer, model_no=payload.model_no, install_date=payload.install_date, rated_power_kw=to_decimal(payload.rated_power_kw, "0.01"), occupied_area_sqm=to_decimal(payload.occupied_area_sqm, "0.01"), maintenance_cycle_hours=to_decimal(payload.maintenance_cycle_hours, "0.01"), last_maintenance_at=None, next_maintenance_at=payload.next_maintenance_at, status=payload.status, remark=payload.remark, ) db.add(equipment) db.commit() row = db.execute(get_equipment_query(limit=1).where(Equipment.id == equipment.id)).mappings().first() if not row: raise HTTPException(status_code=500, detail="设备创建后读取失败") return EquipmentRead.model_validate(dict(row)) @router.get("/maintenance-orders", response_model=list[MaintenanceOrderRead]) def list_maintenance_orders( limit: int = Query(default=100, ge=1, le=500), db: Session = Depends(get_db), ) -> list[MaintenanceOrderRead]: rows = db.execute(get_maintenance_orders_query(limit=limit)).mappings().all() return [MaintenanceOrderRead.model_validate(dict(row)) for row in rows] @router.post("/maintenance-orders", response_model=MaintenanceOrderRead) def create_maintenance_order(payload: MaintenanceOrderCreate, db: Session = Depends(get_db)) -> MaintenanceOrderRead: equipment = db.get(Equipment, payload.equipment_id) if not equipment: raise HTTPException(status_code=404, detail="设备不存在") ensure_employee_has_permission( db, payload.owner_employee_id, "MENU_EQUIPMENT_MANAGEMENT", "负责人", required=False, ) order = MaintenanceOrder( order_no=build_maintenance_order_no(db, equipment, payload.maintenance_type), equipment_id=payload.equipment_id, plan_id=None, maintenance_type=payload.maintenance_type, start_time=payload.start_time or datetime.now(), end_time=payload.end_time, downtime_minutes=to_decimal(payload.downtime_minutes, "0.01"), owner_employee_id=payload.owner_employee_id, result_summary=payload.result_summary, cost_amount=to_decimal(payload.cost_amount, "0.01"), status=payload.status, ) db.add(order) if payload.status == "COMPLETED" or payload.end_time: finished_time = payload.end_time or datetime.now() equipment.last_maintenance_at = finished_time if to_decimal(equipment.maintenance_cycle_hours, "0.01") > 0: equipment.next_maintenance_at = finished_time + timedelta(hours=float(equipment.maintenance_cycle_hours)) db.add(equipment) db.commit() row = db.execute(get_maintenance_orders_query(limit=1).where(MaintenanceOrder.id == order.id)).mappings().first() if not row: raise HTTPException(status_code=500, detail="保养工单创建后读取失败") return MaintenanceOrderRead.model_validate(dict(row))