v3.1 + 批次0: 智能回复重构基线 - ApprovalMatcher + 关键词降级 + 文档速修 + v4.0任务书面化

This commit is contained in:
Simon
2026-07-17 23:08:59 +08:00
parent 5a77a89ab1
commit 3ed86d5fb3
181 changed files with 19738 additions and 2655 deletions
@@ -9,13 +9,14 @@
import logging
from datetime import datetime
from typing import List, Optional, Tuple
from typing import Dict, List, Optional, Tuple
from uuid import UUID
from sqlalchemy import and_, case, desc, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.conversation import Conversation
from app.models.message import Message
logger = logging.getLogger(__name__)
@@ -212,3 +213,108 @@ class SessionQueryService:
raise ERR_CONVERSATION_NOT_FOUND
return conversation
# --------------------------------------------------------------------------
# 获取员工历史消息(跨会话聚合)
# --------------------------------------------------------------------------
async def get_employee_history_messages(
self,
employee_id: str,
limit: int = 50,
before: Optional[str] = None,
current_conversation_id: Optional[str] = None,
) -> Tuple[List[Message], bool, Dict[str, str]]:
"""获取员工的历史消息(跨会话聚合)。
将同一员工的所有会话消息合并为一条时间线,按时间排序。
用于"历史会话"功能,让坐席查看员工过去的所有咨询记录。
查询逻辑:
1. 查询 conversations 表中 employee_id = ? 的所有会话ID
2. 查询 messages 表中 conversation_id IN (会话ID列表) 的消息
3. 如有 before 参数,获取该消息的 created_at,只查更早的消息
4. 按 created_at DESC 排序,取 limit + 1 条(多取1条判断 has_more
5. 对涉及的每个会话,查询其 sender_type='employee' 的最早一条消息,
取前20字作为摘要
6. 返回消息列表 + has_more + conversation_summaries
Args:
employee_id: 员工企微 UserID
limit: 每页消息数量(默认50)
before: 游标消息ID,只查该消息之前的消息(向上翻页)
current_conversation_id: 当前会话ID(仅用于标记,不影响查询逻辑)
Returns:
tuple: (消息列表, 是否还有更多, {conversation_id: "前20字摘要"})
"""
# 1. 查询该员工的所有会话ID
conv_stmt = select(Conversation.id).where(
Conversation.employee_id == employee_id
)
conv_result = await self.db.execute(conv_stmt)
conversation_ids = [row[0] for row in conv_result.all()]
if not conversation_ids:
# 该员工没有任何会话
return [], False, {}
# 2. 构建消息查询(跨会话聚合,按时间倒序)
stmt = select(Message).where(
Message.conversation_id.in_(conversation_ids)
).order_by(Message.created_at.desc())
# 3. 如有 before 参数,获取该消息的 created_at,只查更早的消息
if before:
try:
before_stmt = select(Message.created_at).where(
Message.id == str(before)
)
before_result = await self.db.execute(before_stmt)
before_time = before_result.scalar_one_or_none()
if before_time:
stmt = stmt.where(Message.created_at < before_time)
except Exception:
pass # before 参数格式错误,忽略
# 4. 取 limit + 1 条(多取1条判断 has_more
stmt = stmt.limit(limit + 1)
result = await self.db.execute(stmt)
messages = list(result.scalars().all())
# 判断是否还有更多消息
has_more = len(messages) > limit
if has_more:
messages = messages[:limit]
# 5. 对涉及的每个会话,查询其 sender_type='employee' 的最早一条消息摘要
involved_conv_ids = list(set(m.conversation_id for m in messages))
conversation_summaries: Dict[str, str] = {}
for conv_id in involved_conv_ids:
# 查询该会话中员工发送的最早一条消息
summary_stmt = (
select(Message.content)
.where(
and_(
Message.conversation_id == conv_id,
Message.sender_type == "employee",
)
)
.order_by(Message.created_at.asc())
.limit(1)
)
summary_result = await self.db.execute(summary_stmt)
first_employee_msg = summary_result.scalar_one_or_none()
if first_employee_msg:
# 取前20字作为摘要
conversation_summaries[conv_id] = first_employee_msg[:20]
else:
conversation_summaries[conv_id] = "未知会话"
logger.debug(
f"查询员工历史消息: employee_id={employee_id}, "
f"conv_count={len(conversation_ids)}, "
f"msg_count={len(messages)}, has_more={has_more}"
)
return messages, has_more, conversation_summaries