diff --git a/README.md b/README.md index 9280572..656a3f3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ - **知识库集成**: 基于TF-IDF和余弦相似度的智能检索 - **自定义提示词**: 支持飞书同步和实时对话不同场景的LLM提示词 -### 📊 数据驱动分析 +### 数据驱动分析 - **真实数据**: 基于数据库的真实性能趋势分析 - **多维度统计**: 工单、预警、满意度、性能指标 - **可视化展示**: Chart.js图表,直观的数据呈现 @@ -92,7 +92,7 @@ - **向量化检索**: TF-IDF + 余弦相似度搜索 - **质量验证**: 支持知识条目验证和置信度设置 -### 5. 数据分析 📊 +### 5. 数据分析 - **实时趋势**: 基于真实数据的性能趋势分析 - **多维度统计**: 工单、预警、满意度等关键指标 - **系统健康**: CPU、内存、响应时间监控 @@ -289,7 +289,7 @@ python version.py changelog --message "新功能描述" python version.py tag --message "Release v1.3.0" ``` -## 📊 系统监控 +## 系统监控 ### 健康检查 - **API状态**: `/api/health` @@ -393,38 +393,38 @@ TZ=Asia/Shanghai - 🔧 **知识库搜索修复**: 简化搜索算法,提升检索准确率 - 🔧 **批量删除优化**: 修复外键约束和缓存问题 - 🔧 **日志编码修复**: 解决中文乱码问题 -- 📊 **可视化增强**: 修复预警、性能、满意度图表显示 +- **可视化增强**: 修复预警、性能、满意度图表显示 - 📚 **文档更新**: 完整的Docker部署和使用指南 ### v1.4.0 (2025-09-19) -- ✅ 飞书集成功能:支持飞书多维表格数据同步 -- ✅ 页面功能合并:飞书同步页面合并到主仪表板 -- ✅ 数据库架构优化:扩展工单表字段,支持飞书数据 -- ✅ 代码重构优化:大文件拆分,降低运行风险 -- ✅ 字段映射完善:智能映射飞书字段到本地数据库 -- ✅ 数据库初始化改进:集成字段迁移到初始化流程 +- 飞书集成功能:支持飞书多维表格数据同步 +- 页面功能合并:飞书同步页面合并到主仪表板 +- 数据库架构优化:扩展工单表字段,支持飞书数据 +- 代码重构优化:大文件拆分,降低运行风险 +- 字段映射完善:智能映射飞书字段到本地数据库 +- 数据库初始化改进:集成字段迁移到初始化流程 ### v1.3.0 (2025-09-17) -- ✅ 数据库架构优化:MySQL主数据库+SQLite备份系统 -- ✅ 工单详情API修复:解决数据库会话管理问题 -- ✅ 备份管理系统:自动备份MySQL数据到SQLite -- ✅ 数据库状态监控:实时监控MySQL和SQLite状态 -- ✅ 备份管理API:支持数据备份和恢复操作 +- 数据库架构优化:MySQL主数据库+SQLite备份系统 +- 工单详情API修复:解决数据库会话管理问题 +- 备份管理系统:自动备份MySQL数据到SQLite +- 数据库状态监控:实时监控MySQL和SQLite状态 +- 备份管理API:支持数据备份和恢复操作 ### v1.2.0 (2025-09-16) -- ✅ 系统设置扩展:API管理、模型参数配置、端口管理 -- ✅ 真实数据分析:修复性能趋势图表显示问题 -- ✅ 工单AI建议功能:智能生成处理建议 -- ✅ 知识库搜索优化:提升检索准确率 -- ✅ Agent管理改进:工具使用统计和自定义工具 +- 系统设置扩展:API管理、模型参数配置、端口管理 +- 真实数据分析:修复性能趋势图表显示问题 +- 工单AI建议功能:智能生成处理建议 +- 知识库搜索优化:提升检索准确率 +- Agent管理改进:工具使用统计和自定义工具 ### v1.1.0 (2025-09-16) -- ✅ 工单AI建议功能 -- ✅ 知识库搜索优化 -- ✅ Agent管理改进 +- 工单AI建议功能 +- 知识库搜索优化 +- Agent管理改进 ### v1.0.0 (2024-01-01) -- ✅ 初始版本发布 +- 初始版本发布 ## 📄 许可证 diff --git a/data/tsp_assistant.db b/data/tsp_assistant.db index a640574..33745c5 100644 Binary files a/data/tsp_assistant.db and b/data/tsp_assistant.db differ diff --git a/docs/FEISHU_LONGCONN.md b/docs/FEISHU_LONGCONN.md index 46881ae..d7fcc19 100644 --- a/docs/FEISHU_LONGCONN.md +++ b/docs/FEISHU_LONGCONN.md @@ -1,6 +1,6 @@ # 飞书长连接模式使用指南 -> ✅ **已验证可用** - 2026-02-11 +> **已验证可用** - 2026-02-11 > > 本文档介绍如何使用飞书官方 SDK 的长连接模式,无需公网域名即可接入飞书机器人。 @@ -8,14 +8,14 @@ 飞书官方 SDK 提供了**事件订阅 2.0(长连接模式)**,相比传统的 webhook 模式有以下优势: -| 特性 | Webhook 模式(旧) | 长连接模式(新)✅ | +| 特性 | Webhook 模式(旧) | 长连接模式(新) | |------|-------------------|-------------------| -| 公网域名 | ✅ 必需 | ❌ 不需要 | -| SSL 证书 | ✅ 必需 | ❌ 不需要 | -| 回调配置 | ✅ 需要配置 | ❌ 不需要 | -| 内网部署 | ❌ 不支持 | ✅ 支持 | -| 实时性 | 中等(HTTP 轮询) | ✅ 高(WebSocket) | -| 稳定性 | 中等 | ✅ 高(自动重连) | +| 公网域名 | 必需 | 不需要 | +| SSL 证书 | 必需 | 不需要 | +| 回调配置 | 需要配置 | 不需要 | +| 内网部署 | 不支持 | 支持 | +| 实时性 | 中等(HTTP 轮询) | 高(WebSocket) | +| 稳定性 | 中等 | 高(自动重连) | --- @@ -61,12 +61,12 @@ python init_database.py 访问 [飞书开放平台](https://open.feishu.cn/app),为您的应用添加以下权限: **必需权限:** -- ✅ `im:message` - 获取与发送单聊、群组消息 -- ✅ `im:message:send_as_bot` - 以应用的身份发消息 -- ✅ `im:chat` - 获取群组信息 +- `im:message` - 获取与发送单聊、群组消息 +- `im:message:send_as_bot` - 以应用的身份发消息 +- `im:chat` - 获取群组信息 **可选权限(用于工单同步):** -- ✅ `bitable:app` - 查看、评论、编辑和管理多维表格 +- `bitable:app` - 查看、评论、编辑和管理多维表格 **注意:** 添加权限后需要重新发布应用版本。 @@ -94,7 +94,7 @@ python start_feishu_bot.py - 日志级别: INFO 🔌 启动模式: 事件订阅 2.0(长连接) -✅ 优势: + 优势: - 无需公网域名 - 无需配置 webhook - 自动重连 @@ -107,7 +107,7 @@ python start_feishu_bot.py - App ID: cli_xxxxxxxxxxxxx - 模式: 事件订阅 2.0(长连接) - 无需公网域名和 webhook 配置 -✅ 飞书长连接服务初始化成功 + 飞书长连接服务初始化成功 ``` ### 方式 2: 在代码中集成 @@ -175,10 +175,10 @@ app.run(...) - 原始内容: @TSP助手 车辆无法连接网络 - 清理后内容: 车辆无法连接网络 🔑 会话用户标识: feishu_oc_xxxxxxxxxxxxx_ou_xxxxxxxxxxxxx -🆕 为用户 ou_xxxxxxxxxxxxx 在群聊 oc_xxxxxxxxxxxxx 创建新会话: session_xxxxxxxxxxxxx + 为用户 ou_xxxxxxxxxxxxx 在群聊 oc_xxxxxxxxxxxxx 创建新会话: session_xxxxxxxxxxxxx 🤖 调用 TSP Assistant 处理消息... 📤 准备发送回复 (长度: 156) -✅ 成功回复消息: om_xxxxxxxxxxxxx + 成功回复消息: om_xxxxxxxxxxxxx ``` --- @@ -195,12 +195,12 @@ app.run(...) ``` **缺点:** -- ❌ 需要公网域名 -- ❌ 需要配置 SSL -- ❌ 内网无法部署 -- ❌ 需要在飞书后台配置回调地址 +- 需要公网域名 +- 需要配置 SSL +- 内网无法部署 +- 需要在飞书后台配置回调地址 -### 新模式(长连接)- 推荐 ✅ +### 新模式(长连接)- 推荐 **文件:** `src/integrations/feishu_longconn_service.py` @@ -210,16 +210,16 @@ app.run(...) ``` **优点:** -- ✅ 无需公网域名 -- ✅ 无需 SSL 证书 -- ✅ 内网可部署 -- ✅ 无需配置回调地址 -- ✅ 自动重连 -- ✅ 更稳定 +- 无需公网域名 +- 无需 SSL 证书 +- 内网可部署 +- 无需配置回调地址 +- 自动重连 +- 更稳定 --- -## 📊 架构说明 +## 架构说明 ``` ┌─────────────────────────────────────────────────────────────┐ @@ -332,7 +332,7 @@ python3 -m pip install --upgrade certifi 3. **验证修复**: ```bash -python3 -c "import urllib.request; urllib.request.urlopen('https://open.feishu.cn', timeout=5); print('✅ SSL 验证成功')" +python3 -c "import urllib.request; urllib.request.urlopen('https://open.feishu.cn', timeout=5); print(' SSL 验证成功')" ``` **解决方案(Linux/Windows):** @@ -343,11 +343,11 @@ pip3 install --upgrade certifi ### Q2: 启动后没有收到消息? **检查清单:** -1. ✅ 确认飞书应用权限已配置(`im:message`) -2. ✅ 确认应用已发布最新版本 -3. ✅ 确认机器人已添加到群聊 -4. ✅ 查看日志是否有连接成功的提示 -5. ✅ 确认没有 SSL 证书错误(见 Q1) +1. 确认飞书应用权限已配置(`im:message`) +2. 确认应用已发布最新版本 +3. 确认机器人已添加到群聊 +4. 查看日志是否有连接成功的提示 +5. 确认没有 SSL 证书错误(见 Q1) ### Q3: 提示"权限不足"? @@ -379,7 +379,7 @@ pip3 install --upgrade certifi 现在您的 TSP Assistant 已经支持**飞书官方 SDK 的长连接模式**! -✅ **核心优势:** + **核心优势:** - 无需公网域名 - 无需 webhook 配置 - 内网可部署 @@ -387,7 +387,7 @@ pip3 install --upgrade certifi - 群聊隔离 - 完整的工单管理和 AI 分析功能 -✅ **使用方式:** + **使用方式:** ```bash python start_feishu_bot.py ``` diff --git a/src/integrations/feishu_longconn_service.py b/src/integrations/feishu_longconn_service.py index 229e628..5b8b311 100644 --- a/src/integrations/feishu_longconn_service.py +++ b/src/integrations/feishu_longconn_service.py @@ -6,7 +6,9 @@ import logging import json import threading +import queue from typing import Optional +from concurrent.futures import ThreadPoolExecutor import lark_oapi as lark from lark_oapi.api.im.v1 import P2ImMessageReceiveV1, ReplyMessageRequest, ReplyMessageRequestBody @@ -38,7 +40,7 @@ class FeishuLongConnService: .log_level(lark.LogLevel.DEBUG) \ .build() - logger.info("✅ 飞书客户端创建成功") + logger.info(" 飞书客户端创建成功") # 创建事件处理器 self.event_handler = lark.EventDispatcherHandler.builder( @@ -46,16 +48,31 @@ class FeishuLongConnService: ).register_p2_im_message_receive_v1(self._handle_message) \ .build() - logger.info("✅ 飞书事件处理器创建成功") - logger.info("✅ 飞书长连接服务初始化完成") + logger.info(" 飞书事件处理器创建成功") + logger.info(" 飞书长连接服务初始化完成") + + # 消息处理线程池(最多同时处理 5 条消息) + self._executor = ThreadPoolExecutor(max_workers=5, thread_name_prefix="feishu_msg") + self._msg_count = 0 def _handle_message(self, data: P2ImMessageReceiveV1) -> None: - """收到消息事件,立即派发到后台线程处理,避免阻塞 SDK 事件循环""" - threading.Thread( - target=self._process_message, - args=(data,), - daemon=True - ).start() + """收到消息事件,立即提交到线程池,确保 SDK 回调快速返回""" + self._msg_count += 1 + msg_no = self._msg_count + logger.info(f"📨 收到飞书消息事件 #{msg_no},提交到处理队列") + self._executor.submit(self._process_message_safe, data, msg_no) + + def _process_message_safe(self, data: P2ImMessageReceiveV1, msg_no: int) -> None: + """安全包装,确保异常不会导致线程崩溃""" + try: + self._process_message(data) + except Exception as e: + logger.error(f"❌ 消息 #{msg_no} 处理异常: {e}", exc_info=True) + try: + mid = data.event.message.message_id + self._reply_message(mid, "抱歉,系统遇到了一些问题,请稍后重试。") + except: + pass def _process_message(self, data: P2ImMessageReceiveV1) -> None: """ @@ -131,7 +148,7 @@ class FeishuLongConnService: content_json = json.loads(content) text_content = content_json.get("text", "").strip() except json.JSONDecodeError as e: - logger.error(f"❌ 解析消息内容失败: {e}") + logger.error(f"解析消息内容失败: {e}") return logger.info(f"📝 文本内容: {text_content}") @@ -151,11 +168,11 @@ class FeishuLongConnService: break if not text_content: - logger.warning(f"⚠️ 移除@后内容为空,回复提示") + logger.warning(f" 移除@后内容为空,回复提示") self._reply_message(message_id, "您好!请问有什么可以帮助您的吗?") return - logger.info(f"✅ 清理后内容: {text_content}") + logger.info(f" 清理后内容: {text_content}") # 构造会话用户ID(群聊隔离) session_user_id = f"feishu_{chat_id}_{sender_id}" @@ -178,7 +195,7 @@ class FeishuLongConnService: # 更新会话的 tenant_id(群可能重新绑定了租户) if session_id in chat_manager.active_sessions: chat_manager.active_sessions[session_id]['tenant_id'] = tenant_id - logger.info(f"✅ 找到已有会话: {session_id}") + logger.info(f" 找到已有会话: {session_id}") break # 如果没有会话,创建新会话 @@ -188,7 +205,7 @@ class FeishuLongConnService: work_order_id=None, tenant_id=tenant_id ) - logger.info(f"🆕 创建新会话: {session_id}, 用户={sender_name}({sender_id}), 租户={tenant_id}") + logger.info(f" 创建新会话: {session_id}, 用户={sender_name}({sender_id}), 租户={tenant_id}") # 调用实时对话接口处理消息 response_data = chat_manager.process_message( @@ -198,7 +215,7 @@ class FeishuLongConnService: invocation_method=f"feishu_longconn({chat_type})" ) - logger.info(f"📊 处理结果: {response_data.get('success')}") + logger.info(f" 处理结果: {response_data.get('success')}") # 提取回复 if response_data.get("success"): @@ -206,7 +223,7 @@ class FeishuLongConnService: else: error_msg = response_data.get('error', '未知错误') reply_text = f"处理消息时出错: {error_msg}" - logger.error(f"❌ 处理失败: {error_msg}") + logger.error(f"处理失败: {error_msg}") # 确保回复是字符串 if isinstance(reply_text, dict): @@ -220,10 +237,10 @@ class FeishuLongConnService: # 发送回复 self._reply_message(message_id, reply_text) - logger.info("✅ 消息处理完成") + logger.info(" 消息处理完成") except Exception as e: - logger.error(f"❌ 处理消息时发生错误: {e}", exc_info=True) + logger.error(f"处理消息时发生错误: {e}", exc_info=True) # 尝试发送错误提示 try: if 'message_id' in locals(): @@ -261,14 +278,14 @@ class FeishuLongConnService: response = self.client.im.v1.message.reply(request) if not response.success(): - logger.error(f"❌ 回复失败: {response.code} - {response.msg}") + logger.error(f"回复失败: {response.code} - {response.msg}") return False - logger.info(f"✅ 回复成功: {message_id}") + logger.info(f" 回复成功: {message_id}") return True except Exception as e: - logger.error(f"❌ 回复消息时发生错误: {e}", exc_info=True) + logger.error(f"回复消息时发生错误: {e}", exc_info=True) return False def start(self): @@ -300,7 +317,7 @@ class FeishuLongConnService: logger.info("") logger.info("⏹️ 用户中断,停止飞书长连接客户端") except Exception as e: - logger.error(f"❌ 飞书长连接客户端异常: {e}", exc_info=True) + logger.error(f"飞书长连接客户端异常: {e}", exc_info=True) raise diff --git a/src/integrations/feishu_permission_checker.py b/src/integrations/feishu_permission_checker.py index f0363d3..e9e5c68 100644 --- a/src/integrations/feishu_permission_checker.py +++ b/src/integrations/feishu_permission_checker.py @@ -251,11 +251,11 @@ class FeishuPermissionChecker: result = self.check_permissions() summary = "飞书权限检查结果:\n" - summary += f"整体状态: {'✅ 正常' if result['success'] else '❌ 异常'}\n\n" + summary += f"整体状态: {' 正常' if result['success'] else '异常'}\n\n" summary += "检查项目:\n" for check_name, check_result in result["checks"].items(): - status_icon = "✅" if check_result["status"] == "success" else "⚠️" if check_result["status"] == "warning" else "❌" + status_icon = "" if check_result["status"] == "success" else "" if check_result["status"] == "warning" else "❌" summary += f" {status_icon} {check_name}: {check_result['message']}\n" if result["recommendations"]: diff --git a/src/web/app.py b/src/web/app.py index 51de5e9..9f20210 100644 --- a/src/web/app.py +++ b/src/web/app.py @@ -90,7 +90,7 @@ def check_login(): '/login', '/api/auth/login', '/api/auth/status', - '/api/feishu/bot/event', # ✅ 飞书机器人回调 + '/api/feishu/bot/event', # 飞书机器人回调 '/static/', '/uploads/' ] diff --git a/src/web/blueprints/tenants.py b/src/web/blueprints/tenants.py index c3bc826..9e3b989 100644 --- a/src/web/blueprints/tenants.py +++ b/src/web/blueprints/tenants.py @@ -187,7 +187,7 @@ def resolve_tenant_by_chat_id(chat_id: str) -> str: return t.tenant_id except Exception as e: logger.error(f"解析飞书群租户映射失败: {e}") - logger.warning(f"⚠️ 飞书群 {chat_id} 未绑定任何租户,使用默认租户。请在租户管理页面将此 chat_id 绑定到对应租户。") + logger.warning(f" 飞书群 {chat_id} 未绑定任何租户,使用默认租户。请在租户管理页面将此 chat_id 绑定到对应租户。") return DEFAULT_TENANT diff --git a/start_dashboard.py b/start_dashboard.py index 461d3c5..50aa1c2 100644 --- a/start_dashboard.py +++ b/start_dashboard.py @@ -56,7 +56,7 @@ def start_feishu_longconn_service(): service = get_feishu_longconn_service() service.start() # 这会阻塞当前线程 except Exception as e: - print(f"❌ 飞书长连接服务启动失败: {e}") + print(f"飞书长连接服务启动失败: {e}") import traceback traceback.print_exc() diff --git a/start_feishu_bot.py b/start_feishu_bot.py index cd4f852..28e0442 100755 --- a/start_feishu_bot.py +++ b/start_feishu_bot.py @@ -47,7 +47,7 @@ def main(): logger.info(f" - 日志级别: {log_level}") logger.info("") logger.info("🔌 启动模式: 事件订阅 2.0(长连接)") - logger.info("✅ 优势:") + logger.info(" 优势:") logger.info(" - 无需公网域名") logger.info(" - 无需配置 webhook") logger.info(" - 自动重连") @@ -68,7 +68,7 @@ def main(): logger.info("⏹️ 用户中断,正在停止服务...") logger.info("👋 再见!") except Exception as e: - logger.error(f"❌ 服务异常退出: {e}", exc_info=True) + logger.error(f"服务异常退出: {e}", exc_info=True) sys.exit(1)