142 lines
5.3 KiB
Python
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}
|