158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
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),
|
|
}
|
|
)
|