# ============================================================================= # 企微IT智能服务台 — 趣味话术服务 # ============================================================================= # 说明:管理各场景的趣味话术,包括: # 1. 根据触发场景返回对应话术 # 2. 从 funny_phrases 表读取话术配置 # 3. 支持按员工 VIP 等级自动切换话术(VIP → 正式版话术) # 4. 预置 6 种场景的默认话术 # ============================================================================= import logging import random from typing import Optional from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models.funny_phrase import FunnyPhrase logger = logging.getLogger(__name__) class FunnyPhraseService: """趣味话术服务。 根据触发场景返回对应的趣味话术。 支持后台动态修改话术内容(通过 funny_phrases 表)。 """ # 默认话术(当数据库未配置时使用,和 PRD 一致) DEFAULT_PHRASES = { "shake": "大哥,俺这就去摇人,稍等...", "keyword": "收到!这就帮您摇位大神来", "waiting": "人还在路上,别急别急~", "connected": "人摇来了!IT坐席为您服务", "timeout": "坐席都在忙,不过AI还在呢,要不先聊聊?我再继续摇", "vip": "这就帮您安排专家,请稍候", } def __init__(self, db: AsyncSession): """初始化趣味话术服务。 Args: db: 异步数据库会话 """ self.db = db # -------------------------------------------------------------------------- # 获取话术 # -------------------------------------------------------------------------- async def get_phrase( self, scene: str, is_vip: bool = False ) -> str: """根据触发场景获取趣味话术。 优先从 funny_phrases 表读取,如果未配置则使用默认话术。 VIP 员工自动使用 "vip" 场景的话术。 场景说明: - click_shake / shake: 点击摇人按钮 - keyword: 关键词触发转人工 - waiting: 排队等待(30秒无人接单) - connected: 坐席接入 - timeout: 等待超时(2分钟) - vip: VIP员工专用 Args: scene: 触发场景(shake/keyword/waiting/connected/timeout/vip) is_vip: 是否 VIP 员工(VIP 优先使用 vip 场景话术) Returns: str: 话术内容 """ # VIP 员工优先使用 vip 场景话术 actual_scene = scene if is_vip and scene != "vip": # 尝试获取 VIP 话术,如果不存在则回退到原场景 vip_phrase = await self._get_phrase_from_db("vip") if vip_phrase: logger.debug(f"VIP员工使用专属话术: scene=vip") return vip_phrase # 从数据库获取对应场景的话术 phrase = await self._get_phrase_from_db(actual_scene) if phrase: return phrase # 数据库未配置,使用默认话术 default = self.DEFAULT_PHRASES.get(actual_scene, "请稍候...") logger.debug(f"使用默认话术: scene={actual_scene}") return default # -------------------------------------------------------------------------- # 从数据库获取话术 # -------------------------------------------------------------------------- async def _get_phrase_from_db(self, scene: str) -> Optional[str]: """从 funny_phrases 表获取指定场景的话术。 同一场景可能有多条话术,随机返回一条(增加趣味性)。 只返回 is_active=True 的话术。 Args: scene: 触发场景 Returns: Optional[str]: 话术内容,未找到返回 None """ stmt = ( select(FunnyPhrase) .where( FunnyPhrase.scene == scene, FunnyPhrase.is_active == True, ) .order_by(FunnyPhrase.sort_order) ) result = await self.db.execute(stmt) phrases = list(result.scalars().all()) if not phrases: return None # 随机选一条(如果有多个话术,增加随机趣味性) chosen = random.choice(phrases) return chosen.content # -------------------------------------------------------------------------- # 获取所有场景的话术 # -------------------------------------------------------------------------- async def get_all_phrases(self) -> dict: """获取所有场景的话术。 用于后台管理页面展示当前话术配置。 Returns: dict: 按场景分组的话术字典 """ stmt = select(FunnyPhrase).order_by( FunnyPhrase.scene, FunnyPhrase.sort_order ) result = await self.db.execute(stmt) phrases = list(result.scalars().all()) # 按场景分组 grouped: dict = {} for phrase in phrases: if phrase.scene not in grouped: grouped[phrase.scene] = [] grouped[phrase.scene].append({ "id": str(phrase.id), "content": phrase.content, "tone": phrase.tone, "sort_order": phrase.sort_order, "is_active": phrase.is_active, }) return grouped