feat: Freelancer Match — полная продакшн версия с AI-матчингом и escrow
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Endpoints для заявок фрилансеров."""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, desc
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.security import get_current_user
|
||||
from app.schemas.proposal import ProposalCreate, ProposalResponse
|
||||
from app.models.project import Project
|
||||
from app.models.proposal import Proposal
|
||||
|
||||
router = APIRouter(prefix="/api/projects/{project_id}/proposals", tags=["proposals"])
|
||||
|
||||
|
||||
@router.post("", response_model=ProposalResponse)
|
||||
async def create_proposal(
|
||||
project_id: str, data: ProposalCreate, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Фрилансер подаёт заявку на проект."""
|
||||
|
||||
result = await db.execute(select(Project).where(Project.id == project_id))
|
||||
project = result.scalar_one_or_none()
|
||||
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Проект не найден")
|
||||
|
||||
proposal = Proposal(
|
||||
project_id=project_id,
|
||||
freelancer_id=user["id"],
|
||||
cover_letter=data.cover_letter,
|
||||
proposed_price=data.proposed_price,
|
||||
estimated_days=data.estimated_days,
|
||||
)
|
||||
db.add(proposal)
|
||||
await db.commit()
|
||||
await db.refresh(proposal)
|
||||
|
||||
return ProposalResponse(
|
||||
id=str(proposal.id), project_id=project_id, freelancer_id=user["id"],
|
||||
cover_letter=proposal.cover_letter, proposed_price=float(proposal.proposed_price) if proposal.proposed_price else None,
|
||||
estimated_days=proposal.estimated_days, status=proposal.status, created_at=str(proposal.created_at),
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=list[ProposalResponse])
|
||||
async def list_proposals(project_id: str, db: AsyncSession = Depends(get_db)):
|
||||
"""Список заявок на проект."""
|
||||
|
||||
result = await db.execute(select(Proposal).where(Proposal.project_id == project_id).order_by(desc(Proposal.created_at)))
|
||||
proposals = result.scalars().all()
|
||||
|
||||
return [ProposalResponse(
|
||||
id=str(p.id), project_id=p.project_id, freelancer_id=p.freelancer_id, cover_letter=p.cover_letter,
|
||||
proposed_price=float(p.proposed_price) if p.proposed_price else None, estimated_days=p.estimated_days,
|
||||
status=p.status, created_at=str(p.created_at),
|
||||
) for p in proposals]
|
||||
|
||||
|
||||
@router.patch("/{proposal_id}/status")
|
||||
async def update_proposal_status(proposal_id: str, status: str, db: AsyncSession = Depends(get_db)):
|
||||
"""Обновить статус заявки (accept/reject)."""
|
||||
|
||||
result = await db.execute(select(Proposal).where(Proposal.id == proposal_id))
|
||||
proposal = result.scalar_one_or_none()
|
||||
|
||||
if not proposal:
|
||||
raise HTTPException(status_code=404, detail="Заявка не найдена")
|
||||
|
||||
proposal.status = status
|
||||
await db.commit()
|
||||
|
||||
return {"status": "updated", "proposal_id": str(proposal.id)}
|
||||
Reference in New Issue
Block a user