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
+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