Files
wecom_it_smart_desk/backend/app/services/funny_phrase_service.py
T
Simon bea288e414 feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) ==
- 代办事项真实数据源集成 (企微审批API 8bug修复链)
- H5/坐席端 Logo样式统一+绿色背景
- 视频引导页修复 (localStorage key v2)
- 坐席端 v9 Vue版本修复 (ElMessage._context)
- 截图按钮 v10 修复 (getDisplayMedia user gesture)
- 扫码样式恢复+H5扫码登录跳转修复
- H5截图快捷键提示

== 代码完成待部署 (3项) ==
- 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查)
- 会议室预定-小鱼易联终端 (40文件, 40/40测试通过)
- IT资产升级审批推送 (asset_service.py)

== 需求文档 (2项) ==
- 坐席端AI辅助消息框-PRD (4项新功能确认)
- 坐席端布局优化建议 v2.0 (7天计划)

== 新增文档 ==
- 日报-2026-07-11.md
- 知识迭代Bug修复报告-20260711.md
- 会议室预定-部署指南.md
- CHANGELOG.md 更新

== 测试 ==
- test_todo_integration.py: 40/40
- test_meetingroom.py: 40/40
- test_bugfix_ki_suggestions.py: 21/21
2026-07-11 23:13:10 +08:00

158 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# 企微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