"""CareerNexus 全链路数据模型(pydantic v2)。 覆盖:JD 原始记录、结构化抽取结果、岗位定义、图谱观察、 变更事件、人岗匹配结果、简历画像与人机协同审核记录。 """ from __future__ import annotations from datetime import date from typing import Literal, Optional from pydantic import BaseModel, Field class JDRecord(BaseModel): """一条招聘 JD 原始记录。source_id 由接入层分配(文件名#行号),不可变。""" jid: str job_title: str company: str city: str = "" salary: str = "" industry: str = "" # 招聘平台标注的所属行业 publish_date: date source: str = "" url: str = "" text: str source_id: str = "" # 形如 batch_2023.jsonl#17 weight: float = 1.0 # 清洗阶段赋予的时间衰减权重 is_duplicate: bool = False # 近重复(抄袭)降权标记 dup_of: Optional[str] = None # 指向被保留的最早来源 jid is_noise: bool = False # 噪声(广告/无关条款)标记 class ExtractionResult(BaseModel): """从单条 JD 文本抽取出的结构化岗位定义。""" jid: str = "" job_title: str = "" responsibilities: list[str] = Field(default_factory=list) required_skills: list[str] = Field(default_factory=list) preferred_skills: list[str] = Field(default_factory=list) scenarios: list[str] = Field(default_factory=list) evidence: dict[str, str] = Field(default_factory=dict) # 字段 -> 证据片段 confidence: float = 0.0 extractor: str = "offline" prompt_version: str = "offline-v1" class JobDefinition(BaseModel): """聚合后的岗位定义(图谱中的 Job 节点属性)。""" job_id: str title: str category: str = "" industries: list[str] = Field(default_factory=list) # 归属行业(1-2 个) required_skills: list[str] = Field(default_factory=list) preferred_skills: list[str] = Field(default_factory=list) responsibilities: list[str] = Field(default_factory=list) scenarios: list[str] = Field(default_factory=list) expected_years: int = 3 status: Literal["active", "pending", "rejected"] = "active" source_count: int = 0 # 支撑 JD 数 company_count: int = 0 # 独立企业数 # 候选新岗位的支撑证据(新岗位发现时填写):支撑 JD 的 source_id 与出现季度窗口, # 审核入库时用于生成真实的 REQUIRES 区间、来源列表与演化事件。 support_source_ids: list[str] = Field(default_factory=list) support_windows: list[str] = Field(default_factory=list) class GraphObservation(BaseModel): """一次岗位-技能关系观察(带时间窗,追加而不覆盖)。""" job_id: str skill: str importance: Literal["required", "preferred"] weight: float = 1.0 window: str = "" # 季度窗口,如 2022Q1 source_ids: list[str] = Field(default_factory=list) class ChangeEvent(BaseModel): """岗位能力演化事件:新增 / 增强 / 减弱 / 退出。""" event_id: str job_id: str skill: str change_type: Literal["新增", "增强", "减弱", "退出"] window: str prev_weight: float = 0.0 curr_weight: float = 0.0 source_ids: list[str] = Field(default_factory=list) description: str = "" created_at: str = "" class ResumeProfile(BaseModel): """脱敏后的简历画像。""" name: str = "" phone: str = "" email: str = "" years: float = 0.0 skills: list[str] = Field(default_factory=list) scenarios: list[str] = Field(default_factory=list) raw_masked_text: str = "" # 结构化信息(规范简历模板用) gender: str = "" # 性别(不脱敏,人口统计学字段) education: str = "" # 最高学历(本科/硕士/博士…) address: str = "" # 家庭住址/现居地(脱敏到城市) photo_b64: str = "" # 证件照 base64(data URI,无照片为空) sections: list[dict] = Field(default_factory=list) # [{title, text}] 分节正文 class MatchResult(BaseModel): """人岗匹配结果:总分、四维分、命中/缺失/可迁移技能与学习路径。""" job_id: str job_title: str = "" total_score: float = 0.0 dimension_scores: dict[str, float] = Field(default_factory=dict) matched_required: list[str] = Field(default_factory=list) missing_required: list[str] = Field(default_factory=list) matched_preferred: list[str] = Field(default_factory=list) missing_preferred: list[str] = Field(default_factory=list) transferable_skills: list[str] = Field(default_factory=list) learning_path: list[dict] = Field(default_factory=list) is_match: bool = False class ReviewRecord(BaseModel): """人机协同审核留痕。""" candidate_id: str action: Literal["accept", "modify", "reject"] operator: str reason: str = "" before: dict = Field(default_factory=dict) after: dict = Field(default_factory=dict) time: str = ""