# 业务路由推荐 — 类图 > 数据模型 + 服务类 + API 类的完整定义 ```mermaid classDiagram direction LR %% ===== 数据模型 ===== class BusinessContact { +id: int +name: str +gender: str +department: str +position: str +responsibility: str +extension: str +service_area: str +wecom_userid: str +avatar_url: str +business_category: str +is_active: bool +created_at: datetime +updated_at: datetime } class RoutingEvent { +id: int +conversation_id: str +employee_id: str +message_content: str +business_category: str +routing_confidence: float +contact_id: int +contact_name: str +is_clicked: bool +created_at: datetime } %% ===== 服务类 ===== class RoutingService { -ROUTING_PREFILTER_KEYWORDS: list~str~ -ROUTING_KEYWORD_TO_CATEGORY: dict~str, str~ -ROUTING_CONFIDENCE_THRESHOLD: float +keyword_prefilter(text: str) bool +async detect_routing_intent(text: str, employee_id: str) dict -async _call_dify_unified_intent(text: str, employee_id: str) dict -_fallback_routing_detect(text: str) tuple +async get_contact_by_category(category: str, db: AsyncSession) BusinessContact +async get_contacts(category: str, keyword: str, db: AsyncSession) list~BusinessContact~ +async send_contact_card(db, conversation, employee_id, contact, reason) Message -async _create_routing_text_message(db, conversation, reason, category) Message -async _create_system_hint_message(db, conversation) Message +async record_routing_event(db, conversation_id, employee_id, content, category, confidence, contact) RoutingEvent -async _push_via_ws(employee_id, conversation, messages) None } %% ===== API 类 ===== class RoutingAPI { +GET /api/h5/routing/contact +GET /api/routing/contacts +POST /api/conversations/{id}/send-contact-card } class ApprovalAPI { +POST /api/approval/detect-intent -async _call_dify_approval_intent(text, employee_id) dict } %% ===== Schema 类 ===== class ApprovalDetectIntentResponse { +is_approval_request: bool +confidence: float +approval_type: str|None +source: str +intent_type: str +business_category: str|None +routing_confidence: float } class ContactCardResponse { +id: int +name: str +gender: str +department: str +position: str +responsibility: str +extension: str +service_area: str +wecom_userid: str +avatar_url: str +business_category: str } class SendContactCardRequest { +contact_id: int +reason: str } %% ===== 现有类(引用) ===== class Message { +id: str +conversation_id: str +sender_type: str +msg_type: str +content: str +extra_data: dict } class H5AiTask { +async process_h5_ai_reply(conversation_id, employee_id, content, dify_conversation_id) -async _handle_routing(db, conversation, employee_id, content) bool -async _handle_byod_query(db, conversation, employee_id, content) -async _persist_and_push(db, conversation, employee_id, content, ...) } class WSManager { +async broadcast(data: dict) +async broadcast_to_employees(employee_ids: list~str~, data: dict) } class DifyAPI { POST /v1/chat-messages +inputs: dict +query: str +response_mode: "blocking" +user: str } %% ===== 关系 ===== BusinessContact "1" --> RoutingEvent : contact_id FK RoutingService --> BusinessContact : 查询/创建 RoutingService --> RoutingEvent : 记录 RoutingService --> Message : 创建contact_card消息 RoutingService --> WSManager : WS推送 RoutingService --> DifyAPI : 调用统一意图识别 RoutingService ..> ApprovalDetectIntentResponse : 解析响应 RoutingAPI --> RoutingService : 调用 RoutingAPI ..> ContactCardResponse : 返回 RoutingAPI ..> SendContactCardRequest : 接收 ApprovalAPI ..> ApprovalDetectIntentResponse : 返回(扩展) ApprovalAPI --> DifyAPI : 调用(同一Dify应用) H5AiTask --> RoutingService : 调用_handle_routing H5AiTask --> Message : 创建AI消息 H5AiTask --> WSManager : WS推送 %% ===== 样式 ===== style BusinessContact fill:#e8f5e9,stroke:#07C160 style RoutingEvent fill:#e8f5e9,stroke:#07C160 style RoutingService fill:#fff3e0,stroke:#ff9800 style RoutingAPI fill:#e3f2fd,stroke:#1976d2 style ApprovalAPI fill:#e3f2fd,stroke:#1976d2 style ApprovalDetectIntentResponse fill:#f3e5f5,stroke:#9c27b0 style ContactCardResponse fill:#f3e5f5,stroke:#9c27b0 style SendContactCardRequest fill:#f3e5f5,stroke:#9c27b0 style Message fill:#f5f5f5,stroke:#9e9e9e style H5AiTask fill:#fce4ec,stroke:#e91e63 style WSManager fill:#f5f5f5,stroke:#9e9e9e style DifyAPI fill:#e0f7fa,stroke:#00acc1 ``` ## 字段详情 ### BusinessContact(业务联系人) | 属性 | 类型 | 说明 | |------|------|------| | id | int | PK, 自增 | | name | str(50) | 联系人姓名 | | gender | str(10) | male / female | | department | str(100) | 部门名称(如「行政部」) | | position | str(100) | 岗位(如「设备管理岗」) | | responsibility | str(500) | 负责业务描述 | | extension | str(20) | 分机号 | | service_area | str(200) | 服务区域/办公地点 | | wecom_userid | str(100) | 企微用户ID(openEnterpriseChat 用) | | avatar_url | str(500) | 头像URL(空则用姓名首字渲染) | | business_category | str(50) | 行政/人力资源/财务/法务/行政-物业 | | is_active | bool | 是否启用(默认 true) | | created_at | datetime | 创建时间 | | updated_at | datetime | 更新时间 | ### RoutingEvent(路由命中统计 — P1) | 属性 | 类型 | 说明 | |------|------|------| | id | int | PK, 自增 | | conversation_id | str(36) | FK → conversations.id | | employee_id | str(64) | 员工企微 UserID | | message_content | str(500) | 触发路由的员工消息(截断) | | business_category | str(50) | 识别的业务类别 | | routing_confidence | float | 路由置信度 | | contact_id | int | FK → business_contacts.id | | contact_name | str(50) | 联系人姓名(冗余存储) | | is_clicked | bool | 员工是否点击「联系TA」 | | created_at | datetime | 创建时间 | ### RoutingService 方法说明 | 方法 | 签名 | 说明 | |------|------|------| | keyword_prefilter | (text: str) → bool | 非IT关键词预过滤,命中返回True | | detect_routing_intent | async (text: str, employee_id: str) → dict | 调用Dify统一意图识别,返回路由判断结果 | | _call_dify_unified_intent | async (text: str, employee_id: str) → dict | 调用Dify原生API(私有方法) | | _fallback_routing_detect | (text: str) → tuple | Dify不可用时关键词降级兜底(私有) | | get_contact_by_category | async (category: str, db) → BusinessContact | 按业务类别查询第一个有效联系人 | | get_contacts | async (category: str, keyword: str, db) → list[BusinessContact] | 查询联系人列表(坐席端用) | | send_contact_card | async (db, conversation, employee_id, contact, reason) → Message | 发送名片三段式消息(路由文本+名片+系统提示) | | record_routing_event | async (db, ...) → RoutingEvent | 记录路由命中事件(P1统计) |