# ============================================================================= # 企微IT智能服务台 — 阶段5 自动化闭环 Schema # ============================================================================= # 说明:定义自动化会话相关接口的 Pydantic 请求/响应模型,以及 ORM → dict # 序列化辅助函数。所有响应沿用项目 {code, data, message} 约定, # 此处只描述 data 结构。 # ============================================================================= from __future__ import annotations from datetime import datetime from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field # -------------------------------------------------------------------------- # 请求模型 # -------------------------------------------------------------------------- class CreateSessionRequest(BaseModel): """创建自动化会话请求。""" conversation_id: Optional[str] = Field(None, description="关联工单ID(弱关联)") employee_id: str = Field(..., description="发起员工企微 UserID") description: str = Field(..., description="员工诉求/原始消息") mode: str = Field("real_exec", description="执行模式:plan_only / real_exec") class ApprovalDecisionRequest(BaseModel): """坐席审批决策请求。""" decision: str = Field(..., description="approve / reject") note: Optional[str] = Field(None, description="审批意见") class ConfirmRequest(BaseModel): """员工 H5 二次确认请求。""" confirmed: bool = Field(..., description="是否确认执行") note: Optional[str] = Field(None, description="备注") class TakeoverRequest(BaseModel): """转人工接管请求。""" agent_id: str = Field(..., description="接管坐席ID") note: Optional[str] = Field(None, description="接管说明") class ResolveFeedbackRequest(BaseModel): """处置结果反馈(员工是否满意)。""" satisfied: bool = Field(True, description="是否满意") note: Optional[str] = Field(None, description="反馈备注") class ScenarioConfigUpdate(BaseModel): """场景配置更新请求(管理端)。""" name: Optional[str] = None description: Optional[str] = None enabled: Optional[bool] = None trigger_conditions: Optional[dict] = None actions: Optional[list] = None approval_strategy: Optional[dict] = None # -------------------------------------------------------------------------- # 响应模型 # -------------------------------------------------------------------------- class IntentResult(BaseModel): """意图识别结果。""" scenario_key: Optional[str] = None confidence: float = 0.0 raw: str = "" error: str = "" class ActionResponse(BaseModel): """处置动作响应。""" id: str session_id: str action_index: int action_type: str adapter: str risk_level: str title: str description: str status: str payload: Optional[dict] = None result: Optional[dict] = None error: Optional[str] = None approved_by: Optional[str] = None approved_at: Optional[str] = None class ApprovalTicketResponse(BaseModel): """审批单响应。""" id: str action_id: str session_id: str approver_id: Optional[str] = None channel: str status: str reason: Optional[str] = None decision_note: Optional[str] = None decided_at: Optional[str] = None class SessionResponse(BaseModel): """自动化会话响应。""" id: str conversation_id: Optional[str] = None employee_id: str agent_id: Optional[str] = None scenario_key: Optional[str] = None status: str mode: str confidence: float title: str intent: Optional[dict] = None current_action_id: Optional[str] = None auto_close_at: Optional[str] = None resolved_at: Optional[str] = None closed_by: Optional[str] = None meta: Optional[dict] = None actions: List[ActionResponse] = Field(default_factory=list) approval: Optional[ApprovalTicketResponse] = None created_at: Optional[str] = None updated_at: Optional[str] = None class ScenarioConfigResponse(BaseModel): """场景配置响应。""" id: str scenario_key: str name: str description: str enabled: bool trigger_conditions: Optional[dict] = None actions: Optional[list] = None approval_strategy: Optional[dict] = None current_version_id: Optional[str] = None class RuleVersionResponse(BaseModel): """规则版本响应。""" id: str scenario_key: str version: int content: Optional[dict] = None status: str canary_percent: int created_by: Optional[str] = None remark: str created_at: Optional[str] = None class AutoMetricsResponse(BaseModel): """自动化看板指标响应。""" total_sessions: int = 0 resolved_sessions: int = 0 handoff_sessions: int = 0 error_sessions: int = 0 auto_executed_actions: int = 0 approval_required_actions: int = 0 by_scenario: Dict[str, int] = Field(default_factory=dict) # -------------------------------------------------------------------------- # 序列化辅助 # -------------------------------------------------------------------------- def _iso(dt: Optional[datetime]) -> Optional[str]: """将 datetime 转为 ISO 字符串(None 透传)。""" return dt.isoformat() if dt else None def serialize_action(action: Any) -> ActionResponse: """将 AutoAction ORM 对象序列化为响应模型。""" return ActionResponse( id=action.id, session_id=action.session_id, action_index=action.action_index, action_type=action.action_type, adapter=action.adapter, risk_level=action.risk_level, title=action.title, description=action.description, status=action.status, payload=action.payload, result=action.result, error=action.error, approved_by=action.approved_by, approved_at=_iso(action.approved_at), ) def serialize_approval(ticket: Any) -> ApprovalTicketResponse: """将 ApprovalTicket ORM 对象序列化为响应模型。""" return ApprovalTicketResponse( id=ticket.id, action_id=ticket.action_id, session_id=ticket.session_id, approver_id=ticket.approver_id, channel=ticket.channel, status=ticket.status, reason=ticket.reason, decision_note=ticket.decision_note, decided_at=_iso(ticket.decided_at), ) def serialize_session( session: Any, actions: Optional[List[Any]] = None, ticket: Optional[Any] = None, ) -> SessionResponse: """将 AutoSession ORM 对象序列化为响应模型。""" return SessionResponse( id=session.id, conversation_id=session.conversation_id, employee_id=session.employee_id, agent_id=session.agent_id, scenario_key=session.scenario_key, status=session.status, mode=session.mode, confidence=session.confidence, title=session.title, intent=session.intent, current_action_id=session.current_action_id, auto_close_at=_iso(session.auto_close_at), resolved_at=_iso(session.resolved_at), closed_by=session.closed_by, meta=session.meta, actions=[serialize_action(a) for a in (actions or [])], approval=serialize_approval(ticket) if ticket else None, created_at=_iso(session.created_at), updated_at=_iso(session.updated_at), )