', methods=['POST'])
+@handle_api_errors
def unverify_knowledge(knowledge_id):
"""取消验证知识库条目"""
- try:
- success = service_manager.get_assistant().knowledge_manager.unverify_knowledge_entry(knowledge_id)
- return jsonify({"success": success, "message": "取消验证成功" if success else "取消验证失败"})
- except Exception as e:
- return jsonify({"error": str(e)}), 500
+ success = service_manager.get_assistant().knowledge_manager.unverify_knowledge_entry(knowledge_id)
+ if success:
+ return create_success_response("取消验证成功")
+ else:
+ return create_error_response("取消验证失败", 404)
diff --git a/src/web/blueprints/workorders.py b/src/web/blueprints/workorders.py
index 5b4f9f8..6f52bda 100644
--- a/src/web/blueprints/workorders.py
+++ b/src/web/blueprints/workorders.py
@@ -16,27 +16,8 @@ from sqlalchemy import text
logger = logging.getLogger(__name__)
-# 简化的AI准确率配置类
-class SimpleAIAccuracyConfig:
- """简化的AI准确率配置"""
- def __init__(self):
- self.auto_approve_threshold = 0.95
- self.use_human_resolution_threshold = 0.90
- self.manual_review_threshold = 0.80
- self.ai_suggestion_confidence = 0.95
- self.human_resolution_confidence = 0.90
-
- def should_auto_approve(self, similarity: float) -> bool:
- return similarity >= self.auto_approve_threshold
-
- def should_use_human_resolution(self, similarity: float) -> bool:
- return similarity < self.use_human_resolution_threshold
-
- def get_confidence_score(self, similarity: float, use_human: bool = False) -> float:
- if use_human:
- return self.human_resolution_confidence
- else:
- return max(similarity, self.ai_suggestion_confidence)
+# 移除SimpleAIAccuracyConfig,直接从统一配置获取
+from src.config.unified_config import get_config
from src.main import TSPAssistant
from src.core.database import db_manager
@@ -397,14 +378,14 @@ def save_workorder_human_resolution(workorder_id):
rec.ai_similarity = sim
# 使用简化的配置
- config = SimpleAIAccuracyConfig()
-
+ config = get_config().ai_accuracy
+
# 自动审批条件
- approved = config.should_auto_approve(sim)
+ approved = sim >= config.auto_approve_threshold
rec.approved = approved
-
+
# 记录使用人工描述入库的标记(当AI准确率低于阈值时)
- use_human_resolution = config.should_use_human_resolution(sim)
+ use_human_resolution = sim < config.use_human_resolution_threshold
rec.use_human_resolution = use_human_resolution
session.commit()
@@ -431,19 +412,19 @@ def approve_workorder_to_knowledge(workorder_id):
return jsonify({"error": "未找到工单建议记录"}), 400
# 使用简化的配置
- config = SimpleAIAccuracyConfig()
-
+ config = get_config().ai_accuracy
+
# 确定使用哪个内容入库
if rec.use_human_resolution and rec.human_resolution:
# AI准确率低于阈值,使用人工描述入库
answer_content = rec.human_resolution
- confidence_score = config.get_confidence_score(rec.ai_similarity or 0, use_human=True)
+ confidence_score = config.human_resolution_confidence
verified_by = 'human_resolution'
logger.info(f"工单 {workorder_id} 使用人工描述入库,AI相似度: {rec.ai_similarity:.4f}")
elif rec.approved and rec.ai_suggestion:
# AI准确率≥阈值,使用AI建议入库
answer_content = rec.ai_suggestion
- confidence_score = config.get_confidence_score(rec.ai_similarity or 0, use_human=False)
+ confidence_score = max(rec.ai_similarity or 0, config.ai_suggestion_confidence)
verified_by = 'auto_approve'
logger.info(f"工单 {workorder_id} 使用AI建议入库,相似度: {rec.ai_similarity:.4f}")
else:
diff --git a/src/web/service_manager.py b/src/web/service_manager.py
index 84d552c..50fb162 100644
--- a/src/web/service_manager.py
+++ b/src/web/service_manager.py
@@ -21,7 +21,7 @@ class ServiceManager:
if service_name not in self._services:
try:
self._services[service_name] = factory_func()
- logger.info(f"服务 {service_name} 已初始化")
+ logger.debug(f"服务 {service_name} 已初始化")
except Exception as e:
logger.error(f"初始化服务 {service_name} 失败: {e}")
raise
diff --git a/src/web/websocket_server.py b/src/web/websocket_server.py
index 6314f22..63b7763 100644
--- a/src/web/websocket_server.py
+++ b/src/web/websocket_server.py
@@ -90,8 +90,21 @@ class WebSocketServer:
await self._send_error(websocket, "缺少必要参数", message_id)
return
+ # 获取客户端IP
+ ip_address = None
+ try:
+ # websockets 15.x 获取 remote_address 的方式
+ ip_address = websocket.remote_address[0] if websocket.remote_address else None
+ except Exception:
+ pass
+
# 处理消息
- result = self.chat_manager.process_message(session_id, message)
+ result = self.chat_manager.process_message(
+ session_id,
+ message,
+ ip_address=ip_address,
+ invocation_method="websocket"
+ )
response = {
"type": "message_response",
@@ -210,21 +223,9 @@ class WebSocketServer:
await websocket.send(json.dumps(response, ensure_ascii=False))
- async def handle_client(self, websocket: WebSocketServerProtocol, path: str):
- """处理客户端连接"""
- # 检查连接头
- headers = websocket.request_headers
- connection = headers.get("Connection", "").lower()
-
- # 处理不同的连接头格式
- if "upgrade" not in connection and "keep-alive" in connection:
- logger.warning(f"收到非标准连接头: {connection}")
- # 对于keep-alive连接头,我们仍然接受连接
- elif "upgrade" not in connection:
- logger.warning(f"连接头不包含upgrade: {connection}")
- await websocket.close(code=1002, reason="Invalid connection header")
- return
-
+ async def handle_client(self, websocket):
+ """处理客户端连接(兼容 websockets 15.x)"""
+ # websockets 15.x 版本中,handler 只接收 websocket 参数,不再有 path 参数
await self.register_client(websocket)
try:
@@ -238,61 +239,17 @@ class WebSocketServer:
await self.unregister_client(websocket)
async def start_server(self):
- """启动WebSocket服务器"""
+ """启动WebSocket服务器(兼容 websockets 15.x)"""
logger.info(f"启动WebSocket服务器: ws://{self.host}:{self.port}")
- # 添加CORS支持
- async def handle_client_with_cors(websocket: WebSocketServerProtocol):
- # 获取path,websockets在提供process_request时,不会将path传递给handler
- path = websocket.path
- # 设置CORS头
- if websocket.request_headers.get("Origin"):
- # 允许跨域连接
- pass
- await self.handle_client(websocket, path)
-
+ # websockets 15.x 简化版本:直接传递处理函数
async with websockets.serve(
- handle_client_with_cors,
+ self.handle_client,
self.host,
- self.port,
- # 添加额外的服务器选项
- process_request=self._process_request
+ self.port
):
await asyncio.Future() # 保持服务器运行
- def _process_request(self, path, request_headers):
- """处理HTTP请求,支持CORS"""
- # 检查是否是WebSocket升级请求
- # request_headers 可能是 Headers 对象或 Request 对象
- if hasattr(request_headers, 'get'):
- upgrade_header = request_headers.get("Upgrade", "").lower()
- elif hasattr(request_headers, 'headers'):
- upgrade_header = request_headers.headers.get("Upgrade", "").lower()
- else:
- upgrade_header = ""
-
- if upgrade_header == "websocket":
- return None # 允许WebSocket连接
-
- # 对于非WebSocket请求,返回简单的HTML页面
- return (
- 200,
- [("Content-Type", "text/html; charset=utf-8")],
- b"""
-
-
-
- WebSocket Server
-
-
- WebSocket Server is running
- This is a WebSocket server. Please use a WebSocket client to connect.
- WebSocket URL: ws://localhost:8765
-
-
- """
- )
-
def run(self):
"""运行服务器"""
asyncio.run(self.start_server())
diff --git a/start_dashboard.py b/start_dashboard.py
index b9fab09..aba4aab 100644
--- a/start_dashboard.py
+++ b/start_dashboard.py
@@ -13,15 +13,25 @@ from datetime import datetime
# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-def setup_logging():
- """设置日志"""
+
+def setup_logging(start_time: str):
+ """设置日志,按启动时间创建子目录"""
+ # 日志根目录
+ log_root = "logs"
+ # 以启动时间命名的子目录(避免路径中的空格和冒号)
+ safe_start_time = start_time.replace(" ", "_").replace(":", "-")
+ log_dir = os.path.join(log_root, safe_start_time)
+ os.makedirs(log_dir, exist_ok=True)
+
+ log_file_path = os.path.join(log_dir, "dashboard.log")
+
logging.basicConfig(
level=logging.INFO,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
- logging.FileHandler('logs/dashboard.log', encoding='utf-8'),
- logging.StreamHandler()
- ]
+ logging.FileHandler(log_file_path, encoding="utf-8"),
+ logging.StreamHandler(),
+ ],
)
def start_websocket_server():
@@ -52,17 +62,18 @@ def main():
print("=" * 60)
print("TSP智能助手 - 综合管理平台")
print("=" * 60)
- print(f"启动时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+ start_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
+ print(f"启动时间: {start_time}")
print()
- # 设置日志
- setup_logging()
+ # 设置日志(日志目录按启动时间区分)
+ setup_logging(start_time)
logger = logging.getLogger(__name__)
try:
# 检查必要目录
- os.makedirs('logs', exist_ok=True)
- os.makedirs('data', exist_ok=True)
+ os.makedirs("logs", exist_ok=True)
+ os.makedirs("data", exist_ok=True)
logger.info("正在启动TSP智能助手综合管理平台...")
@@ -77,27 +88,32 @@ def main():
# 导入并启动Flask应用
from src.web.app import app
-
+ from src.config.unified_config import get_config
+
+ # 获取配置
+ config = get_config()
+
print()
print("访问地址:")
- print(" 主页: http://localhost:5000")
- print(" 预警管理: http://localhost:5000/alerts")
- print(" 实时对话: http://localhost:5000/chat")
- print(" WebSocket: ws://localhost:8765")
+ print(f" 主页: http://localhost:{config.server.port}")
+ print(f" 预警管理: http://localhost:{config.server.port}/alerts")
+ print(f" 实时对话: http://localhost:{config.server.port}/chat")
+ print(f" WebSocket: ws://localhost:{config.server.websocket_port}")
print()
print("按 Ctrl+C 停止服务")
print("=" * 60)
-
+
# 在单独线程中启动WebSocket服务器
websocket_thread = threading.Thread(target=start_websocket_server, daemon=True)
websocket_thread.start()
-
+
# 启动Flask应用
app.run(
- debug=False,
- host='0.0.0.0',
- port=5000,
- threaded=True
+ debug=config.server.debug,
+ host=config.server.host,
+ port=config.server.port,
+ threaded=True,
+ use_reloader=False # 禁用重载器,避免重复启动WebSocket服务器
)
except KeyboardInterrupt:
diff --git a/test_refactor.py b/test_refactor.py
deleted file mode 100644
index aae763d..0000000
--- a/test_refactor.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-测试重构后的应用
-"""
-
-import sys
-import os
-
-# 添加项目路径
-sys.path.append('src')
-
-def test_blueprints():
- """测试蓝图注册"""
- try:
- from src.web.app import app
-
- print("✓ Flask应用导入成功")
-
- # 测试蓝图注册
- blueprints = list(app.blueprints.keys())
- print(f"✓ 已注册蓝图: {blueprints}")
-
- expected_blueprints = [
- 'alerts', 'workorders', 'conversations', 'knowledge',
- 'monitoring', 'system', 'feishu_sync', 'core', 'auth',
- 'agent', 'vehicle', 'analytics', 'test'
- ]
-
- for bp in expected_blueprints:
- if bp in blueprints:
- print(f"✓ 蓝图 {bp} 已注册")
- else:
- print(f"✗ 蓝图 {bp} 未注册")
-
- # 测试路由
- routes = [str(rule) for rule in app.url_map.iter_rules()]
- agent_routes = [r for r in routes if 'agent' in r]
- vehicle_routes = [r for r in routes if 'vehicle' in r]
- analytics_routes = [r for r in routes if 'analytics' in r]
-
- print(f"✓ Agent相关路由数量: {len(agent_routes)}")
- print(f"✓ Vehicle相关路由数量: {len(vehicle_routes)}")
- print(f"✓ Analytics相关路由数量: {len(analytics_routes)}")
-
- return True
-
- except Exception as e:
- print(f"✗ 应用测试失败: {e}")
- import traceback
- traceback.print_exc()
- return False
-
-def test_blueprint_imports():
- """测试蓝图模块导入"""
- blueprints_to_test = [
- ('src.web.blueprints.agent', 'agent_bp'),
- ('src.web.blueprints.vehicle', 'vehicle_bp'),
- ('src.web.blueprints.analytics', 'analytics_bp'),
- ('src.web.blueprints.test', 'test_bp'),
- ]
-
- for module_name, bp_name in blueprints_to_test:
- try:
- module = __import__(module_name, fromlist=[bp_name])
- bp = getattr(module, bp_name)
- print(f"✓ {module_name}.{bp_name} 导入成功")
- except Exception as e:
- print(f"✗ {module_name}.{bp_name} 导入失败: {e}")
- return False
-
- return True
-
-if __name__ == '__main__':
- print("开始测试重构后的应用...")
- print("=" * 50)
-
- success = True
-
- print("\n1. 测试蓝图模块导入...")
- success &= test_blueprint_imports()
-
- print("\n2. 测试Flask应用和蓝图注册...")
- success &= test_blueprints()
-
- print("\n" + "=" * 50)
- if success:
- print("✓ 所有测试通过!重构成功!")
- else:
- print("✗ 部分测试失败,请检查代码")
- sys.exit(1)
diff --git a/tsp_assistant-bc.db b/tsp_assistant-bc.db
deleted file mode 100644
index 7c7b2fa..0000000
Binary files a/tsp_assistant-bc.db and /dev/null differ
diff --git a/tsp_assistant.db b/tsp_assistant.db
index 5d14bcb..23fc918 100644
Binary files a/tsp_assistant.db and b/tsp_assistant.db differ