# ============================================================================= # 企微IT智能服务台 — 扫码登录 Pydantic Schema # ============================================================================= # 说明:定义扫码登录的请求/响应数据结构 # 涵盖 4 个端点的入参/出参: # 1. POST /api/auth_qrcode/create — 创建扫码登录票据 # 2. GET /api/auth_qrcode/poll/{ticket} — 前端轮询扫码状态 # 3. POST /api/auth_qrcode/scan — 企微用户扫码后 OAuth code 回调 # 4. POST /api/auth_qrcode/confirm — 当前已登录用户确认授权 # ============================================================================= from datetime import datetime from typing import List, Optional from pydantic import BaseModel, Field # -------------------------------------------------------------------------- # POST /api/auth_qrcode/create — 创建扫码登录票据 # -------------------------------------------------------------------------- class QrcodeCreateResponse(BaseModel): """扫码登录票据创建响应。 Attributes: ticket: 票据 UUID,前端用此票据轮询状态 qrcode_url: 企微 OAuth2 授权 URL(前端渲染二维码) expires_in: 票据有效期(秒),默认 120 expires_at: 票据过期时间(ISO 8601 字符串) """ ticket: str = Field(..., description="票据 UUID") qrcode_url: str = Field(..., description="企微 OAuth2 授权 URL") expires_in: int = Field(120, description="有效期(秒)") expires_at: datetime = Field(..., description="过期时间(ISO 8601)") # -------------------------------------------------------------------------- # GET /api/auth_qrcode/poll/{ticket} — 轮询扫码状态 # -------------------------------------------------------------------------- class QrcodePollResponse(BaseModel): """扫码登录票据轮询响应。 status 取值: - waiting: 票据有效,等待扫码 - scanned: 已扫码,等待确认 - confirmed: 已确认登录成功,附带 token - expired: 票据过期/不存在 Attributes: status: 扫码状态 employee_id: 企微用户 ID(scanned/confirmed 时返回) name: 企微用户姓名(scanned/confirmed 时返回) token: 登录 Token(confirmed 时返回,前端存 localStorage) """ status: str = Field(..., description="等待/已扫码/已确认/已过期") employee_id: Optional[str] = Field(None, description="企微用户 ID") name: Optional[str] = Field(None, description="企微用户姓名") token: Optional[str] = Field(None, description="登录 Token") # -------------------------------------------------------------------------- # POST /api/auth_qrcode/scan — 企微 OAuth code 回调 # -------------------------------------------------------------------------- class QrcodeScanRequest(BaseModel): """扫码登录扫码请求体。 Attributes: ticket: 扫码登录票据(UUID) code: 企微 OAuth2 授权回调 code """ ticket: str = Field(..., min_length=1, description="扫码登录票据") code: str = Field(..., min_length=1, description="企微 OAuth2 授权 code") class QrcodeScanResponse(BaseModel): """扫码登录扫码响应。 Attributes: success: 是否成功 message: 提示消息 """ success: bool = Field(..., description="是否成功") message: str = Field(..., description="提示消息") # -------------------------------------------------------------------------- # POST /api/auth_qrcode/confirm — 当前已登录用户确认授权 # -------------------------------------------------------------------------- class QrcodeConfirmRequest(BaseModel): """扫码登录确认请求体。 Attributes: ticket: 扫码登录票据(UUID) otp_code: OTP 动态码(管理员场景下可选,普通坐席可空) """ ticket: str = Field(..., min_length=1, description="扫码登录票据") otp_code: Optional[str] = Field( None, min_length=6, max_length=6, description="OTP 动态码(管理员可选,普通坐席留空)", ) class QrcodeConfirmResponse(BaseModel): """扫码登录确认响应。 Attributes: token: 登录 Token(scanned 用户换发的新 token) employee_id: 企微用户 ID name: 用户姓名 roles: 用户角色列表 require_otp: 是否需要 OTP 二次验证(预留,本任务不强制) """ token: str = Field(..., description="登录 Token") employee_id: str = Field(..., description="企微用户 ID") name: str = Field(..., description="用户姓名") roles: List[str] = Field(default_factory=list, description="用户角色列表") require_otp: Optional[bool] = Field( None, description="是否需要 OTP 二次验证(预留字段,Phase 2.1 实现)", )