from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, Request, status from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.dependencies import get_current_user_id from app.schemas.match import ( MatchCandidateItem, MatchDetailResponse, MatchLikeRequest, MatchLikeResponse, MyMatchItem, ) from app.services.match_service import ( ai_suggest_candidates, get_candidates, get_match_detail, get_match_users, get_public_user_info, like_user, list_my_matches, ) from app.services.notify_service import send_match_success from app.services.user_service import get_user_by_id from app.utils.rate_limit import enforce_rate_limit router = APIRouter() @router.get("/candidates") async def candidates( source: str = Query(default="manual"), current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: current_user = await get_user_by_id(session, current_user_id) if current_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") items = await get_candidates(session, current_user, source) data = [MatchCandidateItem(**item).model_dump() for item in items] return {"code": 0, "message": "ok", "data": data} @router.post("/ai-suggest") async def ai_suggest( current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: current_user = await get_user_by_id(session, current_user_id) if current_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") items = await ai_suggest_candidates(session, current_user) data = [MatchCandidateItem(**item).model_dump() for item in items] return {"code": 0, "message": "ok", "data": data} @router.post("/like") async def like( payload: MatchLikeRequest, background_tasks: BackgroundTasks, request: Request, current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: await enforce_rate_limit( key=f"rate:match_like:{current_user_id}", limit=20, window_seconds=60, message="操作过于频繁,请稍后再试", ) current_user = await get_user_by_id(session, current_user_id) to_user = await get_user_by_id(session, payload.to_user_id) if current_user is None or to_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") try: result = await like_user(session, current_user, to_user, payload.source, payload.activity_id) except ValueError as exc: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc if result.get("is_mutual") and result.get("match_id"): detail = await get_match_detail(session, current_user, result["match_id"]) if detail: match_user = await get_user_by_id(session, payload.to_user_id) if current_user.subscribe_match == 1 and current_user.openid and match_user: background_tasks.add_task(send_match_success, current_user.openid, match_user.nickname or "新匹配对象") if match_user and match_user.subscribe_match == 1 and match_user.openid: background_tasks.add_task(send_match_success, match_user.openid, current_user.nickname or "新匹配对象") return {"code": 0, "message": "ok", "data": MatchLikeResponse(**result).model_dump()} @router.get("/my") async def my_matches( current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: current_user = await get_user_by_id(session, current_user_id) if current_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") items = await list_my_matches(session, current_user) data = [MyMatchItem(**item).model_dump() for item in items] return {"code": 0, "message": "ok", "data": data} @router.get("/{match_id}/detail") async def match_detail( match_id: int, current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: current_user = await get_user_by_id(session, current_user_id) if current_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") try: detail = await get_match_detail(session, current_user, match_id) except PermissionError as exc: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(exc)) from exc if detail is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Match not found") return {"code": 0, "message": "ok", "data": MatchDetailResponse(**detail).model_dump()} @router.get("/public/{user_id}") async def public_user( user_id: int, current_user_id: int = Depends(get_current_user_id), session: AsyncSession = Depends(get_db), ) -> dict: current_user = await get_user_by_id(session, current_user_id) target_user = await get_user_by_id(session, user_id) if current_user is None or target_user is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") data = await get_public_user_info(session, current_user, target_user) return {"code": 0, "message": "ok", "data": data}