generated from kgod/ai-review-template
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
"""RESUME_ENRICHING 模块组件事件分发与结构化收集器(竞赛/标签/联系方式)。
|
|
|
|
记录类模块(RecordFields/ChoiceChips/条目确认/卡内调整)见 enrichment_record_collectors.py。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .enrichment_custom import (
|
|
CUSTOM_MODULES,
|
|
custom_card_picker_transition,
|
|
finish_custom_enrichment,
|
|
)
|
|
from .enrichment_modules import ENRICHMENT_MODULES, TAG_SEQUENCE, ModuleSpec
|
|
from .enrichment_record_collectors import (
|
|
collect_choice,
|
|
collect_record_fields,
|
|
confirm_module_entry,
|
|
edit_module_entry,
|
|
)
|
|
from .fsm import FSMError, Transition, assistant_turn, component
|
|
from .fsm_enrichment import (
|
|
advance_or_finish,
|
|
begin_module,
|
|
current_module,
|
|
defer_enrichment,
|
|
ensure_enrichment_state,
|
|
mark_completed,
|
|
progress_block,
|
|
skip_module,
|
|
)
|
|
from .models import ComposerMode, Stage
|
|
from .validators import competition_entry_errors, normalize_tags
|
|
|
|
|
|
def process_module_event(
|
|
profile: dict[str, Any],
|
|
component_data: dict[str, Any],
|
|
action: str,
|
|
payload: dict[str, Any],
|
|
) -> Transition:
|
|
ensure_enrichment_state(profile)
|
|
name = component_data.get("component_name")
|
|
if action in {"defer", "finish_enrichment"}:
|
|
return defer_enrichment(profile)
|
|
if name == "CustomCardPicker":
|
|
return _handle_custom_card_picker(profile, payload)
|
|
spec = current_module(profile)
|
|
if spec is None:
|
|
raise FSMError("no_active_module", "当前没有进行中的完善模块", status_code=409)
|
|
if component_data.get("module") != spec.name:
|
|
raise FSMError("stale_module", "该组件不属于当前模块", status_code=409)
|
|
if action in {"skip", "skip_module"}:
|
|
return skip_module(profile, spec)
|
|
if action in {"edit", "edit_anchor"}:
|
|
return edit_module_entry(profile, spec)
|
|
if name == "ChoiceChips":
|
|
return collect_choice(profile, spec, payload)
|
|
if name == "RecordFields":
|
|
return collect_record_fields(profile, spec, payload)
|
|
if name == "CompetitionFields":
|
|
return _collect_competition(profile, spec, payload)
|
|
if name == "TagsInput":
|
|
return _collect_tags(profile, component_data, spec, payload)
|
|
if name == "ExperienceConfirmCard":
|
|
return confirm_module_entry(profile, spec, payload)
|
|
if name == "AddAnother":
|
|
return _handle_add_another(profile, spec, payload)
|
|
raise FSMError("invalid_component", f"Unsupported module component: {name}", status_code=422)
|
|
|
|
|
|
def _collect_competition(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
|
|
entry = {
|
|
"record_type": "competition",
|
|
"module": spec.name,
|
|
"name": str(payload.get("name") or "").strip(),
|
|
"award": str(payload.get("award") or "").strip(),
|
|
"date": str(payload.get("date") or "").strip(),
|
|
"description": str(payload.get("description") or "").strip() or None,
|
|
}
|
|
errors = competition_entry_errors(entry)
|
|
if errors:
|
|
raise FSMError(
|
|
"invalid_competition", "竞赛信息不完整或格式有误", status_code=422, missing_fields=errors
|
|
)
|
|
profile["enrichment"]["module_draft"] = {"entry": entry}
|
|
transition = Transition(
|
|
Stage.RESUME_ENRICHING,
|
|
profile,
|
|
assistant_turn(
|
|
"请确认这条竞赛记录。",
|
|
[
|
|
progress_block(profile, spec),
|
|
component(
|
|
"ExperienceConfirmCard",
|
|
module=spec.name,
|
|
confirmation_kind="module_entry",
|
|
title="确认竞赛记录",
|
|
value={key: entry[key] for key in ("name", "award", "date", "description")},
|
|
labels={"name": "竞赛名称", "award": "获奖名称", "date": "获奖时间", "description": "经历描述"},
|
|
),
|
|
],
|
|
mode=ComposerMode.UI_ONLY,
|
|
),
|
|
)
|
|
transition.polish_description = True
|
|
return transition
|
|
|
|
|
|
def _handle_add_another(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
|
|
value = str(payload.get("value") or "")
|
|
if value == "again":
|
|
profile["enrichment"]["module_draft"] = {}
|
|
return begin_module(profile, spec)
|
|
if value == "next":
|
|
mark_completed(profile, spec.name)
|
|
if profile["enrichment"].get("custom_mode"):
|
|
return custom_card_picker_transition(profile, refresh=True)
|
|
return advance_or_finish(profile)
|
|
raise FSMError("invalid_action", "请选择再添加一段或进入下一项", status_code=422)
|
|
|
|
|
|
def _handle_custom_card_picker(
|
|
profile: dict[str, Any], payload: dict[str, Any]
|
|
) -> Transition:
|
|
value = str(payload.get("value") or payload.get("card_type") or "")
|
|
if value == "finish":
|
|
return finish_custom_enrichment(profile)
|
|
module_name = CUSTOM_MODULES.get(value)
|
|
if module_name is None:
|
|
raise FSMError("invalid_card_type", "请选择列出的简历卡片", status_code=422)
|
|
enrichment = profile["enrichment"]
|
|
enrichment["current"] = module_name
|
|
enrichment["module_draft"] = {}
|
|
enrichment["custom_mode"] = True
|
|
return begin_module(profile, ENRICHMENT_MODULES[module_name])
|
|
|
|
|
|
def _collect_tags(
|
|
profile: dict[str, Any],
|
|
component_data: dict[str, Any],
|
|
spec: ModuleSpec,
|
|
payload: dict[str, Any],
|
|
) -> Transition:
|
|
expected = component_data.get("field")
|
|
field = str(payload.get("field") or "")
|
|
if field != expected or field not in TAG_SEQUENCE:
|
|
raise FSMError("invalid_field", "提交字段与当前组件不符", status_code=422)
|
|
profile["tags"][field] = normalize_tags(payload.get("value") or payload.get("values") or [])
|
|
|
|
mark_completed(profile, spec.name)
|
|
return advance_or_finish(profile, refresh=True)
|
|
|