chore: initial baseline with P0-safety .gitignore

This commit is contained in:
Simon
2026-06-14 16:49:18 +08:00
commit 63262292d7
510 changed files with 146008 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# =============================================================================
# 企微IT智能服务台 — Schema 包初始化
# =============================================================================
# 说明:导出所有 Pydantic Schema,方便统一导入
# =============================================================================
+492
View File
@@ -0,0 +1,492 @@
# =============================================================================
# 企微IT智能服务台 — 管理后台 Pydantic Schema
# =============================================================================
# 说明:定义管理后台专用请求/响应数据结构
# 包含:仪表盘、配置管理、坐席管理、集成配置、快速回复审核、
# 分配模式、会话监控、全局搜索等全部 Schema
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# ==========================================================================
# 配置管理 Schema
# ==========================================================================
class ConfigItemResponse(BaseModel):
"""单个配置项响应 Schema。
Attributes:
key: 配置键
value: 配置值
description: 配置说明
value_type: 值类型(boolean/number/string/json_array/json_object
"""
key: str = Field(..., description="配置键")
value: str = Field(..., description="配置值")
description: str = Field(default="", description="配置说明")
value_type: str = Field(default="string", description="值类型: boolean/number/string/json_array/json_object")
model_config = {"from_attributes": True}
class ConfigGroupResponse(BaseModel):
"""配置分组响应 Schema。
按功能前缀将配置项分组,方便前端展示。
Attributes:
name: 分组名称
key_prefix: 配置键前缀
items: 该分组下的配置项列表
"""
name: str = Field(..., description="分组名称")
key_prefix: str = Field(..., description="配置键前缀")
items: List[ConfigItemResponse] = Field(default_factory=list, description="配置项列表")
class ConfigUpdateRequest(BaseModel):
"""配置更新请求 Schema。
Attributes:
value: 新的配置值
"""
value: str = Field(..., min_length=1, description="新的配置值")
class ConfigHistoryItem(BaseModel):
"""配置变更历史条目 Schema。
Attributes:
id: 日志ID
config_key: 配置键
old_value: 变更前的值
new_value: 变更后的值
changed_by: 变更操作人 agent_id
changed_by_name: 变更操作人姓名
changed_at: 变更时间
"""
id: str = Field(..., description="日志ID")
config_key: str = Field(..., description="配置键")
old_value: str = Field(..., description="变更前的值")
new_value: str = Field(..., description="变更后的值")
changed_by: str = Field(..., description="变更操作人 agent_id")
changed_by_name: str = Field(default="", description="变更操作人姓名")
changed_at: datetime = Field(..., description="变更时间")
model_config = {"from_attributes": True}
class ConfigHistoryResponse(BaseModel):
"""配置变更历史响应 Schema。
Attributes:
items: 变更历史条目列表
"""
items: List[ConfigHistoryItem] = Field(default_factory=list, description="变更历史列表")
# ==========================================================================
# 坐席管理 Schema
# ==========================================================================
class AgentCreateRequest(BaseModel):
"""创建坐席请求 Schema。
Attributes:
user_id: 企微用户ID
name: 坐席姓名
role: 角色(admin=组长, agent=坐席)
skill_tags: 技能标签列表
max_load: 最大同时服务数
"""
user_id: str = Field(..., min_length=1, max_length=64, description="企微用户ID")
name: str = Field(..., min_length=1, max_length=128, description="坐席姓名")
role: str = Field(default="agent", description="角色: admin=组长, agent=坐席")
skill_tags: List[str] = Field(default_factory=list, description="技能标签列表")
max_load: int = Field(default=5, ge=1, le=50, description="最大同时服务数")
class AgentUpdateRequest(BaseModel):
"""更新坐席请求 Schema。
所有字段可选,只更新传入的字段。
Attributes:
role: 角色
skill_tags: 技能标签列表
max_load: 最大同时服务数
"""
role: Optional[str] = Field(None, description="角色: admin=组长, agent=坐席")
skill_tags: Optional[List[str]] = Field(None, description="技能标签列表")
max_load: Optional[int] = Field(None, ge=1, le=50, description="最大同时服务数")
class AdminAgentResponse(BaseModel):
"""管理后台坐席响应 Schema(含角色/技能标签/今日结单数)。
Attributes:
id: 坐席ID
user_id: 企微用户ID
name: 坐席姓名
status: 坐席状态
role: 角色
skill_tags: 技能标签列表
current_load: 当前服务会话数
max_load: 最大同时服务数
today_resolved: 今日结单数
created_at: 创建时间
updated_at: 更新时间
"""
id: str
user_id: str
name: str
status: str
role: str = "agent"
skill_tags: List[str] = []
current_load: int = 0
max_load: int = 5
today_resolved: int = 0
otp_secret: Optional[str] = None
otp_enabled: bool = False
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# =========================================================================
# 集成配置 Schema
# =========================================================================
class IntegrationConfig(BaseModel):
"""集成系统配置 Schema(通用,支持 url_key 和 access_key 两种模式)。
Attributes:
# url_key 模式(Dify / RAGFlow
api_url: API 地址
api_key_set: API Key 是否已设置
# access_key 模式(火绒安全)
access_key_id_set: AccessKey ID 是否已设置
access_key_secret_set: AccessKey Secret 是否已设置
base_url: 内网 Base URL
"""
# url_key 模式(Dify / RAGFlow
api_url: str = Field(default="", description="API 地址")
api_key_set: bool = Field(default=False, description="API Key 是否已设置")
# access_key 模式(火绒安全)
access_key_id_set: bool = Field(default=False, description="AccessKey ID 是否已设置")
access_key_secret_set: bool = Field(default=False, description="AccessKey Secret 是否已设置")
base_url: Optional[str] = Field(default=None, description="内网 Base URL")
# account_password 模式(联软LV7000
api_account_set: bool = Field(default=False, description="API账号是否已设置")
api_password_set: bool = Field(default=False, description="API密码是否已设置")
class IntegrationResponse(BaseModel):
"""集成系统响应 Schema。
Attributes:
id: 集成系统ID(如 dify/ragflow/huorong
name: 集成系统名称
status: 连接状态(connected/partial/disconnected/pending
configurable: 是否可配置
config_type: 配置类型(url_key/access_key),前端据此显示不同表单
config: 配置信息(不可配置时为 None)
"""
id: str = Field(..., description="集成系统ID")
name: str = Field(..., description="集成系统名称")
status: str = Field(default="disconnected", description="连接状态: connected/partial/disconnected/pending")
configurable: bool = Field(default=False, description="是否可配置")
config_type: Optional[str] = Field(default=None, description="配置类型: url_key/access_key/account_password")
config: Optional[IntegrationConfig] = Field(default=None, description="配置信息")
class IntegrationUpdateRequest(BaseModel):
"""集成系统配置更新请求 Schema。
支持三种模式:
- url_key 模式(Dify / RAGFlow):传入 api_url + api_key
- access_key 模式(火绒安全):传入 access_key_id + access_key_secret + base_url
- account_password 模式(联软LV7000):传入 api_account + api_password + base_url + validate_key(可选)
Attributes:
# url_key 模式
api_url: API 地址(可选,火绒/联软模式不需要)
api_key: API Key(可选,火绒/联软模式不需要)
# access_key 模式(火绒)
access_key_id: AccessKey ID(可选)
access_key_secret: AccessKey Secret(可选)
base_url: 内网 Base URL(可选)
# account_password 模式(联软)
api_account: API账号(可选,联软模式)
api_password: API密码(可选,联软模式)
validate_key: 验证密钥(可选,联软模式)
"""
# url_key 模式(Dify / RAGFlow
api_url: Optional[str] = Field(default=None, description="API 地址(Dify/RAGFlow 模式)")
api_key: Optional[str] = Field(default=None, description="API KeyDify/RAGFlow 模式)")
# access_key 模式(火绒安全)
access_key_id: Optional[str] = Field(default=None, description="AccessKey ID(火绒模式)")
access_key_secret: Optional[str] = Field(default=None, description="AccessKey Secret(火绒模式)")
base_url: Optional[str] = Field(default=None, description="内网 Base URL(火绒/联软模式)")
# account_password 模式(联软LV7000
api_account: Optional[str] = Field(default=None, description="API账号(联软模式)")
api_password: Optional[str] = Field(default=None, description="API密码(联软模式)")
validate_key: Optional[str] = Field(default=None, description="验证密钥(联软模式,可选)")
# ==========================================================================
# 快速回复审核 Schema
# ==========================================================================
class QuickReplyReviewRequest(BaseModel):
"""快速回复审核请求 Schema。
Attributes:
action: 审核动作(approve=通过, reject=驳回)
reason: 审核原因/意见(驳回时建议填写)
"""
action: str = Field(..., description="审核动作: approve/reject")
reason: str = Field(default="", description="审核原因/意见")
class AdminQuickReplyResponse(BaseModel):
"""管理后台快速回复响应 Schema(含审核信息)。
Attributes:
id: 模板ID
category: 分类
title: 模板标题
content: 模板内容
variables: 可用变量列表
status: 状态
version: 版本号
submitted_by: 提交人 agent_id
submitted_by_name: 提交人姓名
sort_order: 排序权重
created_at: 创建时间
updated_at: 更新时间
"""
id: str
category: str
title: str
content: str
variables: List[str]
status: str = "approved"
version: int = 1
submitted_by: Optional[str] = None
submitted_by_name: str = ""
sort_order: int = 0
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# ==========================================================================
# 分配模式 Schema
# ==========================================================================
class AssignmentModeItem(BaseModel):
"""分配模式条目 Schema。
Attributes:
id: 模式ID
name: 模式名称
enabled: 是否启用
locked: 是否锁定(阶段一锁定部分模式)
unlock_at: 解锁阶段说明
"""
id: str = Field(..., description="模式ID")
name: str = Field(..., description="模式名称")
enabled: bool = Field(default=False, description="是否启用")
locked: bool = Field(default=True, description="是否锁定")
unlock_at: str = Field(default="", description="解锁阶段说明")
class AssignmentModeResponse(BaseModel):
"""分配模式响应 Schema。
Attributes:
current_mode: 当前启用的分配模式
modes: 所有分配模式列表
"""
current_mode: str = Field(default="manual", description="当前分配模式")
modes: List[AssignmentModeItem] = Field(default_factory=list, description="分配模式列表")
class AssignmentModeUpdateRequest(BaseModel):
"""分配模式更新请求 Schema。
Attributes:
mode: 要切换的分配模式ID
"""
mode: str = Field(..., description="分配模式ID")
# ==========================================================================
# 仪表盘 Schema
# ==========================================================================
class SystemAlertItem(BaseModel):
"""单条系统告警 Schema。
与前端 SystemAlert 接口对齐,支持结构化告警展示。
Attributes:
type: 告警类型(如 quick_reply_pending / agent_offline / system_error
content: 告警内容描述
submitter: 提交人姓名(仅快速回复待审核类告警有值)
time: 告警发生时间(ISO 8601 格式)
severity: 严重程度(info/warning/critical
"""
type: str = Field(..., description="告警类型")
content: str = Field(..., description="告警内容")
submitter: Optional[str] = Field(default=None, description="提交人")
time: str = Field(..., description="告警时间")
severity: str = Field(default="info", description="严重程度: info/warning/critical")
class IntegrationHealthItem(BaseModel):
"""集成系统健康状态条目 Schema。
Attributes:
system: 系统名称
status: 连接状态
"""
system: str = Field(..., description="系统名称")
status: str = Field(default="disconnected", description="连接状态")
class DashboardOverviewResponse(BaseModel):
"""仪表盘总览响应 Schema。
Attributes:
online_agents: 在线坐席数
today_conversations: 今日会话数
avg_response_time: 平均响应时间(阶段一占位)
ai_hit_rate: AI 命中率(阶段一占位)
pending_reviews: 待审核快速回复数
system_alerts: 系统告警列表
integrations_health: 集成系统健康状态
"""
online_agents: int = Field(default=0, description="在线坐席数")
today_conversations: int = Field(default=0, description="今日会话数")
avg_response_time: str = Field(default="", description="平均响应时间(阶段一占位)")
ai_hit_rate: str = Field(default="", description="AI命中率(阶段一占位)")
pending_reviews: int = Field(default=0, description="待审核快速回复数")
system_alerts: List[SystemAlertItem] = Field(default_factory=list, description="系统告警列表")
integrations_health: List[IntegrationHealthItem] = Field(default_factory=list, description="集成系统健康状态")
# ==========================================================================
# 会话监控 Schema
# ==========================================================================
class SessionStats(BaseModel):
"""会话统计 Schema。
Attributes:
in_progress: 服务中会话数
queued: 排队中会话数
resolved_today: 今日已结单数
alerts: 告警数
"""
in_progress: int = Field(default=0, description="服务中会话数")
queued: int = Field(default=0, description="排队中会话数")
resolved_today: int = Field(default=0, description="今日已结单数")
alerts: int = Field(default=0, description="告警数")
class SessionItem(BaseModel):
"""会话条目 Schema(监控列表用)。
Attributes:
id: 会话ID
employee_name: 员工姓名
status: 会话状态
assigned_agent_name: 负责坐席姓名
urgency_score: 紧急度评分
created_at: 创建时间
last_message_summary: 最后消息摘要
"""
id: str = Field(..., description="会话ID")
employee_name: str = Field(default="", description="员工姓名")
status: str = Field(default="queued", description="会话状态")
assigned_agent_name: str = Field(default="", description="负责坐席姓名")
urgency_score: int = Field(default=1, description="紧急度评分")
created_at: datetime = Field(..., description="创建时间")
last_message_summary: str = Field(default="", description="最后消息摘要")
class MonitorSessionsResponse(BaseModel):
"""会话监控响应 Schema。
Attributes:
stats: 会话统计
items: 会话列表
"""
stats: SessionStats = Field(..., description="会话统计")
items: List[SessionItem] = Field(default_factory=list, description="会话列表")
# ==========================================================================
# 全局搜索 Schema
# ==========================================================================
class SearchItem(BaseModel):
"""搜索结果条目 Schema。
Attributes:
type: 结果类型(config/agent/quick_reply
id: 对象ID
name: 对象名称/标题
route: 前端路由路径
"""
type: str = Field(..., description="结果类型: config/agent/quick_reply")
id: str = Field(..., description="对象ID")
name: str = Field(..., description="对象名称/标题")
route: str = Field(..., description="前端路由路径")
class SearchResponse(BaseModel):
"""搜索结果响应 Schema。
Attributes:
items: 搜索结果列表
"""
items: List[SearchItem] = Field(default_factory=list, description="搜索结果列表")
+111
View File
@@ -0,0 +1,111 @@
# =============================================================================
# 企微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 二次验证。
Attributes:
user_id: 企微用户ID
name: 坐席姓名
otp_code: OTP 动态码(admin 角色必填)
"""
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位数字)")
# --------------------------------------------------------------------------
# 坐席状态更新 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]
+319
View File
@@ -0,0 +1,319 @@
# =============================================================================
# 企微IT智能服务台 — 会话 Pydantic Schema
# =============================================================================
# 说明:定义会话相关的请求/响应数据结构
# 包含:创建、更新、响应三种 Schema
# tags 字段使用 JSONB 结构,定义了详细的子结构
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# tags JSONB 字段的子结构定义
# --------------------------------------------------------------------------
class ConversationTags(BaseModel):
"""会话标签集合 — 对应 conversations.tags JSONB 字段。
记录会话的各种标记状态,用于坐席端展示和排序。
Attributes:
hand_raise: 举手标记(员工说"转人工"或点击摇人按钮)
need_intervene: 需介入标记(追问超过N轮)
emotion: 情绪标记(neutral/worried/angry/urgent
emotion_keywords: 触发情绪标记的关键词列表
repeat_count: 追问轮次计数
"""
# 举手标记(员工明确要求转人工)
hand_raise: bool = Field(default=False, description="举手标记")
# 需介入标记(同一问题追问超过阈值)
need_intervene: bool = Field(default=False, description="需介入标记")
# 情绪标记(neutral: 正常, worried: 担忧, angry: 愤怒, urgent: 紧急)
emotion: str = Field(
default="neutral",
description="情绪标记: neutral/worried/angry/urgent",
)
# 触发情绪标记的关键词列表
emotion_keywords: List[str] = Field(
default_factory=list,
description="触发情绪标记的关键词",
)
# 追问轮次计数(同一会话中员工连续追问的次数)
repeat_count: int = Field(default=0, description="追问轮次计数")
# --------------------------------------------------------------------------
# 会话状态枚举值校验
# --------------------------------------------------------------------------
VALID_STATUSES = {"ai_handling", "queued", "serving", "resolved"}
# --------------------------------------------------------------------------
# 创建会话 Schema(从企微消息创建会话时使用)
# --------------------------------------------------------------------------
class ConversationCreate(BaseModel):
"""创建会话请求 Schema。
从企微消息回调创建新会话时使用,
只需要员工ID和姓名,其他信息后续补充。
Attributes:
employee_id: 企微员工UserID
employee_name: 员工姓名
department: 部门
position: 岗位
level: 等级
"""
employee_id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID")
employee_name: str = Field(default="", max_length=128, description="员工姓名")
department: str = Field(default="", max_length=256, description="部门")
position: str = Field(default="", max_length=128, description="岗位")
level: str = Field(default="", max_length=64, description="等级")
# --------------------------------------------------------------------------
# 更新会话 Schema(坐席修改会话信息时使用)
# --------------------------------------------------------------------------
class ConversationUpdate(BaseModel):
"""更新会话请求 Schema。
坐席更新会话信息时使用,所有字段可选(只更新传入的字段)。
Attributes:
employee_name: 员工姓名
department: 部门
position: 岗位
level: 等级
is_vip: VIP标记
tags: 标签集合
urgency_score: 紧急度评分
assigned_agent_id: 分配的坐席ID
last_message_summary: 最后消息摘要
"""
employee_name: Optional[str] = Field(None, max_length=128, description="员工姓名")
department: Optional[str] = Field(None, max_length=256, description="部门")
position: Optional[str] = Field(None, max_length=128, description="岗位")
level: Optional[str] = Field(None, max_length=64, description="等级")
is_vip: Optional[bool] = Field(None, description="VIP标记")
tags: Optional[ConversationTags] = Field(None, description="标签集合")
urgency_score: Optional[int] = Field(None, ge=1, le=5, description="紧急度1-5")
assigned_agent_id: Optional[str] = Field(None, max_length=64, description="分配的坐席ID")
last_message_summary: Optional[str] = Field(None, max_length=256, description="最后消息摘要")
# --------------------------------------------------------------------------
# 更新会话状态 Schema
# --------------------------------------------------------------------------
class ConversationStatusUpdate(BaseModel):
"""更新会话状态请求 Schema。
Attributes:
status: 新的会话状态
"""
status: str = Field(..., description="会话状态: ai_handling/queued/serving/resolved")
@field_validator("status")
@classmethod
def validate_status(cls, v: str) -> str:
"""校验会话状态值是否合法。"""
if v not in VALID_STATUSES:
raise ValueError(f"无效的会话状态: {v},合法值为: {VALID_STATUSES}")
return v
# --------------------------------------------------------------------------
# 坐席接单 Schema
# --------------------------------------------------------------------------
class ConversationAssign(BaseModel):
"""坐席接单请求 Schema。
Attributes:
agent_id: 接单的坐席ID
"""
agent_id: str = Field(..., min_length=1, max_length=64, description="坐席ID")
# --------------------------------------------------------------------------
# 摇人(邀请协作)Schema
# --------------------------------------------------------------------------
class ConversationInvite(BaseModel):
"""摇人邀请请求 Schema。
Attributes:
agent_id: 被邀请的坐席ID
"""
agent_id: str = Field(..., min_length=1, max_length=64, description="被邀请的坐席ID")
# --------------------------------------------------------------------------
# 邀请员工/部门加入会话 SchemaP0-09~P0-11 邀请功能)
# --------------------------------------------------------------------------
class ParticipantInfo(BaseModel):
"""被邀请人信息。
Attributes:
id: 企微员工UserID 或部门ID
name: 姓名 或 部门名称
department: 部门(仅员工类型有)
type: 类型 — employee(个人)或 department(部门)
avatar: 头像URL(从企微通讯录或employees表获取)
joined: 是否已加入(邀请后、点击加入前为 False)
joined_at: 加入时间(ISO 格式字符串)
"""
id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID或部门ID")
name: str = Field(..., min_length=1, max_length=128, description="姓名或部门名称")
department: str = Field(default="", max_length=256, description="部门(仅员工类型)")
type: str = Field(default="employee", description="类型: employee/department")
avatar: str = Field(default="", max_length=512, description="头像URL")
joined: Optional[bool] = Field(default=None, description="是否已加入")
joined_at: Optional[str] = Field(default=None, description="加入时间(ISO格式)")
class InviteParticipantRequest(BaseModel):
"""邀请员工/部门加入会话请求 Schema。
Attributes:
participants: 被邀请人列表
history_mode: 历史消息共享模式 — recent10(最近10条)/ all(全部)/ none(不共享)
"""
participants: List[ParticipantInfo] = Field(
..., min_length=1, max_length=20, description="被邀请人列表"
)
history_mode: str = Field(
default="recent10",
description="历史消息共享模式: recent10/all/none",
)
@field_validator("history_mode")
@classmethod
def validate_history_mode(cls, v: str) -> str:
"""校验历史共享模式。"""
valid_modes = {"recent10", "all", "none"}
if v not in valid_modes:
raise ValueError(f"无效的历史共享模式: {v},合法值为: {valid_modes}")
return v
@field_validator("participants")
@classmethod
def validate_participants_unique(cls, v: List[ParticipantInfo]) -> List[ParticipantInfo]:
"""校验参与者ID不重复。"""
ids = [p.id for p in v]
if len(ids) != len(set(ids)):
raise ValueError("参与者ID不能重复")
return v
class JoinConversationRequest(BaseModel):
"""被邀请人加入会话请求 Schema。
Attributes:
employee_id: 被邀请人的企微UserID
"""
employee_id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID")
# --------------------------------------------------------------------------
# 会话响应 Schema(返回给前端的数据结构)
# --------------------------------------------------------------------------
class ConversationResponse(BaseModel):
"""会话响应 Schema。
返回给前端(坐席端/H5端)的会话数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 会话ID
employee_id: 企微员工UserID
employee_name: 员工姓名
department: 部门
position: 岗位
level: 等级
status: 会话状态
is_vip: VIP标记
is_pinned: 置顶标记
is_todo: 代办标记
urgency_score: 紧急度评分
tags: 标签集合
assigned_agent_id: 分配的坐席ID
collaborating_agent_ids: 协作坐席ID列表
last_message_at: 最后消息时间
last_message_summary: 最后消息摘要
created_at: 创建时间
updated_at: 更新时间
"""
id: str
employee_id: str
employee_name: str
department: str
position: str
level: str
status: str
is_vip: bool
is_pinned: bool
is_todo: bool
urgency_score: int
tags: Dict[str, Any]
assigned_agent_id: Optional[str] = None
collaborating_agent_ids: List[str] = Field(default_factory=list, description="协作坐席ID列表")
# 被邀请参与会话的人员列表(邀请功能 P0-09~P0-11
participants: List[ParticipantInfo] = Field(default_factory=list, description="被邀请参与会话的人员列表")
last_message_at: Optional[datetime] = None
last_message_summary: str
created_at: datetime
updated_at: datetime
# ----- 坐席会话全局可见扩展字段 -----
# 是否为当前坐席的会话
is_mine: bool = Field(default=False, description="是否为当前坐席的会话")
# 分配的坐席姓名(其他坐席会话显示用)
assigned_agent_name: Optional[str] = Field(default=None, description="分配的坐席姓名")
# 是否可以接手(其他坐席已接单的会话为 True)
can_grab: bool = Field(default=False, description="是否可以接手")
# ----- 多坐席协作扩展字段 -----
# 协作坐席姓名映射(agent_id → name
collaborating_agent_names: Dict[str, str] = Field(
default_factory=dict, description="协作坐席姓名映射"
)
# 当前坐席是否为协作坐席(非主责)
is_collaborator: bool = Field(default=False, description="是否为协作坐席")
# ----- v5.3 新增:影响范围 / 阻断性 / 情绪状态 -----
# 影响范围(受影响人数,0=未评估)
impact_scope: int = Field(default=0, description="影响范围")
# 阻断性标记(问题是否阻断员工正常工作流程)
is_blocking: bool = Field(default=False, description="阻断性标记")
# 情绪状态(normal/worried/angry/urgent
emotion_state: str = Field(default="normal", description="情绪状态")
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 会话列表响应 Schema(包含分页信息)
# --------------------------------------------------------------------------
class ConversationListResponse(BaseModel):
"""会话列表响应 Schema。
包含会话列表和总数,用于分页查询。
Attributes:
items: 会话列表
total: 总数
"""
items: List[ConversationResponse]
total: int
+118
View File
@@ -0,0 +1,118 @@
# =============================================================================
# 企微IT智能服务台 — 员工 Pydantic Schema
# =============================================================================
# 说明:定义员工相关的请求/响应数据结构
# 包含:IT等级更新请求、员工响应 Schema
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# IT 等级合法值
# --------------------------------------------------------------------------
VALID_IT_LEVELS = {"bronze", "silver", "gold", "platinum", "diamond", "star", "king"}
# --------------------------------------------------------------------------
# 等级来源合法值
# --------------------------------------------------------------------------
VALID_LEVEL_SOURCES = {"system", "manual", "assessment"}
# --------------------------------------------------------------------------
# IT 等级更新请求 Schema
# --------------------------------------------------------------------------
class ItLevelUpdateRequest(BaseModel):
"""IT技能等级更新请求 Schema。
坐席手动调整员工IT技能等级时使用。
Attributes:
it_level: 新的IT技能等级
source: 等级来源(默认 manual
"""
it_level: str = Field(..., description="IT技能等级: bronze/silver/gold/platinum/diamond/star/king")
source: str = Field(default="manual", description="等级来源: system/manual/assessment")
@field_validator("it_level")
@classmethod
def validate_it_level(cls, v: str) -> str:
"""校验IT等级值是否合法。"""
if v not in VALID_IT_LEVELS:
raise ValueError(f"无效的IT等级: {v},合法值为: {VALID_IT_LEVELS}")
return v
@field_validator("source")
@classmethod
def validate_source(cls, v: str) -> str:
"""校验等级来源值是否合法。"""
if v not in VALID_LEVEL_SOURCES:
raise ValueError(f"无效的等级来源: {v},合法值为: {VALID_LEVEL_SOURCES}")
return v
# --------------------------------------------------------------------------
# 员工响应 Schema(返回给前端的数据结构)
# --------------------------------------------------------------------------
class EmployeeResponse(BaseModel):
"""员工响应 Schema。
返回给前端的员工数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 员工记录唯一标识
corp_id: 企业微信企业ID
employee_id: 企微员工UserID
name: 员工姓名
department: 部门
position: 岗位
mobile: 手机号
email: 邮箱
avatar: 头像URL
status: 激活状态
it_level: IT技能等级
it_level_source: 等级来源
notes: 坐席备注
last_login_at: 最后登录时间
created_at: 创建时间
updated_at: 更新时间
"""
id: str
corp_id: str
employee_id: str
name: str
department: str
position: str
mobile: str
email: str
avatar: str
status: int
it_level: str = "silver"
it_level_source: str = "system"
notes: Dict[str, Any] = Field(default_factory=dict, description="坐席备注")
last_login_at: Optional[datetime] = None
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 员工列表响应 Schema
# --------------------------------------------------------------------------
class EmployeeListResponse(BaseModel):
"""员工列表响应 Schema。
Attributes:
items: 员工列表
total: 总数
"""
items: List[EmployeeResponse]
total: int
+209
View File
@@ -0,0 +1,209 @@
# =============================================================================
# 企微IT智能服务台 — H5 用户端 Pydantic Schema
# =============================================================================
# 说明:定义H5用户端专用的请求/响应数据结构
# 包含:摇人请求、OAuth回调、审批链接、软件下载、员工信息等
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# --------------------------------------------------------------------------
# 摇人请求 Schema
# --------------------------------------------------------------------------
class ShakeRequest(BaseModel):
"""摇人请求 Schema。
用户点击H5页面摇人按钮时发送的请求。
Attributes:
employee_id: 企微员工UserID
employee_name: 员工姓名
"""
employee_id: str = Field(..., min_length=1, max_length=64, description="企微员工UserID")
employee_name: str = Field(default="", max_length=128, description="员工姓名")
# --------------------------------------------------------------------------
# 摇人响应 Schema
# --------------------------------------------------------------------------
class ShakeResponse(BaseModel):
"""摇人响应 Schema。
摇人成功后返回会话信息和趣味话术。
Attributes:
conversation: 会话信息(包含ID、状态、标签)
funny_phrase: 趣味话术内容
"""
conversation: Dict[str, Any] = Field(..., description="会话信息")
funny_phrase: str = Field(..., description="趣味话术")
# --------------------------------------------------------------------------
# OAuth2 回调请求 Schema
# --------------------------------------------------------------------------
class OAuthCallbackRequest(BaseModel):
"""OAuth2 回调请求 Schema。
H5页面通过企微OAuth2授权后,将code传给后端换取员工身份。
Attributes:
code: 企微OAuth2授权码
"""
code: str = Field(..., min_length=1, description="企微OAuth2授权码")
# --------------------------------------------------------------------------
# OAuth2 回调响应 Schema
# --------------------------------------------------------------------------
class OAuthCallbackResponse(BaseModel):
"""OAuth2 回调响应 Schema。
用授权码换取到的员工身份信息和访问令牌。
Attributes:
employee_id: 企微员工UserID
employee_name: 员工姓名
token: 访问令牌(用于后续API请求的Bearer Token
department: 部门名称
position: 岗位
avatar: 头像URL
"""
employee_id: str = Field(..., description="企微员工UserID")
employee_name: str = Field(default="", description="员工姓名")
token: str = Field(..., description="访问令牌")
department: str = Field(default="", description="部门名称")
position: str = Field(default="", description="岗位")
avatar: str = Field(default="", description="头像URL")
# --------------------------------------------------------------------------
# OAuth2 授权URL响应 Schema
# --------------------------------------------------------------------------
class OAuthAuthorizeResponse(BaseModel):
"""OAuth2 授权URL响应 Schema。
返回企微OAuth2授权链接,前端跳转到此URL进行授权。
Attributes:
authorize_url: 企微OAuth2授权URL
"""
authorize_url: str = Field(..., description="企微OAuth2授权URL")
# --------------------------------------------------------------------------
# 员工信息 Schema
# --------------------------------------------------------------------------
class EmployeeInfo(BaseModel):
"""员工信息 Schema。
从企微通讯录获取的员工详细信息。
Attributes:
employee_id: 企微员工UserID
employee_name: 员工姓名
department: 部门名称(逗号分隔)
position: 岗位
mobile: 手机号
email: 邮箱
avatar: 头像URL
is_vip: 是否VIP员工
"""
employee_id: str = Field(..., description="企微员工UserID")
employee_name: str = Field(default="", description="员工姓名")
department: str = Field(default="", description="部门名称")
position: str = Field(default="", description="岗位")
mobile: str = Field(default="", description="手机号")
email: str = Field(default="", description="邮箱")
avatar: str = Field(default="", description="头像URL")
is_vip: bool = Field(default=False, description="是否VIP员工")
# --------------------------------------------------------------------------
# 审批链接响应 Schema
# --------------------------------------------------------------------------
class ApprovalLinkResponse(BaseModel):
"""审批链接响应 Schema。
H5用户端AI助手面板中的审批流程链接。
Attributes:
id: 链接ID
category: 分类
title: 审批名称
url: 审批链接
sort_order: 排序权重
"""
id: str
category: str
title: str
url: str
sort_order: int
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 软件下载响应 Schema
# --------------------------------------------------------------------------
class SoftwareDownloadResponse(BaseModel):
"""软件下载响应 Schema。
H5用户端AI助手面板中的软件下载入口。
Attributes:
id: 下载入口ID
category: 分类
name: 软件名称
version: 版本号
platform: 平台
download_url: 下载链接
sort_order: 排序权重
"""
id: str
category: str
name: str
version: str
platform: str
download_url: str
sort_order: int
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 审批链接列表响应 Schema
# --------------------------------------------------------------------------
class ApprovalLinkListResponse(BaseModel):
"""审批链接列表响应 Schema。
Attributes:
items: 审批链接列表
"""
items: List[ApprovalLinkResponse]
# --------------------------------------------------------------------------
# 软件下载列表响应 Schema
# --------------------------------------------------------------------------
class SoftwareDownloadListResponse(BaseModel):
"""软件下载列表响应 Schema。
Attributes:
items: 软件下载列表
"""
items: List[SoftwareDownloadResponse]
+145
View File
@@ -0,0 +1,145 @@
# =============================================================================
# 企微IT智能服务台 — 消息 Pydantic Schema
# =============================================================================
# 说明:定义消息相关的请求/响应数据结构
# 支持消息类型:文本(text)/图片(image)/文件(file)/系统(system)
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# 消息类型和发送者类型的合法值
# --------------------------------------------------------------------------
VALID_MSG_TYPES = {"text", "image", "file", "system"}
VALID_SENDER_TYPES = {"employee", "agent", "ai", "system"}
# --------------------------------------------------------------------------
# 创建消息 Schema(坐席发送消息时使用)
# --------------------------------------------------------------------------
class MessageCreate(BaseModel):
"""创建消息请求 Schema。
坐席发送消息时使用。
支持文本消息和文件/图片消息。
Attributes:
content: 消息内容(文本消息为正文,文件消息为文件URL或描述)
msg_type: 消息类型(默认 text,支持 image/file
media_url: 媒体文件URL(图片/文件消息时使用)
file_name: 文件名(文件消息时使用)
file_size: 文件大小(字节,文件消息时使用)
"""
content: str = Field(..., min_length=1, description="消息内容")
# 支持文本、图片、文件类型
msg_type: str = Field(default="text", description="消息类型: text/image/file")
# M1 新增:文件上传相关字段
media_url: Optional[str] = Field(None, description="媒体文件URL(图片/文件消息时使用)")
file_name: Optional[str] = Field(None, description="文件名")
file_size: Optional[int] = Field(None, description="文件大小(字节)")
# M1 新增:引用回复
reply_to_id: Optional[str] = Field(None, description="引用回复:被回复的消息ID")
@field_validator("msg_type")
@classmethod
def validate_msg_type(cls, v: str) -> str:
"""校验消息类型是否合法。"""
if v not in VALID_MSG_TYPES:
raise ValueError(f"无效的消息类型: {v},合法值为: {VALID_MSG_TYPES}")
return v
# --------------------------------------------------------------------------
# 企微回调消息 Schema(从企微接收到的消息)
# --------------------------------------------------------------------------
class WecomInboundMessage(BaseModel):
"""企微回调消息 Schema。
解析企微回调 XML 后得到的结构化消息。
Attributes:
from_user_id: 发送者企微UserID
content: 消息内容
msg_type: 消息类型(text/image等)
create_time: 消息创建时间戳
agent_id: 应用AgentID
"""
from_user_id: str = Field(..., description="发送者企微UserID")
content: str = Field(default="", description="消息内容")
msg_type: str = Field(default="text", description="消息类型")
create_time: Optional[int] = Field(None, description="消息创建时间戳")
agent_id: Optional[str] = Field(None, description="应用AgentID")
# --------------------------------------------------------------------------
# 消息响应 Schema(返回给前端的数据结构)
# --------------------------------------------------------------------------
class MessageResponse(BaseModel):
"""消息响应 Schema。
返回给前端的消息数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 消息ID
conversation_id: 所属会话ID
sender_type: 发送者类型
sender_id: 发送者ID
sender_name: 发送者姓名
content: 消息内容
msg_type: 消息类型
media_url: 媒体文件URL
file_name: 文件名
file_size: 文件大小
extra_data: 扩展元数据
ai_suggestion: 是否为AI建议
is_read: 是否已读
created_at: 创建时间
"""
id: str
conversation_id: str
sender_type: str
sender_id: str
sender_name: str
content: str
msg_type: str
# M1 新增:媒体/文件相关字段
media_url: Optional[str] = None
file_name: Optional[str] = None
file_size: Optional[int] = None
extra_data: Optional[Dict[str, Any]] = None
# M1 新增:引用回复
reply_to_id: Optional[str] = None
ai_suggestion: bool
is_read: bool
created_at: datetime
# M2 新增:消息状态和可撤回时间
status: str = "sent"
recallable_until: Optional[datetime] = None
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 消息列表响应 Schema
# --------------------------------------------------------------------------
class MessageListResponse(BaseModel):
"""消息列表响应 Schema。
包含消息列表和是否还有更多消息的标志,
用于向上加载历史消息。
Attributes:
items: 消息列表
has_more: 是否还有更多历史消息
"""
items: List[MessageResponse]
has_more: bool
+106
View File
@@ -0,0 +1,106 @@
# =============================================================================
# 企微IT智能服务台 — 快速回复模板 Pydantic Schema
# =============================================================================
# 说明:定义快速回复模板的请求/响应数据结构
# 支持 CRUD 操作:创建、读取、更新、删除
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# --------------------------------------------------------------------------
# 创建快速回复模板 Schema
# --------------------------------------------------------------------------
class QuickReplyCreate(BaseModel):
"""创建快速回复模板请求 Schema。
Attributes:
category: 分类(账号/网络/软件/硬件/通用)
title: 模板标题
content: 模板内容(支持 {employee_name} 等变量)
variables: 可用变量列表
sort_order: 排序权重
"""
category: str = Field(default="通用", max_length=64, description="分类")
title: str = Field(..., min_length=1, max_length=128, description="模板标题")
content: str = Field(..., min_length=1, description="模板内容")
variables: List[str] = Field(default_factory=list, description="可用变量列表")
sort_order: int = Field(default=0, description="排序权重")
# --------------------------------------------------------------------------
# 更新快速回复模板 Schema
# --------------------------------------------------------------------------
class QuickReplyUpdate(BaseModel):
"""更新快速回复模板请求 Schema。
所有字段可选,只更新传入的字段。
Attributes:
category: 分类
title: 模板标题
content: 模板内容
variables: 可用变量列表
sort_order: 排序权重
"""
category: Optional[str] = Field(None, max_length=64, description="分类")
title: Optional[str] = Field(None, max_length=128, description="模板标题")
content: Optional[str] = Field(None, description="模板内容")
variables: Optional[List[str]] = Field(None, description="可用变量列表")
sort_order: Optional[int] = Field(None, description="排序权重")
# --------------------------------------------------------------------------
# 快速回复模板响应 Schema
# --------------------------------------------------------------------------
class QuickReplyResponse(BaseModel):
"""快速回复模板响应 Schema。
返回给前端的快速回复模板数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 模板ID
category: 分类
title: 模板标题
content: 模板内容
variables: 可用变量列表
sort_order: 排序权重
status: 状态(draft/pending_review/approved/rejected
version: 版本号
submitted_by: 提交人 agent_id
created_at: 创建时间
updated_at: 更新时间
"""
id: str
category: str
title: str
content: str
variables: List[str]
sort_order: int
status: str = "approved"
version: int = 1
submitted_by: Optional[str] = None
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 快速回复模板列表响应 Schema
# --------------------------------------------------------------------------
class QuickReplyListResponse(BaseModel):
"""快速回复模板列表响应 Schema。
Attributes:
items: 模板列表
"""
items: List[QuickReplyResponse]
+239
View File
@@ -0,0 +1,239 @@
# =============================================================================
# 角色 Pydantic Schema
# =============================================================================
# 说明:定义角色相关的请求/响应数据结构
# 包含:角色响应、角色分配、角色映射规则等 Schema
# =============================================================================
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# 角色响应 Schema
# --------------------------------------------------------------------------
class RoleResponse(BaseModel):
"""角色响应 Schema。
Attributes:
id: 角色ID
name: 角色标识(user/agent/admin
display_name: 显示名称(用户/坐席/管理员)
description: 角色描述
permissions: 权限列表
is_default: 是否默认角色
user_count: 拥有该角色的用户数(可选)
created_at: 创建时间
updated_at: 更新时间
"""
id: str
name: str
display_name: str
description: Optional[str] = None
permissions: List[str] = []
is_default: bool = False
user_count: Optional[int] = None
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 用户角色响应 Schema
# --------------------------------------------------------------------------
class UserRoleResponse(BaseModel):
"""用户角色响应 Schema。
Attributes:
id: 记录ID
employee_id: 企微 UserID
role_id: 角色 ID
role_name: 角色标识
role_display_name: 角色显示名称
source: 角色来源(auto/tag/ehr/manual
assigned_by: 分配者
assigned_at: 分配时间
expires_at: 过期时间
"""
id: str
employee_id: str
role_id: str
role_name: str
role_display_name: str
source: str
assigned_by: Optional[str] = None
assigned_at: datetime
expires_at: Optional[datetime] = None
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 角色分配请求 Schema
# --------------------------------------------------------------------------
class RoleAssignRequest(BaseModel):
"""角色分配请求 Schema。
Attributes:
employee_id: 企微 UserID
role_name: 角色标识(user/agent/admin
reason: 分配原因(可选)
"""
employee_id: str = Field(..., min_length=1, max_length=100, description="企微 UserID")
role_name: str = Field(..., min_length=1, max_length=50, description="角色标识")
reason: Optional[str] = Field(None, max_length=500, description="分配原因")
# --------------------------------------------------------------------------
# 角色撤销请求 Schema
# --------------------------------------------------------------------------
class RoleRevokeRequest(BaseModel):
"""角色撤销请求 Schema。
Attributes:
employee_id: 企微 UserID
role_name: 角色标识(user/agent/admin
reason: 撤销原因(可选)
"""
employee_id: str = Field(..., min_length=1, max_length=100, description="企微 UserID")
role_name: str = Field(..., min_length=1, max_length=50, description="角色标识")
reason: Optional[str] = Field(None, max_length=500, description="撤销原因")
# --------------------------------------------------------------------------
# 角色映射规则响应 Schema
# --------------------------------------------------------------------------
class RoleMappingRuleResponse(BaseModel):
"""角色映射规则响应 Schema。
Attributes:
id: 规则ID
role_id: 目标角色 ID
role_name: 目标角色标识
source_type: 来源类型(wecom_tag/ehr_position
source_value: 来源值(标签名/岗位关键词)
priority: 优先级
is_active: 是否启用
created_at: 创建时间
"""
id: str
role_id: str
role_name: str
source_type: str
source_value: str
priority: int = 0
is_active: bool = True
created_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 角色映射规则创建/更新请求 Schema
# --------------------------------------------------------------------------
class RoleMappingRuleRequest(BaseModel):
"""角色映射规则创建/更新请求 Schema。
Attributes:
role_name: 目标角色标识(user/agent/admin
source_type: 来源类型(wecom_tag/ehr_position
source_value: 来源值(标签名/岗位关键词)
priority: 优先级(数值越大优先级越高)
is_active: 是否启用
"""
role_name: str = Field(..., min_length=1, max_length=50, description="目标角色标识")
source_type: str = Field(..., min_length=1, max_length=50, description="来源类型")
source_value: str = Field(..., min_length=1, max_length=200, description="来源值")
priority: int = Field(0, ge=0, le=100, description="优先级(0-100")
is_active: bool = Field(True, description="是否启用")
@field_validator("source_type")
@classmethod
def validate_source_type(cls, v: str) -> str:
"""校验来源类型是否合法。"""
allowed_types = {"wecom_tag", "ehr_position"}
if v not in allowed_types:
raise ValueError(f"无效的来源类型: {v},合法值为: {allowed_types}")
return v
@field_validator("role_name")
@classmethod
def validate_role_name(cls, v: str) -> str:
"""校验角色标识是否合法。"""
allowed_roles = {"user", "agent", "admin"}
if v not in allowed_roles:
raise ValueError(f"无效的角色标识: {v},合法值为: {allowed_roles}")
return v
@field_validator("source_value")
@classmethod
def validate_source_value(cls, v: str) -> str:
"""校验来源值是否包含恶意内容。"""
# 过滤特殊字符
forbidden_chars = {"<", ">", ";", "'", '"', "\\", "/", "(", ")"}
for char in v:
if char in forbidden_chars:
raise ValueError(f"来源值包含非法字符: {char}")
return v
# --------------------------------------------------------------------------
# Portal 用户信息响应 Schema
# --------------------------------------------------------------------------
class PortalUserInfo(BaseModel):
"""Portal 用户信息响应 Schema。
用于路由选择页展示用户信息和角色列表。
Attributes:
employee_id: 企微 UserID
name: 姓名
department: 部门
avatar: 头像URL
roles: 角色列表
current_role: 当前选择的角色
"""
employee_id: str
name: str
department: Optional[str] = None
avatar: Optional[str] = None
roles: List[RoleResponse] = []
current_role: str = "user"
# --------------------------------------------------------------------------
# 角色切换请求 Schema
# --------------------------------------------------------------------------
class SwitchRoleRequest(BaseModel):
"""角色切换请求 Schema。
Attributes:
new_role: 目标角色标识
"""
new_role: str = Field(..., min_length=1, max_length=50, description="目标角色标识")
# --------------------------------------------------------------------------
# 角色切换响应 Schema
# --------------------------------------------------------------------------
class SwitchRoleResponse(BaseModel):
"""角色切换响应 Schema。
Attributes:
current_role: 切换后的角色标识
redirect_url: 重定向URL
"""
current_role: str
redirect_url: str
+158
View File
@@ -0,0 +1,158 @@
# =============================================================================
# 企微IT智能服务台 — 待办事项 Pydantic Schema
# =============================================================================
# 说明:定义待办事项的 CRUD 数据结构
# 包含:创建、更新、响应 Schema
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# 待办类型和优先级的合法值
# --------------------------------------------------------------------------
VALID_TODO_TYPES = {"ticket", "approval", "device"}
VALID_TODO_PRIORITIES = {"urgent", "high", "normal"}
VALID_TODO_STATUSES = {"pending", "processing", "resolved"}
# --------------------------------------------------------------------------
# 创建待办事项 Schema
# --------------------------------------------------------------------------
class TodoItemCreate(BaseModel):
"""创建待办事项请求 Schema。
Attributes:
type: 待办类型(ticket/approval/device
title: 待办标题
priority: 优先级(urgent/high/normal
description: 详细描述(JSON
assigned_agent_id: 分配的坐席ID(可选)
corp_id: 企业微信企业ID
"""
type: str = Field(default="ticket", description="待办类型: ticket/approval/device")
title: str = Field(..., min_length=1, max_length=256, description="待办标题")
priority: str = Field(default="normal", description="优先级: urgent/high/normal")
description: Dict[str, Any] = Field(default_factory=dict, description="详细描述")
assigned_agent_id: Optional[str] = Field(None, max_length=64, description="分配的坐席ID")
corp_id: str = Field(default="", max_length=64, description="企业微信企业ID")
@field_validator("type")
@classmethod
def validate_type(cls, v: str) -> str:
"""校验待办类型是否合法。"""
if v not in VALID_TODO_TYPES:
raise ValueError(f"无效的待办类型: {v},合法值为: {VALID_TODO_TYPES}")
return v
@field_validator("priority")
@classmethod
def validate_priority(cls, v: str) -> str:
"""校验优先级是否合法。"""
if v not in VALID_TODO_PRIORITIES:
raise ValueError(f"无效的优先级: {v},合法值为: {VALID_TODO_PRIORITIES}")
return v
# --------------------------------------------------------------------------
# 更新待办事项 Schema
# --------------------------------------------------------------------------
class TodoItemUpdate(BaseModel):
"""更新待办事项请求 Schema。
所有字段可选,只更新传入的字段。
Attributes:
type: 待办类型
title: 待办标题
priority: 优先级
description: 详细描述
status: 状态
assigned_agent_id: 分配的坐席ID
"""
type: Optional[str] = Field(None, description="待办类型: ticket/approval/device")
title: Optional[str] = Field(None, max_length=256, description="待办标题")
priority: Optional[str] = Field(None, description="优先级: urgent/high/normal")
description: Optional[Dict[str, Any]] = Field(None, description="详细描述")
status: Optional[str] = Field(None, description="状态: pending/processing/resolved")
assigned_agent_id: Optional[str] = Field(None, max_length=64, description="分配的坐席ID")
@field_validator("type")
@classmethod
def validate_type(cls, v: Optional[str]) -> Optional[str]:
"""校验待办类型是否合法。"""
if v is not None and v not in VALID_TODO_TYPES:
raise ValueError(f"无效的待办类型: {v},合法值为: {VALID_TODO_TYPES}")
return v
@field_validator("priority")
@classmethod
def validate_priority(cls, v: Optional[str]) -> Optional[str]:
"""校验优先级是否合法。"""
if v is not None and v not in VALID_TODO_PRIORITIES:
raise ValueError(f"无效的优先级: {v},合法值为: {VALID_TODO_PRIORITIES}")
return v
@field_validator("status")
@classmethod
def validate_status(cls, v: Optional[str]) -> Optional[str]:
"""校验状态是否合法。"""
if v is not None and v not in VALID_TODO_STATUSES:
raise ValueError(f"无效的状态: {v},合法值为: {VALID_TODO_STATUSES}")
return v
# --------------------------------------------------------------------------
# 待办事项响应 Schema
# --------------------------------------------------------------------------
class TodoItemResponse(BaseModel):
"""待办事项响应 Schema。
返回给前端的待办事项数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 待办唯一标识
type: 待办类型
title: 待办标题
priority: 优先级
description: 详细描述
status: 状态
assigned_agent_id: 分配的坐席ID
corp_id: 企业微信企业ID
created_at: 创建时间
updated_at: 更新时间
"""
id: str
type: str
title: str
priority: str
description: Dict[str, Any]
status: str
assigned_agent_id: Optional[str] = None
corp_id: str
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 待办事项列表响应 Schema
# --------------------------------------------------------------------------
class TodoItemListResponse(BaseModel):
"""待办事项列表响应 Schema。
Attributes:
items: 待办事项列表
total: 总数
"""
items: List[TodoItemResponse]
total: int
@@ -0,0 +1,128 @@
# =============================================================================
# 企微IT智能服务台 — 排障模板 Pydantic Schema
# =============================================================================
# 说明:定义排障模板的 CRUD 数据结构
# 包含:创建、更新、响应 Schema
# =============================================================================
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
# --------------------------------------------------------------------------
# 排障模板分类合法值
# --------------------------------------------------------------------------
VALID_TEMPLATE_CATEGORIES = {"vpn", "email", "system", "account"}
# --------------------------------------------------------------------------
# 创建排障模板 Schema
# --------------------------------------------------------------------------
class TroubleshootingTemplateCreate(BaseModel):
"""创建排障模板请求 Schema。
Attributes:
name: 模板名称
category: 分类(vpn/email/system/account
path_steps: 排障步骤路径
flowchart: 流程图定义
is_active: 是否启用
"""
name: str = Field(..., min_length=1, max_length=256, description="模板名称")
category: str = Field(default="system", description="分类: vpn/email/system/account")
path_steps: List[Dict[str, Any]] = Field(
default_factory=list, description="排障步骤路径"
)
flowchart: Dict[str, Any] = Field(
default_factory=dict, description="流程图定义"
)
is_active: bool = Field(default=True, description="是否启用")
@field_validator("category")
@classmethod
def validate_category(cls, v: str) -> str:
"""校验分类是否合法。"""
if v not in VALID_TEMPLATE_CATEGORIES:
raise ValueError(f"无效的分类: {v},合法值为: {VALID_TEMPLATE_CATEGORIES}")
return v
# --------------------------------------------------------------------------
# 更新排障模板 Schema
# --------------------------------------------------------------------------
class TroubleshootingTemplateUpdate(BaseModel):
"""更新排障模板请求 Schema。
所有字段可选,只更新传入的字段。
Attributes:
name: 模板名称
category: 分类
path_steps: 排障步骤路径
flowchart: 流程图定义
is_active: 是否启用
"""
name: Optional[str] = Field(None, max_length=256, description="模板名称")
category: Optional[str] = Field(None, description="分类: vpn/email/system/account")
path_steps: Optional[List[Dict[str, Any]]] = Field(None, description="排障步骤路径")
flowchart: Optional[Dict[str, Any]] = Field(None, description="流程图定义")
is_active: Optional[bool] = Field(None, description="是否启用")
@field_validator("category")
@classmethod
def validate_category(cls, v: Optional[str]) -> Optional[str]:
"""校验分类是否合法。"""
if v is not None and v not in VALID_TEMPLATE_CATEGORIES:
raise ValueError(f"无效的分类: {v},合法值为: {VALID_TEMPLATE_CATEGORIES}")
return v
# --------------------------------------------------------------------------
# 排障模板响应 Schema
# --------------------------------------------------------------------------
class TroubleshootingTemplateResponse(BaseModel):
"""排障模板响应 Schema。
返回给前端的排障模板数据结构。
使用 from_attributes=True 支持从 SQLAlchemy 模型直接转换。
Attributes:
id: 模板唯一标识
name: 模板名称
category: 分类
path_steps: 排障步骤路径
flowchart: 流程图定义
is_active: 是否启用
created_at: 创建时间
updated_at: 更新时间
"""
id: str
name: str
category: str
path_steps: List[Dict[str, Any]] = Field(default_factory=list, description="排障步骤路径")
flowchart: Dict[str, Any] = Field(default_factory=dict, description="流程图定义")
is_active: bool
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
# --------------------------------------------------------------------------
# 排障模板列表响应 Schema
# --------------------------------------------------------------------------
class TroubleshootingTemplateListResponse(BaseModel):
"""排障模板列表响应 Schema。
Attributes:
items: 排障模板列表
total: 总数
"""
items: List[TroubleshootingTemplateResponse]
total: int
+79
View File
@@ -0,0 +1,79 @@
# =============================================================================
# 企微IT智能服务台 — 企微回调消息 Pydantic Schema
# =============================================================================
# 说明:定义企微回调消息的数据结构
# 包含:GET验证请求、POST消息体、加解密相关
# =============================================================================
from typing import Optional
from pydantic import BaseModel, Field
# --------------------------------------------------------------------------
# 企微回调验证请求 Schema(GET 请求)
# --------------------------------------------------------------------------
class WecomCallbackVerify(BaseModel):
"""企微回调URL验证请求 Schema。
企微管理后台配置回调URL时,会发送GET请求验证。
需要验证签名并返回解密后的 echostr。
Attributes:
msg_signature: 企微签名(用于验证请求来源)
timestamp: 时间戳
nonce: 随机数
echostr: 加密的验证字符串(解密后返回给企微)
"""
msg_signature: str = Field(..., description="企微签名")
timestamp: str = Field(..., description="时间戳")
nonce: str = Field(..., description="随机数")
echostr: str = Field(..., description="加密的验证字符串")
# --------------------------------------------------------------------------
# 企微回调消息体 Schema(POST 请求解析后)
# --------------------------------------------------------------------------
class WecomCallbackMessage(BaseModel):
"""企微回调消息体 Schema。
企微推送消息时发送的XML解析后的结构。
包含加密的消息内容。
Attributes:
to_user_name: 接收方(企业ID
agent_id: 应用AgentID
encrypt: 加密的消息内容
"""
to_user_name: str = Field(default="", description="接收方企业ID")
agent_id: str = Field(default="", description="应用AgentID")
encrypt: str = Field(..., description="加密的消息内容")
# --------------------------------------------------------------------------
# 企微消息内容 Schema(解密后的消息)
# --------------------------------------------------------------------------
class WecomMessageContent(BaseModel):
"""企微消息内容 Schema(解密后)。
AES解密后的XML消息解析结果。
Attributes:
to_user_name: 接收方
from_user_name: 发送者企微UserID
create_time: 消息创建时间戳
msg_type: 消息类型(text/image等)
content: 消息内容
msg_id: 消息ID
agent_id: 应用AgentID
"""
to_user_name: str = Field(default="", description="接收方")
from_user_name: str = Field(..., description="发送者企微UserID")
create_time: int = Field(default=0, description="消息创建时间戳")
msg_type: str = Field(default="text", description="消息类型")
content: str = Field(default="", description="消息内容")
msg_id: str = Field(default="", description="消息ID")
agent_id: str = Field(default="", description="应用AgentID")