feat: Freelancer Match — AI-матчинг, escrow, milestones, portfolio, skill-tests, verification

This commit is contained in:
2026-07-03 15:03:30 +00:00
commit 0b785db1b3
61 changed files with 2725 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
"""Pydantic схемы для валидации запросов/ответов."""
from app.schemas.user import UserCreate, UserLogin, UserProfileUpdate, FreelancerProfileCreate
from app.schemas.project import ProjectCreate, ProjectUpdate, ProjectResponse
from app.schemas.proposal import ProposalCreate, ProposalResponse
from app.schemas.ai_match import AIMatchRequest, AIMatchResponse
from app.schemas.escrow import EscrowCreate, EscrowRelease
from app.schemas.auth import TokenPair
__all__ = [
"UserCreate", "UserLogin", "UserProfileUpdate", "FreelancerProfileCreate",
"ProjectCreate", "ProjectUpdate", "ProjectResponse",
"ProposalCreate", "ProposalResponse",
"AIMatchRequest", "AIMatchResponse",
"EscrowCreate", "EscrowRelease",
"TokenPair",
]
+17
View File
@@ -0,0 +1,17 @@
"""Схемы AI-матчинга."""
from pydantic import BaseModel, Field
class AIMatchRequest(BaseModel):
project_id: str = Field(..., description="ID проекта")
limit: int = Field(default=10, ge=1, le=50)
min_score: float | None = Field(default=None, ge=0.0, le=1.0)
class AIMatchResponse(BaseModel):
freelancer_id: str
name: str
skills_matched: list[str]
match_score: float
reasons: list[str]
+8
View File
@@ -0,0 +1,8 @@
"""Схемы авторизации."""
from pydantic import BaseModel
class TokenPair(BaseModel):
access_token: str
refresh_token: str
+14
View File
@@ -0,0 +1,14 @@
"""Схемы escrow-транзакций."""
from pydantic import BaseModel, Field
class EscrowCreate(BaseModel):
project_id: str = Field(..., description="ID проекта")
client_id: str = Field(..., description="ID клиента")
freelancer_id: str = Field(..., description="ID фрилансера")
amount: float = Field(..., gt=0, description="Сумма в рублях")
class EscrowRelease(BaseModel):
transaction_id: str = Field(..., description="ID транзакции")
+36
View File
@@ -0,0 +1,36 @@
"""Схемы проектов."""
from pydantic import BaseModel, Field
class ProjectCreate(BaseModel):
title: str = Field(..., min_length=5, max_length=255)
description: str = Field(..., min_length=20)
category: str | None = None
required_skills: list[str] = []
budget_min: float | None = None
budget_max: float | None = None
deadline: str | None = None # ISO format
class ProjectUpdate(BaseModel):
title: str | None = None
description: str | None = None
status: str | None = None
budget_min: float | None = None
budget_max: float | None = None
deadline: str | None = None
class ProjectResponse(BaseModel):
id: str
title: str
description: str
category: str | None
required_skills: list[str]
budget_min: float | None
budget_max: float | None
status: str
deadline: str | None
created_at: str
updated_at: str
+20
View File
@@ -0,0 +1,20 @@
"""Схемы заявок."""
from pydantic import BaseModel
class ProposalCreate(BaseModel):
cover_letter: str | None = None
proposed_price: float | None = None
estimated_days: int | None = None
class ProposalResponse(BaseModel):
id: str
project_id: str
freelancer_id: str
cover_letter: str | None
proposed_price: float | None
estimated_days: int | None
status: str
created_at: str
+20
View File
@@ -0,0 +1,20 @@
"""Схемы отзывов и рейтингов."""
from pydantic import BaseModel, Field
class ReviewCreate(BaseModel):
project_id: str = Field(..., description="ID проекта")
reviewee_id: str = Field(..., description="ID того кого оценивают")
rating: int = Field(..., ge=1, le=5)
comment: str | None = Field(default=None, max_length=2000)
class ReviewResponse(BaseModel):
id: str
project_id: str
reviewer_name: str
reviewee_name: str
rating: int
comment: str | None
created_at: str
+28
View File
@@ -0,0 +1,28 @@
"""Схемы пользователей."""
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
email: EmailStr
password: str # min 12 chars
role: str = "freelancer" # client | freelancer | both
full_name: str | None = None
class UserLogin(BaseModel):
email: EmailStr
password: str
class UserProfileUpdate(BaseModel):
full_name: str | None = None
avatar_url: str | None = None
class FreelancerProfileCreate(BaseModel):
bio: str | None = None
skills: list[str] = []
hourly_rate: float | None = None
experience_years: int | None = None
languages: list[str] = []