"""HTTP surface for 服务台分析 (Feishu helpdesk analytics + data). All reads gate on ``global_admin`` + ``management`` (leadership read). The manual re-sync mutation is ``global_admin`` only. This is an external IT-operations report — distinct from the internal ``support`` workbench. """ from __future__ import annotations import uuid from datetime import datetime from fastapi import APIRouter, Depends, Query, Request from sqlalchemy.ext.asyncio import AsyncSession from app.auth.models import AuthUser from app.core.database import get_db from app.core.dependencies import require_admin, require_roles from app.core.exporting import xlsx_export_response from app.core.rbac import GLOBAL_ADMIN_ROLE, MANAGEMENT_ROLE from app.core.response import paginated_response, success_response from app.domains.feishu_helpdesk import ai_insights, service from app.domains.feishu_helpdesk.sync import sync_all_helpdesks router = APIRouter(prefix="/api/v1/feishu-helpdesk", tags=["feishu-helpdesk"]) _read_gate = require_roles(GLOBAL_ADMIN_ROLE, MANAGEMENT_ROLE) _EXPORT_COLUMNS = [ ("工单ID", "ticket_id"), ("服务台", "helpdesk_name"), ("状态", "status"), ("渠道", "channel"), ("类型", "ticket_type"), ("是否解决", "solve"), ("满意度", "score"), ("语言", "language"), ("提单人", "guest_name"), ("提单人邮箱", "guest_email"), ("客服", "agent_owner_name"), ("客服组", "agent_group"), ("首响秒数", "first_response_seconds"), ("解决秒数", "resolution_seconds"), ("客服处理分钟", "agent_service_minutes"), ("创建时间", "created_at"), ("更新时间", "updated_at"), ("关闭时间", "closed_at"), ] @router.get("/helpdesks") async def helpdesks( db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(_read_gate), ) -> dict: return success_response(data=await service.list_helpdesks(db)) @router.get("/analytics") async def analytics( integration_id: uuid.UUID | None = Query(None), start: datetime | None = Query(None), end: datetime | None = Query(None), days: int | None = Query(None, ge=1, le=366), db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(_read_gate), ) -> dict: data = await service.get_analytics( db, integration_id=integration_id, start=start, end=end, days=days, ) return success_response(data=data) @router.get("/ai-insights") async def ai_insights_get( integration_id: uuid.UUID | None = Query(None), db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(_read_gate), ) -> dict: """AI monthly operational insights (cached; generated by 百炼/Qwen).""" return success_response(data=await ai_insights.get_insights(db, integration_id)) @router.post("/ai-insights/refresh") async def ai_insights_refresh( integration_id: uuid.UUID | None = Query(None), db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(require_admin()), ) -> dict: """Force-regenerate the AI insights (global_admin).""" return success_response( data=await ai_insights.get_insights(db, integration_id, force=True), ) @router.get("/tickets") async def tickets( request: Request, integration_id: uuid.UUID | None = Query(None), status: int | None = Query(None), search: str | None = Query(None), start: datetime | None = Query(None), end: datetime | None = Query(None), days: int | None = Query(None, ge=1, le=366), page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=200), db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(_read_gate), ) -> dict: rows, total = await service.list_tickets( db, integration_id=integration_id, status=status, search=search, start=start, end=end, days=days, page=page, page_size=page_size, ) return paginated_response(rows, total, page, page_size, request) @router.get("/tickets/export") async def tickets_export( request: Request, integration_id: uuid.UUID | None = Query(None), status: int | None = Query(None), search: str | None = Query(None), start: datetime | None = Query(None), end: datetime | None = Query(None), days: int | None = Query(None, ge=1, le=366), db: AsyncSession = Depends(get_db), current_user: AuthUser = Depends(_read_gate), ): rows, total = await service.all_tickets_for_export( db, integration_id=integration_id, status=status, search=search, start=start, end=end, days=days, ) return await xlsx_export_response( db, request, current_user, columns=_EXPORT_COLUMNS, rows=rows, total_matched=total, filename_stem="feishu_helpdesk_tickets", sheet_title="服务台工单", audit_action="feishu_helpdesk.export", audit_resource_type="feishu_helpdesk_ticket", filters={ "integration_id": str(integration_id) if integration_id else None, "status": status, "search": search, }, ) @router.post("/sync") async def manual_sync( db: AsyncSession = Depends(get_db), _user: AuthUser = Depends(require_admin()), ) -> dict: """Trigger an immediate incremental sync (global_admin only).""" summary = await sync_all_helpdesks(db) return success_response(data=summary)