from __future__ import annotations from enum import Enum from typing import Literal from fastapi import Depends, HTTPException, Query from sqlalchemy import and_, select from sqlalchemy.ext.asyncio import AsyncSession from ...core.response import success_response from ...dependencies.permissions import require_manager_user from ...models import User, UserProject from ...schemas.feedback_center import FeedbackCenterReviewRequest from ...services.auth import parse_role_values from ...services.feedback_center import FeedbackCenterService, list_reason_catalog from .shared import * MANAGER_ROLES = {"ADMIN", "BOSS"} def _is_manager_user(current_user: User) -> bool: role_value = getattr(current_user, "role", "") if isinstance(role_value, Enum): role_value = role_value.value roles = set(parse_role_values(str(role_value or ""))) return bool(roles.intersection(MANAGER_ROLES)) async def _ensure_summary_scope( *, current_user: User, db: AsyncSession, project_id: int | None, ) -> None: if _is_manager_user(current_user): return if project_id is None: raise HTTPException(status_code=403, detail="Manager permission required for global feedback center") membership = await db.execute( select(UserProject.id).where( and_( UserProject.tenant_id == int(current_user.tenant_id), UserProject.project_id == int(project_id), UserProject.user_id == int(current_user.id), ) ) ) if membership.scalar_one_or_none() is None: raise HTTPException(status_code=403, detail="Project membership required") @router.get("/feedback-center/summary") async def get_feedback_center_summary( project_id: int | None = Query(default=None), window_days: int | None = Query(default=None), source_group: str | None = Query(default=None), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) await _ensure_summary_scope(current_user=current_user, db=db, project_id=project_id) try: data = await FeedbackCenterService(db).summary( tenant_id=effective_tenant_id, project_id=project_id, window_days=window_days, source_group=source_group, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return success_response(data) @router.get("/feedback-center/items") async def list_feedback_center_items( project_id: int | None = Query(default=None), source_type: str | None = Query(default=None), review_status: str | None = Query(default=None), field_name: str | None = Query(default=None), reason_code: str | None = Query(default=None), search: str | None = Query(default=None), limit: int = Query(default=20, ge=1, le=100), offset: int = Query(default=0, ge=0), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) data = await FeedbackCenterService(db).list_records( tenant_id=effective_tenant_id, project_id=project_id, source_type=source_type, review_status=review_status, field_name=field_name, reason_code=reason_code, search=search, limit=limit, offset=offset, ) return success_response(data) @router.get("/feedback-center/items/{record_id}") async def get_feedback_center_item_detail( record_id: int, project_id: int | None = Query(default=None), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) data = await FeedbackCenterService(db).get_detail( tenant_id=effective_tenant_id, record_id=record_id, project_id=project_id, ) if data is None: raise HTTPException(status_code=404, detail="feedback record not found") return success_response(data) @router.post("/feedback-center/items/{record_id}/review") async def review_feedback_center_item( record_id: int, request: FeedbackCenterReviewRequest, current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): effective_tenant_id = int(getattr(current_user, "tenant_id", tenant_id) or tenant_id) service = FeedbackCenterService(db) reviewed = await service.review( tenant_id=effective_tenant_id, record_id=record_id, reviewer_id=int(current_user.id), review_status=request.review_status, reason_code=request.reason_code, reason_text=request.reason_text, ) return success_response(reviewed) @router.get("/feedback-center/reason-catalog") async def get_feedback_center_reason_catalog( source_type: str | None = Query(default=None), field_name: str | None = Query(default=None), current_user: User = Depends(require_manager_user), db: AsyncSession = Depends(get_db), tenant_id: int = Depends(get_tenant_id), ): _ = db, tenant_id, current_user return success_response( { "items": list_reason_catalog(source_type=source_type, field_name=field_name), } )