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
This commit is contained in:
Simon
2026-07-11 23:13:10 +08:00
parent 3d152fc8eb
commit bea288e414
928 changed files with 85169 additions and 54205 deletions
+42 -10
View File
@@ -36,6 +36,7 @@ from app.api.agents import get_current_agent
from app.dependencies import require_permission, get_current_user, UserInfo
from app.services.ws_manager import manager
from app.services.session_service import SessionService
from app.utils.response import AppException, ERR_CONVERSATION_NOT_FOUND, ERR_CONVERSATION_RESOLVED, success_response
logger = logging.getLogger(__name__)
@@ -43,6 +44,37 @@ logger = logging.getLogger(__name__)
# 创建路由器
router = APIRouter()
async def _enrich_message_with_avatar(msg_dict: dict, db: AsyncSession) -> dict:
"""为消息字典补充 sender_avatar 字段。
根据 sender_type 决定头像来源:
- ai: 使用静态 /duckula.webp
- employee: 从 SessionService 获取员工头像
- agent: 暂用占位符(坐席头像功能待实现)
"""
sender_type = msg_dict.get("sender_type", "")
sender_id = msg_dict.get("sender_id", "")
if sender_type == "ai":
msg_dict["sender_avatar"] = "/duckula.webp"
elif sender_type == "employee" and sender_id:
try:
session_svc = SessionService(db=db)
avatar_url = await session_svc._get_employee_avatar(sender_id)
msg_dict["sender_avatar"] = avatar_url
except Exception as e:
logger.warning(f"获取员工头像失败: employee_id={sender_id}, error={e}")
msg_dict["sender_avatar"] = ""
elif sender_type == "agent":
# 坐席头像暂用默认头像
msg_dict["sender_avatar"] = ""
else:
msg_dict["sender_avatar"] = ""
return msg_dict
# 文件大小限制:10MB
MAX_FILE_SIZE = 10 * 1024 * 1024
@@ -122,8 +154,8 @@ async def list_messages(
msg.is_read = True
await db.flush()
# 转换为响应格式
items = [MessageResponse.model_validate(m).model_dump() for m in messages]
# 转换为响应格式(补充发送者头像)
items = [await _enrich_message_with_avatar(MessageResponse.model_validate(m).model_dump(), db) for m in messages]
return success_response(
data={
@@ -241,8 +273,8 @@ async def send_message(
# 做什么:构建 new_message 事件,推送给会话的员工
# 为什么:实现双通道推送(企微消息 + WebSocket),H5 用户可以实时收到消息
try:
# 构建消息载荷
msg_payload = MessageResponse.model_validate(message).model_dump()
# 构建消息载荷(补充发送者头像)
msg_payload = await _enrich_message_with_avatar(MessageResponse.model_validate(message).model_dump(), db)
msg_payload["message_id"] = msg_payload["id"]
# 构建 WebSocket 事件
@@ -258,8 +290,8 @@ async def send_message(
# WebSocket 推送失败不阻塞响应(员工可能未打开H5页面)
logger.warning(f"WebSocket消息推送失败(H5用户可能不在线): {e}")
# 转换为响应格式
response_data = MessageResponse.model_validate(message).model_dump()
# 转换为响应格式(补充发送者头像)
response_data = await _enrich_message_with_avatar(MessageResponse.model_validate(message).model_dump(), db)
return success_response(data=response_data)
@@ -316,8 +348,8 @@ async def poll_messages(
msg.is_read = True
await db.flush()
# 转换为响应格式
items = [MessageResponse.model_validate(m).model_dump() for m in messages]
# 转换为响应格式(补充发送者头像)
items = [await _enrich_message_with_avatar(MessageResponse.model_validate(m).model_dump(), db) for m in messages]
return success_response(
data={
@@ -669,8 +701,8 @@ async def search_messages(
result = await db.execute(stmt)
messages = list(result.scalars().all())
# 转换为响应格式
items = [MessageResponse.model_validate(m).model_dump() for m in messages]
# 转换为响应格式(补充发送者头像)
items = [await _enrich_message_with_avatar(MessageResponse.model_validate(m).model_dump(), db) for m in messages]
return success_response(
data={