JhHardwareWRS_BackPoint/app/routers/monitor.py
2026-06-24 15:19:14 +08:00

193 lines
7.4 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select
from sqlalchemy.orm import Session, selectinload
from app.database import get_db
from app.deps import require_roles
from app.models import MoldLockFeedback, Personnel, Role, SessionStatus, WorkSession, WorkSessionDevice
from app.schemas import MoldLockFeedbackCreate, MoldLockFeedbackOut, MonitorLockOut, PageResponse
from app.services.attendance_points import accessible_point_names
from app.services.common import paginate
from app.services.mold_locks import (
FEEDBACK_STATUS_PENDING,
active_locks_for_points,
current_unreleased_device,
feedback_out,
lock_out,
release_lock,
)
from app.timezone import now
router = APIRouter(prefix="/api/monitor", tags=["monitor"])
def _lock_query():
return (
select(WorkSessionDevice)
.options(
selectinload(WorkSessionDevice.session).selectinload(WorkSession.devices),
selectinload(WorkSessionDevice.session).selectinload(WorkSession.employee),
)
)
def _feedback_query():
return select(MoldLockFeedback).options(
selectinload(MoldLockFeedback.reporter),
selectinload(MoldLockFeedback.occupied_person),
selectinload(MoldLockFeedback.handler),
selectinload(MoldLockFeedback.session_device),
)
def _admin_point_names(db: Session, user: Personnel) -> list[str]:
return accessible_point_names(db, user)
def _get_admin_lock(db: Session, user: Personnel, lock_id: int) -> WorkSessionDevice:
lock = db.scalar(_lock_query().where(WorkSessionDevice.id == lock_id))
if lock is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="占用记录不存在")
if lock.attendance_point_name not in _admin_point_names(db, user):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无该考勤点监控权限")
return lock
def _get_admin_feedback(db: Session, user: Personnel, feedback_id: int) -> MoldLockFeedback:
feedback = db.scalar(_feedback_query().where(MoldLockFeedback.id == feedback_id))
if feedback is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="反馈不存在")
if feedback.attendance_point_name not in _admin_point_names(db, user):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无该考勤点监控权限")
return feedback
def _is_active_current_lock(lock: WorkSessionDevice) -> bool:
if (
lock.session is None
or lock.session.status not in [SessionStatus.active, SessionStatus.reporting]
or lock.released_at is not None
):
return False
current = current_unreleased_device(lock.session)
return bool(current and current.id == lock.id)
@router.get("/locks", response_model=list[MonitorLockOut])
def list_active_locks(
user: Personnel = Depends(require_roles(Role.admin)),
db: Session = Depends(get_db),
) -> list[MonitorLockOut]:
return [lock_out(db, lock) for lock in active_locks_for_points(db, _admin_point_names(db, user))]
@router.post("/locks/{lock_id}/release")
def force_release_lock(
lock_id: int,
user: Personnel = Depends(require_roles(Role.admin)),
db: Session = Depends(get_db),
) -> dict[str, bool]:
_get_admin_lock(db, user, lock_id)
try:
release_lock(db, lock_id, user.phone, "管理员强制解除占用")
except ValueError as exc:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
db.commit()
return {"ok": True}
@router.get("/feedbacks", response_model=PageResponse)
def list_feedbacks(
box: str = Query("unread", pattern="^(unread|read)$"),
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=10),
user: Personnel = Depends(require_roles(Role.admin)),
db: Session = Depends(get_db),
) -> PageResponse:
point_names = _admin_point_names(db, user)
query = _feedback_query().where(MoldLockFeedback.attendance_point_name.in_(point_names))
if box == "read":
query = query.where(MoldLockFeedback.read_at.is_not(None)).order_by(
MoldLockFeedback.read_at.desc(),
MoldLockFeedback.created_at.desc(),
)
else:
query = query.where(MoldLockFeedback.read_at.is_(None)).order_by(MoldLockFeedback.created_at.desc())
result = paginate(db, query, page, page_size)
return PageResponse(**{**result, "rows": [feedback_out(db, row) for row in result["rows"]]})
@router.post("/feedbacks", response_model=MoldLockFeedbackOut)
def create_feedback(
payload: MoldLockFeedbackCreate,
user: Personnel = Depends(require_roles(Role.worker)),
db: Session = Depends(get_db),
) -> MoldLockFeedbackOut:
lock = db.scalar(_lock_query().where(WorkSessionDevice.id == payload.lock_id))
if lock is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="占用记录不存在")
if lock.attendance_point_name not in accessible_point_names(db, user):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无该考勤点反馈权限")
if not _is_active_current_lock(lock):
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="该模具占用已解除")
if lock.session.employee_phone == user.phone:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不能反馈自己的占用")
existing = db.scalar(
_feedback_query().where(
MoldLockFeedback.session_device_id == lock.id,
MoldLockFeedback.reporter_phone == user.phone,
MoldLockFeedback.status == FEEDBACK_STATUS_PENDING,
)
)
if existing is not None:
return feedback_out(db, existing)
feedback = MoldLockFeedback(
attendance_point_name=lock.attendance_point_name,
mold_name=lock.device_no,
process_name=lock.process_name,
session_device_id=lock.id,
reporter_phone=user.phone,
occupied_phone=lock.session.employee_phone,
status=FEEDBACK_STATUS_PENDING,
)
db.add(feedback)
db.commit()
feedback = db.scalar(_feedback_query().where(MoldLockFeedback.id == feedback.id))
if feedback is None:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="反馈保存失败")
return feedback_out(db, feedback)
@router.post("/feedbacks/{feedback_id}/read", response_model=MoldLockFeedbackOut)
def mark_feedback_read(
feedback_id: int,
user: Personnel = Depends(require_roles(Role.admin)),
db: Session = Depends(get_db),
) -> MoldLockFeedbackOut:
feedback = _get_admin_feedback(db, user, feedback_id)
if feedback.read_at is None:
feedback.read_at = now()
db.commit()
feedback = _get_admin_feedback(db, user, feedback_id)
return feedback_out(db, feedback)
@router.post("/feedbacks/{feedback_id}/release", response_model=MoldLockFeedbackOut)
def release_feedback_lock(
feedback_id: int,
user: Personnel = Depends(require_roles(Role.admin)),
db: Session = Depends(get_db),
) -> MoldLockFeedbackOut:
feedback = _get_admin_feedback(db, user, feedback_id)
if feedback.read_at is None:
feedback.read_at = now()
try:
release_lock(db, feedback.session_device_id, user.phone, "管理员通过反馈解除占用")
except ValueError as exc:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
db.commit()
feedback = _get_admin_feedback(db, user, feedback_id)
return feedback_out(db, feedback)