193 lines
7.4 KiB
Python
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)
|