feat: добавить отзывы, milestone-платежи, портфолио, skill-tests и верификацию (фичи Upwork/Fiverr)

Фичи конкурентов внедрены:
- Reviews API + UI — система отзывов с рейтингом 1-5 звёзд
- Milestones (Upwork-style) — разделение escrow на этапы с submit/approve
- Portfolio — портфолио фрилансера с превью работ и технологиями
- Skill Tests (Upwork-style) — сертификация навыков с тестами
- Verification Badges — верификация email/phone/id/bank

Модели: Milestone, PortfolioItem, SkillTest/SkillTestResult, Verification
This commit is contained in:
2026-07-03 14:03:44 +00:00
parent fd52eeae3c
commit 6ecb110768
13 changed files with 623 additions and 1 deletions
+33
View File
@@ -0,0 +1,33 @@
"""Модель Skill Tests (сертификация навыков как на Upwork)."""
import uuid
from datetime import datetime, timezone
from sqlalchemy import Column, DateTime, Enum, Float, ForeignKey, Integer, String, Text, func
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.core.database import Base
class SkillTest(Base):
__tablename__ = "skill_tests"
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name: Mapped[str] = mapped_column(String(255), nullable=False) # например "Python Basics"
category: Mapped[str] = mapped_column(String(100)) # programming, design, etc.
questions_count: Mapped[int] = mapped_column(Integer, default=40)
passing_score: Mapped[float] = mapped_column(Float(precision=5, scale=2), default=70.0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
class SkillTestResult(Base):
__tablename__ = "skill_test_results"
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
skill_test_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("skill_tests.id"), nullable=False)
score: Mapped[float] = mapped_column(Float(precision=5, scale=2))
passed: Mapped[bool] = mapped_column(default=False)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))