# ============================================================================= # 企微IT智能服务台 — 坐席 Pydantic Schema # ============================================================================= # 说明:定义坐席相关的请求/响应数据结构 # 包含:登录、状态更新、响应三种 Schema # ============================================================================= from datetime import datetime from typing import List, Optional from pydantic import BaseModel, Field, field_validator # -------------------------------------------------------------------------- # 坐席状态合法值 # -------------------------------------------------------------------------- VALID_AGENT_STATUSES = {"online", "offline", "busy"} # -------------------------------------------------------------------------- # 坐席登录 Schema # -------------------------------------------------------------------------- class AgentLogin(BaseModel): """坐席登录请求 Schema。 第一步使用简单的用户名密码登录。 user_id 对应企微通讯录中的 UserID。 admin 角色需要 OTP 二次验证。 可选本地密码认证(企微验证失败时的备用认证)。 P0-#5 改动: - 新增 password 字段:本地密码(可选) - 企微主路径优先 → 本地 password 双因子(新增) Attributes: user_id: 企微用户ID name: 坐席姓名 otp_code: OTP 动态码(admin 角色必填) password: 本地密码(企微验证失败时的备用认证) """ user_id: str = Field(..., min_length=1, max_length=64, description="企微用户ID") name: str = Field(..., min_length=1, max_length=128, description="坐席姓名") otp_code: Optional[str] = Field(None, min_length=6, max_length=6, description="OTP动态码(6位数字)") password: Optional[str] = Field(None, description="本地密码(可选)") # -------------------------------------------------------------------------- # 坐席状态更新 Schema # -------------------------------------------------------------------------- class AgentStatusUpdate(BaseModel): """坐席状态更新请求 Schema。 坐席上线、离线、设为忙碌时使用。 Attributes: status: 新的坐席状态 """ status: str = Field(..., description="坐席状态: online/offline/busy") @field_validator("status") @classmethod def validate_status(cls, v: str) -> str: """校验坐席状态值是否合法。""" if v not in VALID_AGENT_STATUSES: raise ValueError(f"无效的坐席状态: {v},合法值为: {VALID_AGENT_STATUSES}") return v # -------------------------------------------------------------------------- # 坐席响应 Schema(返回给前端的数据结构) # -------------------------------------------------------------------------- class AgentResponse(BaseModel): """坐席响应 Schema。 返回给前端的坐席数据结构。 使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。 Attributes: id: 坐席ID user_id: 企微用户ID name: 坐席姓名 status: 坐席状态 role: 角色(admin=组长, agent=坐席) skill_tags: 技能标签列表 current_load: 当前服务会话数 max_load: 最大同时服务数 created_at: 创建时间 updated_at: 更新时间 """ id: str user_id: str name: str status: str role: str = "agent" skill_tags: List[str] = [] current_load: int max_load: int otp_enabled: bool = False # OTP 是否已启用 created_at: datetime updated_at: datetime model_config = {"from_attributes": True} # -------------------------------------------------------------------------- # 坐席列表响应 Schema # -------------------------------------------------------------------------- class AgentListResponse(BaseModel): """坐席列表响应 Schema。 Attributes: items: 坐席列表 """ items: List[AgentResponse]