xiangqinxiaochengxu/backend/app/routers/matches.py
2026-04-17 10:49:14 +08:00

142 lines
5.3 KiB
Python

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}