Files
Airtep/gig-poc/apps/api/app/domain/schemas.py
2026-03-30 20:49:40 +08:00

172 lines
4.1 KiB
Python

from __future__ import annotations
from datetime import datetime
from enum import Enum
from pydantic import BaseModel, Field, field_validator, model_validator
class SalaryType(str, Enum):
daily = "daily"
hourly = "hourly"
monthly = "monthly"
task = "task"
class SourceType(str, Enum):
job_to_worker = "job_to_worker"
worker_to_job = "worker_to_job"
class Salary(BaseModel):
type: SalaryType = SalaryType.daily
amount: float = 0
currency: str = "CNY"
class SkillScore(BaseModel):
name: str
score: float = Field(ge=0, le=1)
class JobCard(BaseModel):
job_id: str
title: str
category: str
description: str
skills: list[str] = Field(default_factory=list)
city: str
region: str
location_detail: str
start_time: datetime
duration_hours: float = Field(gt=0)
headcount: int = Field(gt=0)
salary: Salary
work_mode: str
tags: list[str] = Field(default_factory=list)
confidence: float = Field(ge=0, le=1)
class WorkerCard(BaseModel):
worker_id: str
name: str
description: str
skills: list[SkillScore] = Field(default_factory=list)
cities: list[str] = Field(default_factory=list)
regions: list[str] = Field(default_factory=list)
availability: list[str] = Field(default_factory=list)
experience_tags: list[str] = Field(default_factory=list)
reliability_score: float = Field(ge=0, le=1)
profile_completion: float = Field(ge=0, le=1)
confidence: float = Field(ge=0, le=1)
class MatchBreakdown(BaseModel):
skill_score: float = Field(ge=0, le=1)
region_score: float = Field(ge=0, le=1)
time_score: float = Field(ge=0, le=1)
experience_score: float = Field(ge=0, le=1)
reliability_score: float = Field(ge=0, le=1)
class MatchResult(BaseModel):
match_id: str
source_type: SourceType
source_id: str
target_id: str
match_score: float = Field(ge=0, le=1)
breakdown: MatchBreakdown
reasons: list[str] = Field(default_factory=list, min_length=3)
class ExtractTextRequest(BaseModel):
text: str = Field(min_length=5)
class IngestJobRequest(BaseModel):
job: JobCard
class IngestWorkerRequest(BaseModel):
worker: WorkerCard
class MatchWorkersRequest(BaseModel):
job_id: str | None = None
job: JobCard | None = None
top_n: int = Field(default=10, ge=1, le=50)
@model_validator(mode="after")
def validate_source(self) -> "MatchWorkersRequest":
if not self.job_id and not self.job:
raise ValueError("job_id 或 job 至少需要提供一个")
return self
class MatchJobsRequest(BaseModel):
worker_id: str | None = None
worker: WorkerCard | None = None
top_n: int = Field(default=10, ge=1, le=50)
@model_validator(mode="after")
def validate_source(self) -> "MatchJobsRequest":
if not self.worker_id and not self.worker:
raise ValueError("worker_id 或 worker 至少需要提供一个")
return self
class ExtractResponse(BaseModel):
success: bool
data: JobCard | WorkerCard | None = None
errors: list[str] = Field(default_factory=list)
missing_fields: list[str] = Field(default_factory=list)
class BootstrapResponse(BaseModel):
jobs: int
workers: int
skills: int
categories: int
regions: int
class HealthStatus(BaseModel):
service: str
database: str
rag: str
timestamp: datetime
class ListResponse(BaseModel):
items: list[dict]
total: int
class MatchResponse(BaseModel):
items: list[MatchResult]
class ExplainResponse(BaseModel):
match: MatchResult
class PromptOutput(BaseModel):
content: dict
raw_text: str
class QueryFilters(BaseModel):
entity_type: str
city: str | None = None
region: str | None = None
categories: list[str] = Field(default_factory=list)
tags: list[str] = Field(default_factory=list)
skills: list[str] = Field(default_factory=list)
@field_validator("entity_type")
@classmethod
def validate_entity_type(cls, value: str) -> str:
if value not in {"job", "worker"}:
raise ValueError("entity_type must be job or worker")
return value