chore: initial baseline with P0-safety .gitignore
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# =============================================================================
|
||||
# 企微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
|
||||
Reference in New Issue
Block a user