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