diff --git a/AI建议权限问题最终解决方案.md b/AI建议权限问题最终解决方案.md deleted file mode 100644 index 0ed77c9..0000000 --- a/AI建议权限问题最终解决方案.md +++ /dev/null @@ -1,218 +0,0 @@ -# AI建议权限问题最终解决方案 - -## 🔍 问题确认 - -通过深度诊断工具确认: -- ✅ **访问令牌获取正常**:tenant_access_token获取成功 -- ✅ **表格访问正常**:可以成功访问表格和字段信息 -- ✅ **记录读取正常**:可以成功读取表格记录 -- ✅ **AI建议字段存在**:确认表格中有"AI建议"字段 -- ❌ **记录写入失败**:403 Forbidden,权限不足 - -**根本原因**:飞书应用缺少**记录写入权限** - -## 🛠️ 最终解决方案 - -### 步骤1:飞书开放平台权限配置 - -#### 1.1 登录飞书开放平台 -1. 访问:https://open.feishu.cn/ -2. 使用管理员账号登录 -3. 进入"应用管理" → "我的应用" - -#### 1.2 找到您的应用 -- 应用ID:`cli_a8b50ec0eed1500d` -- 应用名称:您的TSP智能助手应用 - -#### 1.3 添加必需权限 -在"权限管理"页面,确保应用具有以下权限: - -**核心权限**: -``` -bitable:app # 多维表格应用权限 -bitable:app:readonly # 多维表格只读权限 -bitable:app:readwrite # 多维表格读写权限 ⭐ 关键权限 -base:record:write # 记录写入权限 ⭐ 关键权限 -``` - -#### 1.4 重新发布应用 -- 权限修改后,点击"发布"或"上线" -- 等待权限生效(通常需要1-5分钟) - -### 步骤2:飞书多维表格协作者权限 - -#### 2.1 打开飞书多维表格 -- 使用浏览器打开您的飞书多维表格 -- 确保您有表格的管理权限 - -#### 2.2 添加应用为协作者 -1. 点击表格右上角的"分享"按钮 -2. 点击"添加协作者" -3. 搜索您的飞书应用名称或应用ID -4. 将应用添加为协作者 - -#### 2.3 设置协作者权限 -**重要**:将权限设置为以下之一: -- **编辑者** ✅ (推荐) -- **管理员** ✅ (完全权限) - -**不要设置为**: -- **查看者** ❌ (只能读取,无法写入) - -### 步骤3:验证修复结果 - -#### 3.1 使用Web界面验证 -1. 打开TSP智能助手主页面 -2. 点击"飞书同步"标签页 -3. 点击"权限检查"按钮 -4. 查看检查结果 - -#### 3.2 测试AI建议功能 -1. 点击"同步+AI建议"按钮 -2. 查看是否还有403错误 -3. 检查飞书表格中是否出现AI建议 - -## 📋 详细修复步骤 - -### 🔧 飞书开放平台操作 - -1. **登录飞书开放平台** - ``` - URL: https://open.feishu.cn/ - 账号: 管理员账号 - ``` - -2. **进入应用管理** - ``` - 路径: 应用管理 → 我的应用 - 应用ID: cli_a8b50ec0eed1500d - ``` - -3. **权限配置** - ``` - 页面: 权限管理 - 操作: 添加权限 - 权限列表: - - bitable:app - - bitable:app:readonly - - bitable:app:readwrite ⭐ - - base:record:write ⭐ - ``` - -4. **发布应用** - ``` - 操作: 点击"发布"按钮 - 等待: 1-5分钟权限生效 - ``` - -### 🔧 飞书多维表格操作 - -1. **打开表格** - ``` - 方式: 浏览器访问飞书多维表格 - 权限: 确保有管理权限 - ``` - -2. **添加协作者** - ``` - 操作: 点击右上角"分享"按钮 - 步骤: 添加协作者 → 搜索应用名称 - 应用: 您的TSP智能助手应用 - ``` - -3. **设置权限** - ``` - 权限级别: 编辑者 或 管理员 - 不要选择: 查看者 - 保存: 确认设置 - ``` - -## 🚨 常见问题解决 - -### 问题1:找不到权限设置 -**解决方案**: -- 确保使用管理员账号登录飞书开放平台 -- 检查应用是否已发布 -- 联系飞书技术支持 - -### 问题2:权限添加后仍然失败 -**解决方案**: -- 等待5-10分钟让权限生效 -- 重新发布应用 -- 清除浏览器缓存后重试 - -### 问题3:找不到应用名称 -**解决方案**: -- 使用应用ID:`cli_a8b50ec0eed1500d` -- 在飞书开放平台搜索应用ID -- 确认应用状态为"已发布" - -### 问题4:表格分享设置找不到 -**解决方案**: -- 确保您有表格的管理权限 -- 使用表格创建者账号 -- 联系表格管理员协助设置 - -## 📊 权限配置检查清单 - -### ✅ 飞书开放平台 -- [ ] 应用已启用并发布 -- [ ] 已添加`bitable:app`权限 -- [ ] 已添加`bitable:app:readonly`权限 -- [ ] 已添加`bitable:app:readwrite`权限 ⭐ -- [ ] 已添加`base:record:write`权限 ⭐ -- [ ] 权限修改后已重新发布 - -### ✅ 飞书多维表格 -- [ ] 应用已添加为表格协作者 -- [ ] 协作者权限设置为"编辑者"或"管理员" -- [ ] 表格未被锁定或设置为只读 -- [ ] "AI建议"字段存在且类型正确 - -## 🎯 验证成功标志 - -修复成功后,您应该看到: - -1. **权限检查通过** - ``` - ✅ 访问令牌获取成功 - ✅ 表格访问权限正常 - ✅ 记录读取权限正常 - ✅ 记录写入权限正常 - ✅ AI建议字段存在 - ``` - -2. **AI建议功能正常** - ``` - - 点击"同步+AI建议"无403错误 - - 飞书表格中出现AI建议内容 - - 日志显示"更新飞书AI建议成功" - ``` - -3. **系统日志正常** - ``` - 2025-09-22 XX:XX:XX - INFO - 更新飞书AI建议成功 - 2025-09-22 XX:XX:XX - INFO - 飞书同步完成 - ``` - -## 📞 技术支持 - -如果按照以上步骤仍然无法解决问题,请: - -1. **收集信息**: - - 飞书应用ID:`cli_a8b50ec0eed1500d` - - 表格ID:`tblnl3vJPpgMTSiP` - - 完整的错误日志 - - 权限检查结果截图 - -2. **联系支持**: - - 飞书开放平台技术支持 - - TSP智能助手技术支持 - -3. **检查企业设置**: - - 确认是否有企业级权限限制 - - 联系企业飞书管理员 - ---- - -**重要提醒**:403权限错误通常需要飞书管理员权限才能解决,建议联系相关技术人员协助配置。修复完成后,AI建议功能将可以正常工作!🎉 diff --git a/Linux使用说明.md b/Linux使用说明.md deleted file mode 100644 index d26c7d4..0000000 --- a/Linux使用说明.md +++ /dev/null @@ -1,259 +0,0 @@ -# TSP智能助手 Linux 使用说明 - -## 🐧 Linux环境部署指南 - -### 1. 系统要求 - -- **操作系统**: Ubuntu 18.04+, CentOS 7+, Debian 9+, Arch Linux -- **Node.js**: 18.x 或更高版本 -- **Python**: 3.7 或更高版本 -- **内存**: 至少 2GB RAM -- **磁盘**: 至少 1GB 可用空间 - -### 2. 快速开始 - -#### 方法一:一键安装(推荐) - -```bash -# 下载并运行安装脚本 -chmod +x install_dependencies.sh -./install_dependencies.sh -``` - -#### 方法二:手动安装 - -```bash -# 安装Node.js (Ubuntu/Debian) -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt-get install -y nodejs - -# 安装Node.js (CentOS/RHEL) -curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - -sudo yum install -y nodejs - -# 安装Python依赖 -pip3 install -r requirements.txt - -# 安装前端依赖 -cd frontend -npm install -cd .. -``` - -### 3. 启动服务 - -#### 启动传统版本(立即可用) - -```bash -chmod +x start_traditional.sh -./start_traditional.sh -``` - -访问:http://localhost:5000 - -#### 启动现代化前端(需要Node.js) - -```bash -chmod +x start_frontend.sh -./start_frontend.sh -``` - -访问:http://localhost:3000 - -#### 构建生产版本 - -```bash -chmod +x build_frontend.sh -./build_frontend.sh -``` - -### 4. 功能对比 - -| 功能 | 传统版本 | 现代化前端 | -|------|----------|------------| -| 基础功能 | ✅ 完整支持 | ✅ 完整支持 | -| 聊天系统 | ✅ 支持 | ✅ 统一组件 | -| 预警管理 | ✅ 支持 | ✅ 增强体验 | -| 国际化 | ❌ 仅中文 | ✅ 中英文切换 | -| 主题切换 | ❌ 固定主题 | ✅ 暗色/亮色 | -| 响应式设计 | ⚠️ 基础支持 | ✅ 完整支持 | -| 开发体验 | ⚠️ 传统开发 | ✅ 现代化开发 | - -### 5. 常见问题 - -#### Q: Node.js安装失败 -```bash -# 检查Node.js版本 -node --version -npm --version - -# 如果版本过低,重新安装 -curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - -sudo apt-get install -y nodejs -``` - -#### Q: Python依赖安装失败 -```bash -# 升级pip -pip3 install --upgrade pip - -# 重新安装依赖 -pip3 install -r requirements.txt -``` - -#### Q: 前端依赖安装失败 -```bash -# 清理缓存 -npm cache clean --force - -# 删除node_modules重新安装 -rm -rf frontend/node_modules -cd frontend -npm install -``` - -#### Q: 端口被占用 -```bash -# 查看端口占用 -sudo netstat -tlnp | grep :5000 -sudo netstat -tlnp | grep :3000 - -# 杀死占用进程 -sudo kill -9 -``` - -### 6. 开发环境配置 - -#### 使用VS Code开发 - -```bash -# 安装VS Code -wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg -sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/ -sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list' -sudo apt update -sudo apt install code - -# 安装推荐扩展 -code --install-extension ms-python.python -code --install-extension vue.volar -code --install-extension bradlc.vscode-tailwindcss -``` - -#### 使用Docker开发 - -```bash -# 创建Dockerfile -cat > Dockerfile << EOF -FROM node:20-alpine -WORKDIR /app -COPY frontend/package*.json ./ -RUN npm install -COPY frontend/ . -EXPOSE 3000 -CMD ["npm", "run", "dev"] -EOF - -# 构建并运行 -docker build -t tsp-frontend . -docker run -p 3000:3000 tsp-frontend -``` - -### 7. 生产部署 - -#### 使用Nginx反向代理 - -```bash -# 安装Nginx -sudo apt install nginx - -# 配置Nginx -sudo tee /etc/nginx/sites-available/tsp-assistant << EOF -server { - listen 80; - server_name your-domain.com; - - location / { - proxy_pass http://localhost:3000; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - } - - location /api/ { - proxy_pass http://localhost:5000; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - } -} -EOF - -# 启用站点 -sudo ln -s /etc/nginx/sites-available/tsp-assistant /etc/nginx/sites-enabled/ -sudo nginx -t -sudo systemctl reload nginx -``` - -#### 使用PM2进程管理 - -```bash -# 安装PM2 -npm install -g pm2 - -# 启动应用 -pm2 start src/web/app.py --name "tsp-backend" --interpreter python3 -pm2 start frontend/package.json --name "tsp-frontend" - -# 保存配置 -pm2 save -pm2 startup -``` - -### 8. 监控和日志 - -```bash -# 查看应用状态 -pm2 status - -# 查看日志 -pm2 logs tsp-backend -pm2 logs tsp-frontend - -# 重启应用 -pm2 restart tsp-backend -pm2 restart tsp-frontend -``` - -### 9. 备份和恢复 - -```bash -# 备份数据库 -cp tsp_assistant.db tsp_assistant_backup_$(date +%Y%m%d).db - -# 备份配置文件 -tar -czf config_backup_$(date +%Y%m%d).tar.gz config/ - -# 恢复数据库 -cp tsp_assistant_backup_20240101.db tsp_assistant.db -``` - ---- - -## 🚀 快速命令参考 - -```bash -# 一键启动传统版本 -./start_traditional.sh - -# 一键启动现代化前端 -./start_frontend.sh - -# 构建生产版本 -./build_frontend.sh - -# 安装所有依赖 -./install_dependencies.sh -``` - -现在您可以在Linux环境中愉快地使用TSP智能助手了!🎉 diff --git a/build_frontend.bat b/build_frontend.bat deleted file mode 100644 index e6c4be2..0000000 --- a/build_frontend.bat +++ /dev/null @@ -1,54 +0,0 @@ -@echo off -echo 构建TSP智能助手前端... -echo. - -cd frontend - -echo 检查Node.js环境... -node --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到Node.js,请先安装Node.js - echo 下载地址: https://nodejs.org/ - pause - exit /b 1 -) - -echo 检查npm环境... -npm --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到npm,请检查Node.js安装 - pause - exit /b 1 -) - -echo 检查依赖包... -if not exist "node_modules" ( - echo 安装依赖包... - npm install - if %errorlevel% neq 0 ( - echo 错误: 依赖包安装失败 - pause - exit /b 1 - ) -) - -echo 运行类型检查... -npm run type-check -if %errorlevel% neq 0 ( - echo 警告: 类型检查失败,但继续构建... -) - -echo 构建生产版本... -npm run build -if %errorlevel% neq 0 ( - echo 错误: 构建失败 - pause - exit /b 1 -) - -echo. -echo 构建完成! -echo 构建文件已输出到: src/web/static/dist -echo. - -pause diff --git a/build_frontend.sh b/build_frontend.sh deleted file mode 100644 index 0ed77c6..0000000 --- a/build_frontend.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -echo "构建TSP智能助手前端..." -echo - -# 检查Node.js环境 -echo "检查Node.js环境..." -if ! command -v node &> /dev/null; then - echo "错误: 未找到Node.js,请先安装Node.js" - echo "安装命令:" - echo " Ubuntu/Debian: sudo apt update && sudo apt install nodejs npm" - echo " CentOS/RHEL: sudo yum install nodejs npm" - echo " 或访问: https://nodejs.org/" - exit 1 -fi - -# 检查npm环境 -echo "检查npm环境..." -if ! command -v npm &> /dev/null; then - echo "错误: 未找到npm,请检查Node.js安装" - exit 1 -fi - -echo "Node.js版本: $(node --version)" -echo "npm版本: $(npm --version)" -echo - -# 检查Node.js版本兼容性 -NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) -if [ "$NODE_VERSION" -ge 22 ]; then - echo "检测到Node.js v22+,使用兼容性构建模式..." - SKIP_TYPE_CHECK=true -else - echo "使用标准构建模式..." - SKIP_TYPE_CHECK=false -fi - -# 进入前端目录 -cd frontend - -# 检查依赖包 -echo "检查依赖包..." -if [ ! -d "node_modules" ]; then - echo "安装依赖包..." - npm install - if [ $? -ne 0 ]; then - echo "错误: 依赖包安装失败" - exit 1 - fi -fi - -# 运行类型检查(根据Node.js版本决定) -if [ "$SKIP_TYPE_CHECK" = true ]; then - echo "跳过类型检查(Node.js v22+兼容性模式)..." -else - echo "运行类型检查..." - npm run type-check - if [ $? -ne 0 ]; then - echo "警告: 类型检查失败,但继续构建..." - fi -fi - -# 构建生产版本 -echo "构建生产版本..." -if [ "$SKIP_TYPE_CHECK" = true ]; then - # 直接使用Vite构建,跳过vue-tsc - echo "使用Vite直接构建(跳过TypeScript检查)..." - npx vite build -else - # 标准构建流程 - npm run build -fi - -if [ $? -ne 0 ]; then - echo "错误: 构建失败" - exit 1 -fi - -echo -echo "构建完成!" -echo "构建文件已输出到: ../src/web/static/dist" -echo diff --git a/config/field_mapping_config.json b/config/field_mapping_config.json index 3c4821d..848a13a 100644 --- a/config/field_mapping_config.json +++ b/config/field_mapping_config.json @@ -307,17 +307,17 @@ }, "field_priorities": { "order_id": 3, - "description": 1, - "category": 1, - "priority": 1, - "status": 1, - "created_at": 1, - "source": 2, - "solution": 2, - "resolution": 2, - "created_by": 2, - "vehicle_type": 2, - "vin_sim": 2, + "description": 3, + "category": 3, + "priority": 3, + "status": 3, + "created_at": 3, + "source": 3, + "solution": 3, + "resolution": 3, + "created_by": 3, + "vehicle_type": 3, + "vin_sim": 3, "module": 3, "wilfulness": 3, "date_of_close": 3, @@ -327,7 +327,7 @@ "has_updated_same_day": 3, "operating_time": 3, "ai_suggestion": 3, - "updated_at": 2 + "updated_at": 3 }, "auto_mapping_enabled": true, "similarity_threshold": 0.6 diff --git a/download_nodejs.bat b/download_nodejs.bat deleted file mode 100644 index 3dd5464..0000000 --- a/download_nodejs.bat +++ /dev/null @@ -1,15 +0,0 @@ -@echo off -echo 下载便携版Node.js... -echo. - -echo 正在下载Node.js便携版... -echo 请手动下载Node.js便携版: -echo 1. 访问: https://nodejs.org/dist/v20.10.0/node-v20.10.0-win-x64.zip -echo 2. 下载并解压到 frontend\nodejs\ 目录 -echo 3. 确保解压后的目录结构为: frontend\nodejs\node.exe -echo. - -echo 下载完成后,请运行: .\start_frontend_portable.bat -echo. - -pause diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 4b7588a..0000000 --- a/frontend/README.md +++ /dev/null @@ -1,211 +0,0 @@ -# TSP智能助手前端 - -基于 Vue 3 + TypeScript + Element Plus 的现代化前端应用。 - -## 技术栈 - -- **Vue 3** - 渐进式 JavaScript 框架 -- **TypeScript** - JavaScript 的超集,提供类型安全 -- **Element Plus** - Vue 3 组件库 -- **Vite** - 快速构建工具 -- **Vue Router** - 官方路由管理器 -- **Pinia** - 状态管理库 -- **Vue I18n** - 国际化解决方案 -- **Socket.IO** - 实时通信 -- **ECharts** - 数据可视化 - -## 项目结构 - -``` -frontend/ -├── src/ -│ ├── components/ # 公共组件 -│ │ └── ChatWidget.vue # 聊天组件 -│ ├── views/ # 页面组件 -│ │ ├── Dashboard.vue # 仪表板 -│ │ ├── Chat.vue # 聊天页面 -│ │ ├── Alerts.vue # 预警管理 -│ │ ├── AlertRules.vue # 预警规则 -│ │ ├── Knowledge.vue # 知识库 -│ │ ├── FieldMapping.vue # 字段映射 -│ │ └── System.vue # 系统设置 -│ ├── stores/ # 状态管理 -│ │ ├── useAppStore.ts # 应用状态 -│ │ ├── useChatStore.ts # 聊天状态 -│ │ └── useAlertStore.ts # 预警状态 -│ ├── router/ # 路由配置 -│ ├── i18n/ # 国际化 -│ ├── App.vue # 根组件 -│ └── main.ts # 入口文件 -├── package.json -├── vite.config.ts -├── tsconfig.json -└── README.md -``` - -## 功能特性 - -### 🎯 核心功能 -- **统一聊天系统** - 支持首页和独立页面的聊天功能 -- **预警管理** - 实时预警监控和规则管理 -- **知识库管理** - 智能知识库的增删改查 -- **字段映射** - 灵活的字段映射配置 -- **系统监控** - 系统状态和性能监控 - -### 🌍 国际化支持 -- 支持中文/英文切换 -- 完整的国际化文本覆盖 -- Element Plus 组件国际化 - -### 🎨 现代化UI -- 响应式设计,支持移动端 -- 暗色/亮色主题切换 -- 优雅的动画效果 -- 统一的视觉风格 - -### 🔧 开发体验 -- TypeScript 类型安全 -- 组件自动导入 -- 热重载开发 -- ESLint 代码规范 - -## 快速开始 - -### 安装依赖 - -```bash -cd frontend -npm install -``` - -### 开发模式 - -```bash -npm run dev -``` - -访问 http://localhost:3000 - -### 构建生产版本 - -```bash -npm run build -``` - -构建文件将输出到 `../src/web/static/dist` - -### 类型检查 - -```bash -npm run type-check -``` - -## 开发指南 - -### 添加新页面 - -1. 在 `src/views/` 创建新的 Vue 组件 -2. 在 `src/router/index.ts` 添加路由配置 -3. 在 `src/i18n/locales/` 添加国际化文本 -4. 在 `src/layout/index.vue` 添加导航菜单 - -### 添加新组件 - -1. 在 `src/components/` 创建组件 -2. 使用 TypeScript 定义 props 和 emits -3. 添加必要的样式 - -### 状态管理 - -使用 Pinia 进行状态管理: - -```typescript -// stores/useExampleStore.ts -import { defineStore } from 'pinia' - -export const useExampleStore = defineStore('example', () => { - const state = ref('') - - const action = () => { - // 状态更新逻辑 - } - - return { state, action } -}) -``` - -### 国际化 - -在组件中使用: - -```vue - - - -``` - -## API 集成 - -### HTTP 请求 - -使用 axios 进行 API 调用: - -```typescript -import axios from 'axios' - -const response = await axios.get('/api/alerts') -``` - -### WebSocket 连接 - -使用 Socket.IO 进行实时通信: - -```typescript -import { io } from 'socket.io-client' - -const socket = io('ws://localhost:8765') -socket.on('message', (data) => { - // 处理消息 -}) -``` - -## 部署说明 - -### 开发环境 - -前端开发服务器运行在端口 3000,通过代理访问后端 API: - -- API 请求代理到 `http://localhost:5000` -- WebSocket 代理到 `ws://localhost:8765` - -### 生产环境 - -1. 运行 `npm run build` 构建生产版本 -2. 构建文件输出到 `../src/web/static/dist` -3. 后端 Flask 应用会直接提供静态文件服务 - -## 浏览器支持 - -- Chrome >= 87 -- Firefox >= 78 -- Safari >= 14 -- Edge >= 88 - -## 贡献指南 - -1. Fork 项目 -2. 创建功能分支 -3. 提交更改 -4. 推送到分支 -5. 创建 Pull Request - -## 许可证 - -MIT License diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index 2ee14ec..0000000 --- a/frontend/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - TSP智能助手 - - -
- - - diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 28d1ca2..0000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "tsp-assistant-frontend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "tsp-assistant-frontend", - "version": "1.0.0", - "type": "module", - "dependencies": { - "@element-plus/icons-vue": "^2.1.0", - "@vitejs/plugin-vue": "^4.2.3", - "axios": "^1.4.0", - "echarts": "^5.4.2", - "element-plus": "^2.3.8", - "pinia": "^2.1.6", - "sass": "^1.64.1", - "socket.io-client": "^4.7.2", - "typescript": "^5.0.2", - "unplugin-auto-import": "^0.16.6", - "unplugin-vue-components": "^0.25.1", - "vite": "^4.4.5", - "vue": "^3.3.4", - "vue-echarts": "^6.6.0", - "vue-i18n": "^9.2.2", - "vue-router": "^4.2.4", - "vue-tsc": "^1.8.5" - }, - "devDependencies": { - "@types/node": "^20.4.5" - } - } - } -} diff --git a/frontend/package.json b/frontend/package.json deleted file mode 100644 index a4fca76..0000000 --- a/frontend/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "tsp-assistant-frontend", - "version": "1.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "build-with-check": "vue-tsc --noEmit && vite build", - "preview": "vite preview", - "type-check": "vue-tsc --noEmit", - "build-safe": "vite build" - }, - "dependencies": { - "vue": "^3.4.0", - "vue-router": "^4.2.5", - "pinia": "^2.1.7", - "element-plus": "^2.4.0", - "@element-plus/icons-vue": "^2.3.1", - "axios": "^1.6.0", - "vue-i18n": "^9.8.0", - "socket.io-client": "^4.7.4", - "echarts": "^5.4.3", - "vue-echarts": "^6.6.1" - }, - "devDependencies": { - "@vitejs/plugin-vue": "^5.0.0", - "typescript": "^5.3.0", - "vue-tsc": "^1.8.25", - "vite": "^5.0.0", - "@types/node": "^20.10.0", - "sass": "^1.69.0", - "unplugin-auto-import": "^0.17.0", - "unplugin-vue-components": "^0.26.0" - } -} diff --git a/frontend/src/App.vue b/frontend/src/App.vue deleted file mode 100644 index 8bbfad2..0000000 --- a/frontend/src/App.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/frontend/src/components/ChatWidget.vue b/frontend/src/components/ChatWidget.vue deleted file mode 100644 index 3225a58..0000000 --- a/frontend/src/components/ChatWidget.vue +++ /dev/null @@ -1,475 +0,0 @@ - - - - - diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts deleted file mode 100644 index a6e3790..0000000 --- a/frontend/src/i18n/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createI18n } from 'vue-i18n' -import zh from './locales/zh.json' -import en from './locales/en.json' - -export function setupI18n() { - const i18n = createI18n({ - legacy: false, - locale: 'zh', - fallbackLocale: 'en', - messages: { - zh, - en - } - }) - - return i18n -} diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json deleted file mode 100644 index b1ca37e..0000000 --- a/frontend/src/i18n/locales/en.json +++ /dev/null @@ -1,252 +0,0 @@ -{ - "common": { - "confirm": "Confirm", - "cancel": "Cancel", - "save": "Save", - "delete": "Delete", - "edit": "Edit", - "add": "Add", - "search": "Search", - "refresh": "Refresh", - "loading": "Loading...", - "success": "Success", - "error": "Error", - "warning": "Warning", - "info": "Info", - "yes": "Yes", - "no": "No", - "close": "Close", - "submit": "Submit", - "reset": "Reset", - "back": "Back", - "next": "Next", - "previous": "Previous", - "finish": "Finish", - "start": "Start", - "stop": "Stop", - "pause": "Pause", - "resume": "Resume", - "enable": "Enable", - "disable": "Disable", - "status": "Status", - "time": "Time", - "name": "Name", - "description": "Description", - "type": "Type", - "level": "Level", - "priority": "Priority", - "category": "Category", - "action": "Action", - "result": "Result", - "message": "Message", - "details": "Details", - "settings": "Settings", - "config": "Configuration", - "version": "Version", - "author": "Author", - "created": "Created", - "updated": "Updated" - }, - "dashboard": { - "title": "Dashboard", - "overview": "Overview", - "systemHealth": "System Health", - "activeAlerts": "Active Alerts", - "recentActivity": "Recent Activity", - "quickActions": "Quick Actions", - "monitoring": "Monitoring", - "startMonitoring": "Start Monitoring", - "stopMonitoring": "Stop Monitoring", - "checkAlerts": "Check Alerts", - "healthScore": "Health Score", - "status": "Status", - "excellent": "Excellent", - "good": "Good", - "fair": "Fair", - "poor": "Poor", - "critical": "Critical", - "unknown": "Unknown" - }, - "chat": { - "title": "Intelligent Chat", - "startChat": "Start Chat", - "endChat": "End Chat", - "sendMessage": "Send Message", - "inputPlaceholder": "Please enter your question...", - "userId": "User ID", - "workOrderId": "Work Order ID", - "workOrderIdPlaceholder": "Leave empty to auto-create", - "createWorkOrder": "Create Work Order", - "quickActions": "Quick Actions", - "sessionInfo": "Session Info", - "connectionStatus": "Connection Status", - "connected": "Connected", - "disconnected": "Disconnected", - "typing": "Assistant is thinking...", - "welcome": "Welcome to TSP Intelligent Assistant", - "welcomeDesc": "Please click 'Start Chat' button to begin chatting", - "chatStarted": "Chat started, please describe your problem.", - "chatEnded": "Chat ended.", - "workOrderCreated": "Work order created successfully! Order ID: {orderId}", - "quickActions": { - "remoteStart": "Remote Start Issue", - "appDisplay": "APP Display Issue", - "bluetoothAuth": "Bluetooth Auth Issue", - "unbindVehicle": "Unbind Vehicle" - }, - "workOrder": { - "title": "Create Work Order", - "titleLabel": "Work Order Title", - "descriptionLabel": "Problem Description", - "categoryLabel": "Problem Category", - "priorityLabel": "Priority", - "categories": { - "technical": "Technical Issue", - "app": "APP Function", - "remoteControl": "Remote Control", - "vehicleBinding": "Vehicle Binding", - "other": "Other" - }, - "priorities": { - "low": "Low", - "medium": "Medium", - "high": "High", - "urgent": "Urgent" - } - } - }, - "alerts": { - "title": "Alert Management", - "rules": { - "title": "Alert Rules", - "addRule": "Add Rule", - "editRule": "Edit Rule", - "deleteRule": "Delete Rule", - "ruleName": "Rule Name", - "ruleType": "Alert Type", - "ruleLevel": "Alert Level", - "threshold": "Threshold", - "condition": "Condition Expression", - "checkInterval": "Check Interval (seconds)", - "cooldown": "Cooldown (seconds)", - "enabled": "Enable Rule", - "types": { - "performance": "Performance Alert", - "quality": "Quality Alert", - "volume": "Volume Alert", - "system": "System Alert", - "business": "Business Alert" - }, - "levels": { - "info": "Info", - "warning": "Warning", - "error": "Error", - "critical": "Critical" - }, - "presetTemplates": "Preset Templates", - "presetCategories": { - "performance": "Performance Alert Templates", - "business": "Business Alert Templates", - "system": "System Alert Templates", - "quality": "Quality Alert Templates" - } - }, - "statistics": { - "critical": "Critical Alerts", - "warning": "Warning Alerts", - "info": "Info Alerts", - "total": "Total Alerts" - }, - "filters": { - "all": "All Alerts", - "critical": "Critical", - "error": "Error", - "warning": "Warning", - "info": "Info" - }, - "sort": { - "timeDesc": "Time Descending", - "timeAsc": "Time Ascending", - "levelDesc": "Level Descending", - "levelAsc": "Level Ascending" - }, - "actions": { - "resolve": "Resolve", - "refresh": "Refresh" - }, - "empty": { - "title": "No Active Alerts", - "description": "System is running normally, no alerts to handle" - } - }, - "knowledge": { - "title": "Knowledge Management", - "add": "Add Knowledge", - "edit": "Edit Knowledge", - "delete": "Delete Knowledge", - "search": "Search Knowledge", - "category": "Category", - "title": "Title", - "content": "Content", - "tags": "Tags", - "status": "Status", - "actions": "Actions" - }, - "fieldMapping": { - "title": "Field Mapping", - "sourceField": "Source Field", - "targetField": "Target Field", - "mappingType": "Mapping Type", - "transformation": "Transformation Rule", - "addMapping": "Add Mapping", - "editMapping": "Edit Mapping", - "deleteMapping": "Delete Mapping", - "testMapping": "Test Mapping", - "importMapping": "Import Mapping", - "exportMapping": "Export Mapping" - }, - "system": { - "title": "System Settings", - "general": "General Settings", - "monitoring": "Monitoring Settings", - "alerts": "Alert Settings", - "integrations": "Integration Settings", - "backup": "Backup Settings", - "logs": "Log Management", - "performance": "Performance Monitoring", - "users": "User Management", - "permissions": "Permission Management" - }, - "navigation": { - "dashboard": "Dashboard", - "chat": "Intelligent Chat", - "alerts": "Alert Management", - "knowledge": "Knowledge Base", - "fieldMapping": "Field Mapping", - "system": "System Settings", - "logout": "Logout" - }, - "notifications": { - "monitoringStarted": "Monitoring service started", - "monitoringStopped": "Monitoring service stopped", - "alertsChecked": "Check completed, found {count} alerts", - "alertResolved": "Alert resolved", - "ruleCreated": "Rule created successfully", - "ruleUpdated": "Rule updated successfully", - "ruleDeleted": "Rule deleted successfully", - "workOrderCreated": "Work order created successfully", - "error": { - "startMonitoring": "Failed to start monitoring", - "stopMonitoring": "Failed to stop monitoring", - "checkAlerts": "Failed to check alerts", - "resolveAlert": "Failed to resolve alert", - "createRule": "Failed to create rule", - "updateRule": "Failed to update rule", - "deleteRule": "Failed to delete rule", - "createWorkOrder": "Failed to create work order", - "websocketConnection": "WebSocket connection failed, please check if server is running", - "requestTimeout": "Request timeout", - "networkError": "Network error" - } - } -} diff --git a/frontend/src/i18n/locales/zh.json b/frontend/src/i18n/locales/zh.json deleted file mode 100644 index 625af49..0000000 --- a/frontend/src/i18n/locales/zh.json +++ /dev/null @@ -1,252 +0,0 @@ -{ - "common": { - "confirm": "确认", - "cancel": "取消", - "save": "保存", - "delete": "删除", - "edit": "编辑", - "add": "添加", - "search": "搜索", - "refresh": "刷新", - "loading": "加载中...", - "success": "成功", - "error": "错误", - "warning": "警告", - "info": "信息", - "yes": "是", - "no": "否", - "close": "关闭", - "submit": "提交", - "reset": "重置", - "back": "返回", - "next": "下一步", - "previous": "上一步", - "finish": "完成", - "start": "开始", - "stop": "停止", - "pause": "暂停", - "resume": "继续", - "enable": "启用", - "disable": "禁用", - "status": "状态", - "time": "时间", - "name": "名称", - "description": "描述", - "type": "类型", - "level": "级别", - "priority": "优先级", - "category": "分类", - "action": "操作", - "result": "结果", - "message": "消息", - "details": "详情", - "settings": "设置", - "config": "配置", - "version": "版本", - "author": "作者", - "created": "创建时间", - "updated": "更新时间" - }, - "dashboard": { - "title": "仪表板", - "overview": "概览", - "systemHealth": "系统健康", - "activeAlerts": "活跃预警", - "recentActivity": "最近活动", - "quickActions": "快速操作", - "monitoring": "监控", - "startMonitoring": "启动监控", - "stopMonitoring": "停止监控", - "checkAlerts": "检查预警", - "healthScore": "健康评分", - "status": "状态", - "excellent": "优秀", - "good": "良好", - "fair": "一般", - "poor": "较差", - "critical": "严重", - "unknown": "未知" - }, - "chat": { - "title": "智能对话", - "startChat": "开始对话", - "endChat": "结束对话", - "sendMessage": "发送消息", - "inputPlaceholder": "请输入您的问题...", - "userId": "用户ID", - "workOrderId": "工单ID", - "workOrderIdPlaceholder": "留空则自动创建", - "createWorkOrder": "创建工单", - "quickActions": "快速操作", - "sessionInfo": "会话信息", - "connectionStatus": "连接状态", - "connected": "已连接", - "disconnected": "未连接", - "typing": "助手正在思考中...", - "welcome": "欢迎使用TSP智能助手", - "welcomeDesc": "请点击\"开始对话\"按钮开始聊天", - "chatStarted": "对话已开始,请描述您的问题。", - "chatEnded": "对话已结束。", - "workOrderCreated": "工单创建成功!工单号: {orderId}", - "quickActions": { - "remoteStart": "远程启动问题", - "appDisplay": "APP显示问题", - "bluetoothAuth": "蓝牙授权问题", - "unbindVehicle": "解绑车辆" - }, - "workOrder": { - "title": "创建工单", - "titleLabel": "工单标题", - "descriptionLabel": "问题描述", - "categoryLabel": "问题分类", - "priorityLabel": "优先级", - "categories": { - "technical": "技术问题", - "app": "APP功能", - "remoteControl": "远程控制", - "vehicleBinding": "车辆绑定", - "other": "其他" - }, - "priorities": { - "low": "低", - "medium": "中", - "high": "高", - "urgent": "紧急" - } - } - }, - "alerts": { - "title": "预警管理", - "rules": { - "title": "预警规则", - "addRule": "添加规则", - "editRule": "编辑规则", - "deleteRule": "删除规则", - "ruleName": "规则名称", - "ruleType": "预警类型", - "ruleLevel": "预警级别", - "threshold": "阈值", - "condition": "条件表达式", - "checkInterval": "检查间隔(秒)", - "cooldown": "冷却时间(秒)", - "enabled": "启用规则", - "types": { - "performance": "性能预警", - "quality": "质量预警", - "volume": "量级预警", - "system": "系统预警", - "business": "业务预警" - }, - "levels": { - "info": "信息", - "warning": "警告", - "error": "错误", - "critical": "严重" - }, - "presetTemplates": "预设模板", - "presetCategories": { - "performance": "性能预警模板", - "business": "业务预警模板", - "system": "系统预警模板", - "quality": "质量预警模板" - } - }, - "statistics": { - "critical": "严重预警", - "warning": "警告预警", - "info": "信息预警", - "total": "总预警数" - }, - "filters": { - "all": "全部预警", - "critical": "严重", - "error": "错误", - "warning": "警告", - "info": "信息" - }, - "sort": { - "timeDesc": "时间降序", - "timeAsc": "时间升序", - "levelDesc": "级别降序", - "levelAsc": "级别升序" - }, - "actions": { - "resolve": "解决", - "refresh": "刷新" - }, - "empty": { - "title": "暂无活跃预警", - "description": "系统运行正常,没有需要处理的预警" - } - }, - "knowledge": { - "title": "知识库管理", - "add": "添加知识", - "edit": "编辑知识", - "delete": "删除知识", - "search": "搜索知识", - "category": "分类", - "title": "标题", - "content": "内容", - "tags": "标签", - "status": "状态", - "actions": "操作" - }, - "fieldMapping": { - "title": "字段映射", - "sourceField": "源字段", - "targetField": "目标字段", - "mappingType": "映射类型", - "transformation": "转换规则", - "addMapping": "添加映射", - "editMapping": "编辑映射", - "deleteMapping": "删除映射", - "testMapping": "测试映射", - "importMapping": "导入映射", - "exportMapping": "导出映射" - }, - "system": { - "title": "系统设置", - "general": "常规设置", - "monitoring": "监控设置", - "alerts": "预警设置", - "integrations": "集成设置", - "backup": "备份设置", - "logs": "日志管理", - "performance": "性能监控", - "users": "用户管理", - "permissions": "权限管理" - }, - "navigation": { - "dashboard": "仪表板", - "chat": "智能对话", - "alerts": "预警管理", - "knowledge": "知识库", - "fieldMapping": "字段映射", - "system": "系统设置", - "logout": "退出登录" - }, - "notifications": { - "monitoringStarted": "监控服务已启动", - "monitoringStopped": "监控服务已停止", - "alertsChecked": "检查完成,发现 {count} 个预警", - "alertResolved": "预警已解决", - "ruleCreated": "规则创建成功", - "ruleUpdated": "规则更新成功", - "ruleDeleted": "规则删除成功", - "workOrderCreated": "工单创建成功", - "error": { - "startMonitoring": "启动监控失败", - "stopMonitoring": "停止监控失败", - "checkAlerts": "检查预警失败", - "resolveAlert": "解决预警失败", - "createRule": "创建规则失败", - "updateRule": "更新规则失败", - "deleteRule": "删除规则失败", - "createWorkOrder": "创建工单失败", - "websocketConnection": "WebSocket连接失败,请检查服务器是否启动", - "requestTimeout": "请求超时", - "networkError": "网络错误" - } - } -} diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue deleted file mode 100644 index 5be4cd5..0000000 --- a/frontend/src/layout/index.vue +++ /dev/null @@ -1,304 +0,0 @@ - - - - - diff --git a/frontend/src/main.ts b/frontend/src/main.ts deleted file mode 100644 index bdfacef..0000000 --- a/frontend/src/main.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createApp } from 'vue' -import { createPinia } from 'pinia' -import ElementPlus from 'element-plus' -import 'element-plus/dist/index.css' -import 'element-plus/theme-chalk/dark/css-vars.css' -import * as ElementPlusIconsVue from '@element-plus/icons-vue' -import zhCn from 'element-plus/dist/locale/zh-cn.mjs' - -import App from './App.vue' -import router from './router' -import { setupI18n } from './i18n' - -const app = createApp(App) - -// 注册Element Plus图标 -for (const [key, component] of Object.entries(ElementPlusIconsVue)) { - app.component(key, component) -} - -app.use(createPinia()) -app.use(router) -app.use(ElementPlus, { - locale: zhCn, -}) -app.use(setupI18n()) - -app.mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts deleted file mode 100644 index 37afe6d..0000000 --- a/frontend/src/router/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { createRouter, createWebHistory } from 'vue-router' -import type { RouteRecordRaw } from 'vue-router' - -const routes: RouteRecordRaw[] = [ - { - path: '/', - name: 'Layout', - component: () => import('@/layout/index.vue'), - children: [ - { - path: '', - name: 'Dashboard', - component: () => import('@/views/Dashboard.vue'), - meta: { title: 'dashboard.title' } - }, - { - path: '/chat', - name: 'Chat', - component: () => import('@/views/Chat.vue'), - meta: { title: 'chat.title' } - }, - { - path: '/alerts', - name: 'Alerts', - component: () => import('@/views/Alerts.vue'), - meta: { title: 'alerts.title' } - }, - { - path: '/alerts/rules', - name: 'AlertRules', - component: () => import('@/views/AlertRules.vue'), - meta: { title: 'alerts.rules.title' } - }, - { - path: '/knowledge', - name: 'Knowledge', - component: () => import('@/views/Knowledge.vue'), - meta: { title: 'knowledge.title' } - }, - { - path: '/field-mapping', - name: 'FieldMapping', - component: () => import('@/views/FieldMapping.vue'), - meta: { title: 'fieldMapping.title' } - }, - { - path: '/system', - name: 'System', - component: () => import('@/views/System.vue'), - meta: { title: 'system.title' } - } - ] - } -] - -const router = createRouter({ - history: createWebHistory(), - routes -}) - -export default router diff --git a/frontend/src/stores/useAlertStore.ts b/frontend/src/stores/useAlertStore.ts deleted file mode 100644 index 8d5eb97..0000000 --- a/frontend/src/stores/useAlertStore.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import axios from 'axios' - -export interface Alert { - id: number - rule_name: string - message: string - level: 'critical' | 'error' | 'warning' | 'info' - alert_type: 'performance' | 'quality' | 'volume' | 'system' | 'business' - data?: any - created_at: string - resolved_at?: string - status: 'active' | 'resolved' -} - -export interface AlertRule { - name: string - description?: string - alert_type: 'performance' | 'quality' | 'volume' | 'system' | 'business' - level: 'critical' | 'error' | 'warning' | 'info' - threshold: number - condition: string - enabled: boolean - check_interval: number - cooldown: number -} - -export interface SystemHealth { - health_score: number - status: 'excellent' | 'good' | 'fair' | 'poor' | 'critical' | 'unknown' - details?: any -} - -export interface MonitorStatus { - monitor_status: 'running' | 'stopped' | 'unknown' -} - -export const useAlertStore = defineStore('alert', () => { - // 状态 - const alerts = ref([]) - const rules = ref([]) - const health = ref({ health_score: 0, status: 'unknown' }) - const monitorStatus = ref({ monitor_status: 'unknown' }) - const loading = ref(false) - const alertFilter = ref('all') - const alertSort = ref('time-desc') - - // 计算属性 - const filteredAlerts = computed(() => { - let filtered = alerts.value - - // 应用过滤 - if (alertFilter.value !== 'all') { - filtered = filtered.filter(alert => alert.level === alertFilter.value) - } - - // 应用排序 - filtered.sort((a, b) => { - switch (alertSort.value) { - case 'time-desc': - return new Date(b.created_at).getTime() - new Date(a.created_at).getTime() - case 'time-asc': - return new Date(a.created_at).getTime() - new Date(b.created_at).getTime() - case 'level-desc': - const levelOrder = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 } - return (levelOrder[b.level] || 0) - (levelOrder[a.level] || 0) - case 'level-asc': - const levelOrderAsc = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 } - return (levelOrderAsc[a.level] || 0) - (levelOrderAsc[b.level] || 0) - default: - return 0 - } - }) - - return filtered - }) - - const alertStatistics = computed(() => { - const stats = alerts.value.reduce((acc, alert) => { - acc[alert.level] = (acc[alert.level] || 0) + 1 - acc.total = (acc.total || 0) + 1 - return acc - }, {} as Record) - - return { - critical: stats.critical || 0, - warning: stats.warning || 0, - info: stats.info || 0, - total: stats.total || 0 - } - }) - - // 动作 - const loadAlerts = async () => { - try { - loading.value = true - const response = await axios.get('/api/alerts') - alerts.value = response.data - } catch (error) { - console.error('加载预警失败:', error) - throw error - } finally { - loading.value = false - } - } - - const loadRules = async () => { - try { - loading.value = true - const response = await axios.get('/api/rules') - rules.value = response.data - } catch (error) { - console.error('加载规则失败:', error) - throw error - } finally { - loading.value = false - } - } - - const loadHealth = async () => { - try { - const response = await axios.get('/api/health') - health.value = response.data - } catch (error) { - console.error('加载健康状态失败:', error) - throw error - } - } - - const loadMonitorStatus = async () => { - try { - const response = await axios.get('/api/monitor/status') - monitorStatus.value = response.data - } catch (error) { - console.error('加载监控状态失败:', error) - throw error - } - } - - const startMonitoring = async () => { - try { - const response = await axios.post('/api/monitor/start') - if (response.data.success) { - await loadMonitorStatus() - return true - } - return false - } catch (error) { - console.error('启动监控失败:', error) - throw error - } - } - - const stopMonitoring = async () => { - try { - const response = await axios.post('/api/monitor/stop') - if (response.data.success) { - await loadMonitorStatus() - return true - } - return false - } catch (error) { - console.error('停止监控失败:', error) - throw error - } - } - - const checkAlerts = async () => { - try { - const response = await axios.post('/api/check-alerts') - if (response.data.success) { - await loadAlerts() - return response.data.count - } - return 0 - } catch (error) { - console.error('检查预警失败:', error) - throw error - } - } - - const resolveAlert = async (alertId: number) => { - try { - const response = await axios.post(`/api/alerts/${alertId}/resolve`) - if (response.data.success) { - await loadAlerts() - return true - } - return false - } catch (error) { - console.error('解决预警失败:', error) - throw error - } - } - - const createRule = async (rule: Omit & { name: string }) => { - try { - const response = await axios.post('/api/rules', rule) - if (response.data.success) { - await loadRules() - return true - } - return false - } catch (error) { - console.error('创建规则失败:', error) - throw error - } - } - - const updateRule = async (originalName: string, rule: AlertRule) => { - try { - const response = await axios.put(`/api/rules/${originalName}`, rule) - if (response.data.success) { - await loadRules() - return true - } - return false - } catch (error) { - console.error('更新规则失败:', error) - throw error - } - } - - const deleteRule = async (ruleName: string) => { - try { - const response = await axios.delete(`/api/rules/${ruleName}`) - if (response.data.success) { - await loadRules() - return true - } - return false - } catch (error) { - console.error('删除规则失败:', error) - throw error - } - } - - const setAlertFilter = (filter: string) => { - alertFilter.value = filter - } - - const setAlertSort = (sort: string) => { - alertSort.value = sort - } - - const loadInitialData = async () => { - await Promise.all([ - loadHealth(), - loadAlerts(), - loadRules(), - loadMonitorStatus() - ]) - } - - return { - // 状态 - alerts, - rules, - health, - monitorStatus, - loading, - alertFilter, - alertSort, - - // 计算属性 - filteredAlerts, - alertStatistics, - - // 动作 - loadAlerts, - loadRules, - loadHealth, - loadMonitorStatus, - startMonitoring, - stopMonitoring, - checkAlerts, - resolveAlert, - createRule, - updateRule, - deleteRule, - setAlertFilter, - setAlertSort, - loadInitialData - } -}) diff --git a/frontend/src/stores/useAppStore.ts b/frontend/src/stores/useAppStore.ts deleted file mode 100644 index 6000a2a..0000000 --- a/frontend/src/stores/useAppStore.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' - -export const useAppStore = defineStore('app', () => { - // 状态 - const loading = ref(false) - const theme = ref<'light' | 'dark'>('light') - const language = ref<'zh' | 'en'>('zh') - const sidebarCollapsed = ref(false) - - // 计算属性 - const isDark = computed(() => theme.value === 'dark') - - // 动作 - const setLoading = (value: boolean) => { - loading.value = value - } - - const setTheme = (value: 'light' | 'dark') => { - theme.value = value - document.documentElement.classList.toggle('dark', value === 'dark') - localStorage.setItem('theme', value) - } - - const setLanguage = (value: 'zh' | 'en') => { - language.value = value - localStorage.setItem('language', value) - } - - const toggleSidebar = () => { - sidebarCollapsed.value = !sidebarCollapsed.value - } - - const init = () => { - // 恢复主题设置 - const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' - if (savedTheme) { - setTheme(savedTheme) - } - - // 恢复语言设置 - const savedLanguage = localStorage.getItem('language') as 'zh' | 'en' - if (savedLanguage) { - setLanguage(savedLanguage) - } - } - - return { - loading, - theme, - language, - sidebarCollapsed, - isDark, - setLoading, - setTheme, - setLanguage, - toggleSidebar, - init - } -}) diff --git a/frontend/src/stores/useChatStore.ts b/frontend/src/stores/useChatStore.ts deleted file mode 100644 index 35850e3..0000000 --- a/frontend/src/stores/useChatStore.ts +++ /dev/null @@ -1,297 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import { io, type Socket } from 'socket.io-client' - -export interface ChatMessage { - id: string - role: 'user' | 'assistant' | 'system' - content: string - timestamp: Date - metadata?: { - knowledge_used?: string[] - confidence_score?: number - work_order_id?: string - } -} - -export interface ChatSession { - id: string - userId: string - workOrderId?: string - messages: ChatMessage[] - status: 'active' | 'ended' - createdAt: Date -} - -export const useChatStore = defineStore('chat', () => { - // 状态 - const socket = ref(null) - const isConnected = ref(false) - const currentSession = ref(null) - const messages = ref([]) - const isTyping = ref(false) - const userId = ref('user_001') - const workOrderId = ref('') - - // 计算属性 - const hasActiveSession = computed(() => - currentSession.value && currentSession.value.status === 'active' - ) - - const messageCount = computed(() => messages.value.length) - - // 动作 - const connectWebSocket = () => { - return new Promise((resolve, reject) => { - try { - socket.value = io('ws://localhost:8765', { - transports: ['websocket'] - }) - - socket.value.on('connect', () => { - isConnected.value = true - resolve() - }) - - socket.value.on('disconnect', () => { - isConnected.value = false - }) - - socket.value.on('error', (error) => { - console.error('WebSocket error:', error) - reject(error) - }) - - socket.value.on('message_response', (data) => { - handleMessageResponse(data) - }) - - socket.value.on('typing_start', () => { - isTyping.value = true - }) - - socket.value.on('typing_end', () => { - isTyping.value = false - }) - - } catch (error) { - reject(error) - } - }) - } - - const disconnectWebSocket = () => { - if (socket.value) { - socket.value.disconnect() - socket.value = null - isConnected.value = false - } - } - - const startChat = async () => { - try { - if (!socket.value) { - await connectWebSocket() - } - - const response = await sendSocketMessage({ - type: 'create_session', - user_id: userId.value, - work_order_id: workOrderId.value ? parseInt(workOrderId.value) : null - }) - - if (response.type === 'session_created') { - currentSession.value = { - id: response.session_id, - userId: userId.value, - workOrderId: workOrderId.value, - messages: [], - status: 'active', - createdAt: new Date() - } - - addMessage('system', '对话已开始,请描述您的问题。') - return true - } - - return false - } catch (error) { - console.error('启动对话失败:', error) - throw error - } - } - - const endChat = async () => { - try { - if (currentSession.value) { - await sendSocketMessage({ - type: 'end_session', - session_id: currentSession.value.id - }) - - currentSession.value.status = 'ended' - addMessage('system', '对话已结束。') - } - } catch (error) { - console.error('结束对话失败:', error) - } - } - - const sendMessage = async (content: string) => { - if (!currentSession.value || !content.trim()) { - return - } - - // 添加用户消息 - addMessage('user', content) - - try { - const response = await sendSocketMessage({ - type: 'send_message', - session_id: currentSession.value.id, - message: content - }) - - if (response.type === 'message_response' && response.result.success) { - const result = response.result - - // 添加助手回复 - addMessage('assistant', result.content, { - knowledge_used: result.knowledge_used, - confidence_score: result.confidence_score, - work_order_id: result.work_order_id - }) - - // 更新工单ID - if (result.work_order_id) { - workOrderId.value = result.work_order_id.toString() - } - } else { - addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。') - } - } catch (error) { - console.error('发送消息失败:', error) - addMessage('assistant', '发送消息失败,请检查网络连接。') - } - } - - const createWorkOrder = async (data: { - title: string - description: string - category: string - priority: string - }) => { - if (!currentSession.value) { - throw new Error('没有活跃的会话') - } - - try { - const response = await sendSocketMessage({ - type: 'create_work_order', - session_id: currentSession.value.id, - ...data - }) - - if (response.type === 'work_order_created' && response.result.success) { - workOrderId.value = response.result.work_order_id.toString() - addMessage('system', `工单创建成功!工单号: ${response.result.order_id}`) - return response.result - } else { - throw new Error(response.result.error || '创建工单失败') - } - } catch (error) { - console.error('创建工单失败:', error) - throw error - } - } - - const sendSocketMessage = (message: any): Promise => { - return new Promise((resolve, reject) => { - if (!socket.value) { - reject(new Error('WebSocket未连接')) - return - } - - const messageId = 'msg_' + Date.now() - message.messageId = messageId - - const timeout = setTimeout(() => { - reject(new Error('请求超时')) - }, 10000) - - const handleResponse = (data: any) => { - if (data.messageId === messageId) { - clearTimeout(timeout) - socket.value?.off('message_response', handleResponse) - resolve(data) - } - } - - socket.value.on('message_response', handleResponse) - socket.value.emit('message', message) - }) - } - - const addMessage = (role: 'user' | 'assistant' | 'system', content: string, metadata?: any) => { - const message: ChatMessage = { - id: 'msg_' + Date.now() + '_' + Math.random(), - role, - content, - timestamp: new Date(), - metadata - } - - messages.value.push(message) - - if (currentSession.value) { - currentSession.value.messages.push(message) - } - } - - const clearMessages = () => { - messages.value = [] - if (currentSession.value) { - currentSession.value.messages = [] - } - } - - const setUserId = (id: string) => { - userId.value = id - } - - const setWorkOrderId = (id: string) => { - workOrderId.value = id - } - - const handleMessageResponse = (data: any) => { - // 处理WebSocket消息响应 - console.log('收到消息响应:', data) - } - - return { - // 状态 - socket, - isConnected, - currentSession, - messages, - isTyping, - userId, - workOrderId, - - // 计算属性 - hasActiveSession, - messageCount, - - // 动作 - connectWebSocket, - disconnectWebSocket, - startChat, - endChat, - sendMessage, - createWorkOrder, - addMessage, - clearMessages, - setUserId, - setWorkOrderId - } -}) diff --git a/frontend/src/views/AlertRules.vue b/frontend/src/views/AlertRules.vue deleted file mode 100644 index 335df7b..0000000 --- a/frontend/src/views/AlertRules.vue +++ /dev/null @@ -1,561 +0,0 @@ - - - - - diff --git a/frontend/src/views/Alerts.vue b/frontend/src/views/Alerts.vue deleted file mode 100644 index adcaf7b..0000000 --- a/frontend/src/views/Alerts.vue +++ /dev/null @@ -1,463 +0,0 @@ - - - - - diff --git a/frontend/src/views/Chat.vue b/frontend/src/views/Chat.vue deleted file mode 100644 index 2c896eb..0000000 --- a/frontend/src/views/Chat.vue +++ /dev/null @@ -1,753 +0,0 @@ - - - - - diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue deleted file mode 100644 index 0613c42..0000000 --- a/frontend/src/views/Dashboard.vue +++ /dev/null @@ -1,667 +0,0 @@ - - - - - diff --git a/frontend/src/views/FieldMapping.vue b/frontend/src/views/FieldMapping.vue deleted file mode 100644 index 099e453..0000000 --- a/frontend/src/views/FieldMapping.vue +++ /dev/null @@ -1,366 +0,0 @@ - - - - - diff --git a/frontend/src/views/Knowledge.vue b/frontend/src/views/Knowledge.vue deleted file mode 100644 index b34ef05..0000000 --- a/frontend/src/views/Knowledge.vue +++ /dev/null @@ -1,330 +0,0 @@ - - - - - diff --git a/frontend/src/views/System.vue b/frontend/src/views/System.vue deleted file mode 100644 index a230680..0000000 --- a/frontend/src/views/System.vue +++ /dev/null @@ -1,409 +0,0 @@ - - - - - diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json deleted file mode 100644 index 430fe8d..0000000 --- a/frontend/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "preserve", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json deleted file mode 100644 index 42872c5..0000000 --- a/frontend/tsconfig.node.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts deleted file mode 100644 index ab77d07..0000000 --- a/frontend/vite.config.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import { resolve } from 'path' -import AutoImport from 'unplugin-auto-import/vite' -import Components from 'unplugin-vue-components/vite' -import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' - -export default defineConfig({ - plugins: [ - vue(), - AutoImport({ - resolvers: [ElementPlusResolver()], - imports: ['vue', 'vue-router', 'pinia'], - dts: true - }), - Components({ - resolvers: [ElementPlusResolver()], - dts: true - }) - ], - resolve: { - alias: { - '@': resolve(__dirname, 'src') - } - }, - server: { - port: 3000, - proxy: { - '/api': { - target: 'http://localhost:5000', - changeOrigin: true - }, - '/ws': { - target: 'ws://localhost:8765', - ws: true - } - } - }, - build: { - outDir: '../src/web/static/dist', - emptyOutDir: true - } -}) diff --git a/install_dependencies.sh b/install_dependencies.sh deleted file mode 100644 index 8be5b5f..0000000 --- a/install_dependencies.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash - -echo "TSP智能助手依赖安装脚本" -echo "==========================" -echo - -# 检测操作系统 -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - OS="linux" -elif [[ "$OSTYPE" == "darwin"* ]]; then - OS="macos" -else - OS="unknown" -fi - -echo "检测到操作系统: $OS" -echo - -# 安装Node.js和npm -install_nodejs() { - echo "安装Node.js和npm..." - - if command -v node &> /dev/null; then - echo "Node.js已安装: $(node --version)" - return 0 - fi - - case $OS in - "linux") - # Ubuntu/Debian - if command -v apt &> /dev/null; then - echo "使用apt安装Node.js..." - curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - - sudo apt-get install -y nodejs - # CentOS/RHEL - elif command -v yum &> /dev/null; then - echo "使用yum安装Node.js..." - curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - - sudo yum install -y nodejs - # Arch Linux - elif command -v pacman &> /dev/null; then - echo "使用pacman安装Node.js..." - sudo pacman -S nodejs npm - else - echo "请手动安装Node.js: https://nodejs.org/" - return 1 - fi - ;; - "macos") - if command -v brew &> /dev/null; then - echo "使用Homebrew安装Node.js..." - brew install node - else - echo "请安装Homebrew或手动安装Node.js: https://nodejs.org/" - return 1 - fi - ;; - *) - echo "请手动安装Node.js: https://nodejs.org/" - return 1 - ;; - esac -} - -# 安装Python依赖 -install_python_deps() { - echo "安装Python依赖..." - - if [ -f "requirements.txt" ]; then - if command -v python3 &> /dev/null; then - python3 -m pip install -r requirements.txt - elif command -v python &> /dev/null; then - python -m pip install -r requirements.txt - else - echo "警告: 未找到Python" - fi - else - echo "警告: 未找到requirements.txt文件" - fi -} - -# 安装前端依赖 -install_frontend_deps() { - echo "安装前端依赖..." - - if [ -d "frontend" ]; then - cd frontend - if [ -f "package.json" ]; then - npm install - else - echo "警告: 未找到package.json文件" - fi - cd .. - else - echo "警告: 未找到frontend目录" - fi -} - -# 主安装流程 -main() { - echo "开始安装依赖..." - echo - - # 安装Node.js - install_nodejs - if [ $? -eq 0 ]; then - echo "Node.js安装成功: $(node --version)" - echo "npm版本: $(npm --version)" - else - echo "Node.js安装失败,请手动安装" - fi - - echo - - # 安装Python依赖 - install_python_deps - - echo - - # 安装前端依赖 - install_frontend_deps - - echo - echo "依赖安装完成!" - echo - echo "使用方法:" - echo " 启动传统版本: ./start_traditional.sh" - echo " 启动前端开发: ./start_frontend.sh" - echo " 构建前端: ./build_frontend.sh" -} - -# 执行主函数 -main diff --git a/src/knowledge_base/knowledge_manager.py b/src/knowledge_base/knowledge_manager.py index 66e5a59..104f8c5 100644 --- a/src/knowledge_base/knowledge_manager.py +++ b/src/knowledge_base/knowledge_manager.py @@ -130,50 +130,69 @@ class KnowledgeManager: entries = session.query(KnowledgeEntry).filter(KnowledgeEntry.is_active == True).all() if not entries: + logger.warning("知识库中没有活跃条目") return [] - # 计算相似度 - texts = [entry.question + " " + entry.answer for entry in entries] - - # 确保向量器已训练 - try: - vocab_ok = hasattr(self.vectorizer, 'vocabulary_') and bool(self.vectorizer.vocabulary_) - if not vocab_ok: - self.vectorizer.fit(texts) - query_vector = self.vectorizer.transform([query]) - entry_vectors = self.vectorizer.transform(texts) - similarities = cosine_similarity(query_vector, entry_vectors)[0] - except Exception as vec_err: - logger.warning(f"TF-IDF搜索失败,回退到子串匹配: {vec_err}") - # 回退:子串匹配评分 - similarities = [] - q = query.strip() - for t in texts: - if not q: - similarities.append(0.0) - else: - score = 1.0 if q in t else 0.0 - similarities.append(score) - similarities = np.array(similarities, dtype=float) - - # 获取top_k个最相似的条目 - top_indices = np.argsort(similarities)[-top_k:][::-1] + # 如果查询为空,返回所有条目 + if not query.strip(): + logger.info("查询为空,返回所有条目") + return [{ + "id": entry.id, + "question": entry.question, + "answer": entry.answer, + "category": entry.category, + "confidence_score": entry.confidence_score, + "similarity_score": 1.0, + "usage_count": entry.usage_count, + "is_verified": entry.is_verified + } for entry in entries[:top_k]] + # 使用简化的关键词匹配搜索 + q = query.strip().lower() results = [] - for idx in top_indices: - if similarities[idx] > 0.1: # 最小相似度阈值 - entry = entries[idx] + + for entry in entries: + # 组合问题和答案进行搜索 + search_text = (entry.question + " " + entry.answer).lower() + + # 计算匹配分数 + score = 0.0 + + # 完全匹配 + if q in search_text: + score = 1.0 + else: + # 分词匹配 + query_words = q.split() + text_words = search_text.split() + + # 计算单词匹配度 + matched_words = 0 + for word in query_words: + if word in text_words: + matched_words += 1 + + if matched_words > 0: + score = matched_words / len(query_words) * 0.8 + + # 如果分数大于0,添加到结果中 + if score > 0: results.append({ "id": entry.id, "question": entry.question, "answer": entry.answer, "category": entry.category, "confidence_score": entry.confidence_score, - "similarity_score": float(similarities[idx]), + "similarity_score": score, "usage_count": entry.usage_count, "is_verified": entry.is_verified }) + # 按相似度排序并返回top_k个结果 + results.sort(key=lambda x: x['similarity_score'], reverse=True) + results = results[:top_k] + + logger.info(f"搜索查询 '{query}' 返回 {len(results)} 个结果") return results except Exception as e: diff --git a/src/web/app.py b/src/web/app.py index 13ed9e5..ca81493 100644 --- a/src/web/app.py +++ b/src/web/app.py @@ -9,6 +9,7 @@ import sys import os import logging from datetime import datetime, timedelta +from typing import Dict, Any from flask import Flask, render_template, request, jsonify, send_from_directory from flask_cors import CORS @@ -16,14 +17,11 @@ from flask_cors import CORS # 添加项目根目录到Python路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -# 延迟导入,避免启动时重复初始化 -# from src.main import TSPAssistant -# from src.agent_assistant import TSPAgentAssistant -# from src.dialogue.realtime_chat import RealtimeChatManager -# from src.vehicle.vehicle_data_manager import VehicleDataManager +# 导入核心模块 from src.core.database import db_manager from src.core.models import Conversation, Alert, WorkOrder from src.core.query_optimizer import query_optimizer +from src.web.service_manager import service_manager # 导入蓝图 from src.web.blueprints.alerts import alerts_bp @@ -33,6 +31,7 @@ from src.web.blueprints.knowledge import knowledge_bp from src.web.blueprints.monitoring import monitoring_bp from src.web.blueprints.system import system_bp from src.web.blueprints.feishu_sync import feishu_sync_bp +from src.web.blueprints.core import core_bp # 配置日志 logger = logging.getLogger(__name__) @@ -59,43 +58,7 @@ UPLOAD_FOLDER = 'uploads' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size -# 延迟初始化TSP助手和Agent助手(避免启动时重复初始化) -assistant = None -agent_assistant = None -chat_manager = None -vehicle_manager = None - -def get_assistant(): - """获取TSP助手实例(懒加载)""" - global assistant - if assistant is None: - from src.main import TSPAssistant - assistant = TSPAssistant() - return assistant - -def get_agent_assistant(): - """获取Agent助手实例(懒加载)""" - global agent_assistant - if agent_assistant is None: - from src.agent_assistant import TSPAgentAssistant - agent_assistant = TSPAgentAssistant() - return agent_assistant - -def get_chat_manager(): - """获取聊天管理器实例(懒加载)""" - global chat_manager - if chat_manager is None: - from src.dialogue.realtime_chat import RealtimeChatManager - chat_manager = RealtimeChatManager() - return chat_manager - -def get_vehicle_manager(): - """获取车辆数据管理器实例(懒加载)""" - global vehicle_manager - if vehicle_manager is None: - from src.vehicle.vehicle_data_manager import VehicleDataManager - vehicle_manager = VehicleDataManager() - return vehicle_manager +# 使用统一的服务管理器 # 注册蓝图 app.register_blueprint(alerts_bp) @@ -105,6 +68,7 @@ app.register_blueprint(knowledge_bp) app.register_blueprint(monitoring_bp) app.register_blueprint(system_bp) app.register_blueprint(feishu_sync_bp) +app.register_blueprint(core_bp) # 页面路由 @app.route('/') @@ -132,157 +96,15 @@ def uploaded_file(filename): """提供上传文件的下载服务""" return send_from_directory(app.config['UPLOAD_FOLDER'], filename) -# 核心API路由 -@app.route('/api/health') -def get_health(): - """获取系统健康状态(附加1小时业务指标)""" - try: - base = get_assistant().get_system_health() or {} - # 追加数据库近1小时指标 - with db_manager.get_session() as session: - since = datetime.now() - timedelta(hours=1) - conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() - resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] - avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 - open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() - levels = session.query(Alert.level).filter(Alert.is_active == True).all() - level_map = {} - for (lvl,) in levels: - level_map[lvl] = level_map.get(lvl, 0) + 1 - base.update({ - "throughput_1h": conv_count, - "avg_response_time_1h": avg_resp, - "open_workorders": open_wos, - "active_alerts_by_level": level_map - }) - return jsonify(base) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules') -def get_rules(): - """获取预警规则列表""" - try: - rules = get_assistant().alert_system.rules - rules_data = [] - for name, rule in rules.items(): - rules_data.append({ - "name": rule.name, - "description": rule.description, - "alert_type": rule.alert_type.value, - "level": rule.level.value, - "threshold": rule.threshold, - "condition": rule.condition, - "enabled": rule.enabled, - "check_interval": rule.check_interval, - "cooldown": rule.cooldown - }) - return jsonify(rules_data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules', methods=['POST']) -def create_rule(): - """创建预警规则""" - try: - from src.analytics.alert_system import AlertRule, AlertLevel, AlertType - data = request.get_json() - rule = AlertRule( - name=data['name'], - description=data['description'], - alert_type=AlertType(data['alert_type']), - level=AlertLevel(data['level']), - threshold=float(data['threshold']), - condition=data['condition'], - enabled=data.get('enabled', True), - check_interval=int(data.get('check_interval', 300)), - cooldown=int(data.get('cooldown', 3600)) - ) - - success = get_assistant().alert_system.add_custom_rule(rule) - if success: - return jsonify({"success": True, "message": "规则创建成功"}) - else: - return jsonify({"success": False, "message": "规则创建失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['PUT']) -def update_rule(rule_name): - """更新预警规则""" - try: - data = request.get_json() - success = get_assistant().alert_system.update_rule(rule_name, **data) - if success: - return jsonify({"success": True, "message": "规则更新成功"}) - else: - return jsonify({"success": False, "message": "规则更新失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['DELETE']) -def delete_rule(rule_name): - """删除预警规则""" - try: - success = get_assistant().alert_system.delete_rule(rule_name) - if success: - return jsonify({"success": True, "message": "规则删除成功"}) - else: - return jsonify({"success": False, "message": "规则删除失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/start', methods=['POST']) -def start_monitoring(): - """启动监控服务""" - try: - success = get_assistant().start_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已启动"}) - else: - return jsonify({"success": False, "message": "启动监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/stop', methods=['POST']) -def stop_monitoring(): - """停止监控服务""" - try: - success = get_assistant().stop_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已停止"}) - else: - return jsonify({"success": False, "message": "停止监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/status') -def get_monitor_status(): - """获取监控服务状态""" - try: - health = get_assistant().get_system_health() - return jsonify({ - "monitor_status": health.get("monitor_status", "unknown"), - "health_score": health.get("health_score", 0), - "active_alerts": health.get("active_alerts", 0) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/check-alerts', methods=['POST']) -def check_alerts(): - """手动检查预警""" - try: - alerts = get_assistant().check_alerts() - return jsonify({ - "success": True, - "alerts": alerts, - "count": len(alerts) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 +# ============================================================================ +# 核心API路由 - 已迁移到蓝图 +# ============================================================================ +# 健康检查、预警规则、监控状态等核心功能已迁移到 core 蓝图 +# 分析数据相关功能也已迁移到 core 蓝图 +# ============================================================================ # 实时对话相关路由 +# ============================================================================ @app.route('/api/chat/session', methods=['POST']) def create_chat_session(): """创建对话会话""" @@ -291,7 +113,7 @@ def create_chat_session(): user_id = data.get('user_id', 'anonymous') work_order_id = data.get('work_order_id') - session_id = get_chat_manager().create_session(user_id, work_order_id) + session_id = service_manager.get_chat_manager().create_session(user_id, work_order_id) return jsonify({ "success": True, @@ -312,7 +134,7 @@ def send_chat_message(): if not session_id or not message: return jsonify({"error": "缺少必要参数"}), 400 - result = get_chat_manager().process_message(session_id, message) + result = service_manager.get_chat_manager().process_message(session_id, message) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -321,7 +143,7 @@ def send_chat_message(): def get_chat_history(session_id): """获取对话历史""" try: - history = get_chat_manager().get_session_history(session_id) + history = service_manager.get_chat_manager().get_session_history(session_id) return jsonify({ "success": True, "history": history @@ -343,7 +165,7 @@ def create_work_order(): if not session_id or not title or not description: return jsonify({"error": "缺少必要参数"}), 400 - result = get_chat_manager().create_work_order(session_id, title, description, category, priority) + result = service_manager.get_chat_manager().create_work_order(session_id, title, description, category, priority) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -352,7 +174,7 @@ def create_work_order(): def get_work_order_status(work_order_id): """获取工单状态""" try: - result = get_chat_manager().get_work_order_status(work_order_id) + result = service_manager.get_chat_manager().get_work_order_status(work_order_id) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -361,7 +183,7 @@ def get_work_order_status(work_order_id): def end_chat_session(session_id): """结束对话会话""" try: - success = get_chat_manager().end_session(session_id) + success = service_manager.get_chat_manager().end_session(session_id) return jsonify({ "success": success, "message": "会话已结束" if success else "结束会话失败" @@ -374,7 +196,7 @@ def get_active_sessions(): """获取活跃会话列表""" try: # 确保chat_manager已初始化 - manager = get_chat_manager() + manager = service_manager.get_chat_manager() sessions = manager.get_active_sessions() return jsonify({ "success": True, @@ -384,12 +206,14 @@ def get_active_sessions(): logger.error(f"获取活跃会话失败: {e}") return jsonify({"error": str(e)}), 500 +# ============================================================================ # Agent相关API +# ============================================================================ @app.route('/api/agent/status') def get_agent_status(): """获取Agent状态""" try: - status = get_agent_assistant().get_agent_status() + status = service_manager.get_agent_assistant().get_agent_status() return jsonify(status) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -399,7 +223,7 @@ def get_agent_action_history(): """获取Agent动作执行历史""" try: limit = request.args.get('limit', 50, type=int) - history = get_agent_assistant().get_action_history(limit) + history = service_manager.get_agent_assistant().get_action_history(limit) return jsonify({ "success": True, "history": history, @@ -413,7 +237,7 @@ def trigger_sample_action(): """触发示例动作""" try: import asyncio - result = asyncio.run(get_agent_assistant().trigger_sample_actions()) + result = asyncio.run(service_manager.get_agent_assistant().trigger_sample_actions()) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -422,7 +246,7 @@ def trigger_sample_action(): def clear_agent_history(): """清空Agent执行历史""" try: - result = get_agent_assistant().clear_execution_history() + result = service_manager.get_agent_assistant().clear_execution_history() return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -431,7 +255,7 @@ def clear_agent_history(): def get_llm_stats(): """获取LLM使用统计""" try: - stats = get_agent_assistant().get_llm_usage_stats() + stats = service_manager.get_agent_assistant().get_llm_usage_stats() return jsonify({ "success": True, "stats": stats @@ -445,7 +269,7 @@ def toggle_agent_mode(): try: data = request.get_json() enabled = data.get('enabled', True) - success = get_agent_assistant().toggle_agent_mode(enabled) + success = service_manager.get_agent_assistant().toggle_agent_mode(enabled) return jsonify({ "success": success, "message": f"Agent模式已{'启用' if enabled else '禁用'}" @@ -457,7 +281,7 @@ def toggle_agent_mode(): def start_agent_monitoring(): """启动Agent监控""" try: - success = get_agent_assistant().start_proactive_monitoring() + success = service_manager.get_agent_assistant().start_proactive_monitoring() return jsonify({ "success": success, "message": "Agent监控已启动" if success else "启动失败" @@ -469,7 +293,7 @@ def start_agent_monitoring(): def stop_agent_monitoring(): """停止Agent监控""" try: - success = get_agent_assistant().stop_proactive_monitoring() + success = service_manager.get_agent_assistant().stop_proactive_monitoring() return jsonify({ "success": success, "message": "Agent监控已停止" if success else "停止失败" @@ -481,7 +305,7 @@ def stop_agent_monitoring(): def proactive_monitoring(): """主动监控检查""" try: - result = get_agent_assistant().run_proactive_monitoring() + result = service_manager.get_agent_assistant().run_proactive_monitoring() return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -490,7 +314,7 @@ def proactive_monitoring(): def intelligent_analysis(): """智能分析""" try: - analysis = get_agent_assistant().run_intelligent_analysis() + analysis = service_manager.get_agent_assistant().run_intelligent_analysis() return jsonify({"success": True, "analysis": analysis}) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -507,7 +331,7 @@ def agent_chat(): return jsonify({"error": "消息不能为空"}), 400 # 使用Agent助手处理消息 - agent_assistant = get_agent_assistant() + agent_assistant = service_manager.get_agent_assistant() # 模拟Agent处理(实际应该调用真正的Agent处理逻辑) import asyncio @@ -527,12 +351,15 @@ def agent_chat(): except Exception as e: return jsonify({"error": str(e)}), 500 +# ============================================================================ # Agent 工具统计与自定义工具 +# ============================================================================ @app.route('/api/agent/tools/stats') def get_agent_tools_stats(): try: - tools = get_agent_assistant().agent_core.tool_manager.get_available_tools() - performance = get_agent_assistant().agent_core.tool_manager.get_tool_performance_report() + agent_assistant = service_manager.get_agent_assistant() + tools = agent_assistant.agent_core.tool_manager.get_available_tools() + performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() return jsonify({ "success": True, "tools": tools, @@ -552,7 +379,7 @@ def execute_agent_tool(): return jsonify({"error": "缺少工具名称tool"}), 400 import asyncio - result = asyncio.run(get_agent_assistant().agent_core.tool_manager.execute_tool(tool_name, parameters)) + result = asyncio.run(service_manager.get_agent_assistant().agent_core.tool_manager.execute_tool(tool_name, parameters)) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -570,7 +397,7 @@ def register_custom_tool(): def _placeholder_tool(**kwargs): return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs} - get_agent_assistant().agent_core.tool_manager.register_tool( + service_manager.get_agent_assistant().agent_core.tool_manager.register_tool( name, _placeholder_tool, metadata={"description": description, "custom": True} @@ -582,55 +409,21 @@ def register_custom_tool(): @app.route('/api/agent/tools/unregister/', methods=['DELETE']) def unregister_custom_tool(name): try: - success = get_agent_assistant().agent_core.tool_manager.unregister_tool(name) + success = service_manager.get_agent_assistant().agent_core.tool_manager.unregister_tool(name) return jsonify({"success": success}) except Exception as e: return jsonify({"error": str(e)}), 500 -# 分析相关API -@app.route('/api/analytics') -def get_analytics(): - """获取分析数据""" - try: - # 支持多种参数 - time_range = request.args.get('timeRange', request.args.get('days', '30')) - dimension = request.args.get('dimension', 'workorders') - - # 参数验证 - try: - days = int(time_range) - if days <= 0 or days > 365: - days = 30 - except (ValueError, TypeError): - days = 30 - - analytics = generate_db_analytics(days, dimension) - - # 确保返回的数据结构完整 - if not analytics: - analytics = { - "workorders": {"total": 0, "open": 0, "resolved": 0, "trend": []}, - "alerts": {"total": 0, "critical": 0, "warning": 0, "trend": []}, - "conversations": {"total": 0, "avg_confidence": 0, "trend": []}, - "performance": {"avg_response_time": 0, "success_rate": 0} - } - - return jsonify(analytics) - except Exception as e: - logger.error(f"获取分析数据失败: {e}") - return jsonify({"error": str(e)}), 500 - -def generate_db_analytics(days: int, dimension: str) -> dict: - """基于数据库生成真实分析数据(优化版)""" - # 使用优化后的查询 - return query_optimizer.get_analytics_optimized(days) +# ============================================================================ +# 分析相关API - 已迁移到 core 蓝图 +# ============================================================================ @app.route('/api/analytics/export') def export_analytics(): """导出分析报告""" try: # 生成Excel报告(使用数据库真实数据) - analytics = generate_db_analytics(30, 'workorders') + analytics = query_optimizer.get_analytics_optimized(30) # 创建工作簿 from openpyxl import Workbook @@ -664,7 +457,9 @@ def export_analytics(): except Exception as e: return jsonify({"error": str(e)}), 500 +# ============================================================================ # 车辆数据相关API +# ============================================================================ @app.route('/api/vehicle/data') def get_vehicle_data(): """获取车辆数据""" @@ -674,12 +469,13 @@ def get_vehicle_data(): data_type = request.args.get('data_type') limit = request.args.get('limit', 10, type=int) + vehicle_mgr = service_manager.get_vehicle_manager() if vehicle_vin: - data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) + data = vehicle_mgr.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) elif vehicle_id: - data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit) + data = vehicle_mgr.get_vehicle_data(vehicle_id, data_type, limit) else: - data = vehicle_manager.search_vehicle_data(limit=limit) + data = vehicle_mgr.search_vehicle_data(limit=limit) return jsonify(data) except Exception as e: @@ -689,7 +485,7 @@ def get_vehicle_data(): def get_latest_vehicle_data_by_vin(vehicle_vin): """按VIN获取车辆最新数据""" try: - data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin) + data = service_manager.get_vehicle_manager().get_latest_vehicle_data_by_vin(vehicle_vin) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -698,7 +494,7 @@ def get_latest_vehicle_data_by_vin(vehicle_vin): def get_latest_vehicle_data(vehicle_id): """获取车辆最新数据""" try: - data = vehicle_manager.get_latest_vehicle_data(vehicle_id) + data = service_manager.get_vehicle_manager().get_latest_vehicle_data(vehicle_id) return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -707,7 +503,7 @@ def get_latest_vehicle_data(vehicle_id): def get_vehicle_summary(vehicle_id): """获取车辆数据摘要""" try: - summary = vehicle_manager.get_vehicle_summary(vehicle_id) + summary = service_manager.get_vehicle_manager().get_vehicle_summary(vehicle_id) return jsonify(summary) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -717,7 +513,7 @@ def add_vehicle_data(): """添加车辆数据""" try: data = request.get_json() - success = vehicle_manager.add_vehicle_data( + success = service_manager.get_vehicle_manager().add_vehicle_data( vehicle_id=data['vehicle_id'], data_type=data['data_type'], data_value=data['data_value'], @@ -731,12 +527,14 @@ def add_vehicle_data(): def init_sample_vehicle_data(): """初始化示例车辆数据""" try: - success = vehicle_manager.add_sample_vehicle_data() + success = service_manager.get_vehicle_manager().add_sample_vehicle_data() return jsonify({"success": success, "message": "示例数据初始化成功" if success else "初始化失败"}) except Exception as e: return jsonify({"error": str(e)}), 500 +# ============================================================================ # API测试相关接口 +# ============================================================================ @app.route('/api/test/connection', methods=['POST']) def test_api_connection(): """测试API连接""" @@ -778,6 +576,9 @@ def test_model_response(): except Exception as e: return jsonify({"success": False, "error": str(e)}), 500 +# ============================================================================ +# 应用启动配置 +# ============================================================================ # 飞书同步功能已合并到主页面,不再需要单独的路由 if __name__ == '__main__': diff --git a/src/web/app_backup.py b/src/web/app_backup.py deleted file mode 100644 index 51fa408..0000000 --- a/src/web/app_backup.py +++ /dev/null @@ -1,1955 +0,0 @@ - -# -*- coding: utf-8 -*- -""" -TSP助手预警管理Web应用 -提供预警系统的Web界面和API接口 -""" - -import sys -import os -import json -import logging -import pandas as pd - -# 配置日志 -logger = logging.getLogger(__name__) -from datetime import datetime, timedelta -from openpyxl import Workbook -from openpyxl.styles import Font -from flask import Flask, render_template, request, jsonify, redirect, url_for, send_from_directory, send_file -from flask_cors import CORS -from werkzeug.utils import secure_filename - -# 添加项目根目录到Python路径 -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from src.main import TSPAssistant -from src.agent_assistant import TSPAgentAssistant -from src.analytics.alert_system import AlertRule, AlertLevel, AlertType -from src.dialogue.realtime_chat import RealtimeChatManager -from src.vehicle.vehicle_data_manager import VehicleDataManager -from src.core.database import db_manager -from src.core.models import WorkOrder, Alert, Conversation, KnowledgeEntry, WorkOrderSuggestion, VehicleData -from src.core.backup_manager import backup_manager -from src.core.query_optimizer import query_optimizer -from sqlalchemy import func, text - -app = Flask(__name__) -CORS(app) - -# 抑制 /api/health 的访问日志 -werkzeug_logger = logging.getLogger('werkzeug') - -class HealthLogFilter(logging.Filter): - def filter(self, record): - try: - msg = record.getMessage() - return '/api/health' not in msg - except Exception: - return True - -werkzeug_logger.addFilter(HealthLogFilter()) - -# 配置上传文件夹 -UPLOAD_FOLDER = 'uploads' -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER -app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size - -# 延迟初始化TSP助手和Agent助手(避免启动时重复初始化) -assistant = None -agent_assistant = None -chat_manager = None -vehicle_manager = None - -def get_assistant(): - """获取TSP助手实例(懒加载)""" - global assistant - if assistant is None: - assistant = TSPAssistant() - return assistant - -def get_agent_assistant(): - """获取Agent助手实例(懒加载)""" - global agent_assistant - if agent_assistant is None: - agent_assistant = TSPAgentAssistant() - return agent_assistant - -def get_chat_manager(): - """获取聊天管理器实例(懒加载)""" - global chat_manager - if chat_manager is None: - chat_manager = RealtimeChatManager() - return chat_manager - -def get_vehicle_manager(): - """获取车辆数据管理器实例(懒加载)""" - global vehicle_manager - if vehicle_manager is None: - vehicle_manager = VehicleDataManager() - return vehicle_manager - -# 工具函数: -def _ensure_workorder_template_file() -> str: - """返回已有的模板xlsx路径;不做动态生成,避免运行时依赖问�?"" - template_path = os.path.join(app.config['UPLOAD_FOLDER'], 'workorder_template.xlsx') - # 确保目录存在 - os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) - if not os.path.exists(template_path): - # 如果运行目录不存在模板,尝试从项目根相对路径拷贝一�? repo_template = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '..', 'uploads', 'workorder_template.xlsx') - repo_template = os.path.abspath(repo_template) - try: - if os.path.exists(repo_template): - import shutil - shutil.copyfile(repo_template, template_path) - else: - raise FileNotFoundError('模板文件缺失:uploads/workorder_template.xlsx') - except Exception as copy_err: - raise copy_err - return template_path - -@app.route('/') -def index(): - """主页 - 综合管理平台""" - return render_template('dashboard.html') - -@app.route('/alerts') -def alerts(): - """预警管理页面""" - return render_template('index.html') - -@app.route('/api/health') -def get_health(): - """获取系统健康状态(附加�?小时业务指标�?"" - try: - base = get_assistant().get_system_health() or {} - # 追加数据库近1小时指标 - from datetime import datetime, timedelta - with db_manager.get_session() as session: - since = datetime.now() - timedelta(hours=1) - conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() - resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] - avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 - open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() - levels = session.query(Alert.level).filter(Alert.is_active == True).all() - level_map = {} - for (lvl,) in levels: - level_map[lvl] = level_map.get(lvl, 0) + 1 - base.update({ - "throughput_1h": conv_count, - "avg_response_time_1h": avg_resp, - "open_workorders": open_wos, - "active_alerts_by_level": level_map - }) - return jsonify(base) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/alerts') -def get_alerts(): - """获取预警列表""" - try: - alerts = get_assistant().get_active_alerts() - return jsonify(alerts) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/alerts', methods=['POST']) -def create_alert(): - """创建预警""" - try: - data = request.get_json() - alert = get_assistant().create_alert( - alert_type=data.get('alert_type', 'manual'), - title=data.get('title', '手动预警'), - description=data.get('description', ''), - level=data.get('level', 'medium') - ) - return jsonify({"success": True, "alert": alert}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/alerts/statistics') -def get_alert_statistics(): - """获取预警统计""" - try: - stats = get_assistant().get_alert_statistics() - return jsonify(stats) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/alerts//resolve', methods=['POST']) -def resolve_alert(alert_id): - """解决预警""" - try: - success = get_assistant().resolve_alert(alert_id) - if success: - return jsonify({"success": True, "message": "预警已解�?"}) - else: - return jsonify({"success": False, "message": "解决预警失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules') -def get_rules(): - """获取预警规则列表""" - try: - rules = get_assistant().alert_system.rules - rules_data = [] - for name, rule in rules.items(): - rules_data.append({ - "name": rule.name, - "description": rule.description, - "alert_type": rule.alert_type.value, - "level": rule.level.value, - "threshold": rule.threshold, - "condition": rule.condition, - "enabled": rule.enabled, - "check_interval": rule.check_interval, - "cooldown": rule.cooldown - }) - return jsonify(rules_data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules', methods=['POST']) -def create_rule(): - """创建预警规则""" - try: - data = request.get_json() - rule = AlertRule( - name=data['name'], - description=data['description'], - alert_type=AlertType(data['alert_type']), - level=AlertLevel(data['level']), - threshold=float(data['threshold']), - condition=data['condition'], - enabled=data.get('enabled', True), - check_interval=int(data.get('check_interval', 300)), - cooldown=int(data.get('cooldown', 3600)) - ) - - success = get_assistant().alert_system.add_custom_rule(rule) - if success: - return jsonify({"success": True, "message": "规则创建成功"}) - else: - return jsonify({"success": False, "message": "规则创建失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['PUT']) -def update_rule(rule_name): - """更新预警规则""" - try: - data = request.get_json() - success = get_assistant().alert_system.update_rule(rule_name, **data) - if success: - return jsonify({"success": True, "message": "规则更新成功"}) - else: - return jsonify({"success": False, "message": "规则更新失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['DELETE']) -def delete_rule(rule_name): - """删除预警规则""" - try: - success = get_assistant().alert_system.delete_rule(rule_name) - if success: - return jsonify({"success": True, "message": "规则删除成功"}) - else: - return jsonify({"success": False, "message": "规则删除失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/start', methods=['POST']) -def start_monitoring(): - """启动监控服务""" - try: - success = get_assistant().start_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已启�?"}) - else: - return jsonify({"success": False, "message": "启动监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/stop', methods=['POST']) -def stop_monitoring(): - """停止监控服务""" - try: - success = get_assistant().stop_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已停�?}) - else: - return jsonify({"success": False, "message": "停止监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/status') -def get_monitor_status(): - """获取监控服务状�?"" - try: - health = get_assistant().get_system_health() - return jsonify({ - "monitor_status": health.get("monitor_status", "unknown"), - "health_score": health.get("health_score", 0), - "active_alerts": health.get("active_alerts", 0) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/check-alerts', methods=['POST']) -def check_alerts(): - """手动检查预�?"" - try: - alerts = get_assistant().check_alerts() - return jsonify({ - "success": True, - "alerts": alerts, - "count": len(alerts) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 实时对话相关路由 -@app.route('/chat') -def chat(): - """实时对话页面 (WebSocket版本)""" - return render_template('chat.html') - -@app.route('/chat-http') -def chat_http(): - """实时对话页面 (HTTP版本)""" - return render_template('chat_http.html') - -@app.route('/api/chat/session', methods=['POST']) -def create_chat_session(): - """创建对话会话""" - try: - data = request.get_json() - user_id = data.get('user_id', 'anonymous') - work_order_id = data.get('work_order_id') - - session_id = get_chat_manager().create_session(user_id, work_order_id) - - return jsonify({ - "success": True, - "session_id": session_id, - "message": "会话创建成功" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/message', methods=['POST']) -def send_chat_message(): - """发送聊天消�?"" - try: - data = request.get_json() - session_id = data.get('session_id') - message = data.get('message') - - if not session_id or not message: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().process_message(session_id, message) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/history/') -def get_chat_history(session_id): - """获取对话历史""" - try: - history = get_chat_manager().get_session_history(session_id) - return jsonify({ - "success": True, - "history": history - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order', methods=['POST']) -def create_work_order(): - """创建工单""" - try: - data = request.get_json() - session_id = data.get('session_id') - title = data.get('title') - description = data.get('description') - category = data.get('category', '技术问�?) - priority = data.get('priority', 'medium') - - if not session_id or not title or not description: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().create_work_order(session_id, title, description, category, priority) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order/') -def get_work_order_status(work_order_id): - """获取工单状�?"" - try: - result = get_chat_manager().get_work_order_status(work_order_id) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/session/', methods=['DELETE']) -def end_chat_session(session_id): - """结束对话会话""" - try: - success = get_chat_manager().end_session(session_id) - return jsonify({ - "success": success, - "message": "会话已结�? if success else "结束会话失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/sessions') -def get_active_sessions(): - """获取活跃会话列表""" - try: - sessions = chat_manager.get_active_sessions() - return jsonify({ - "success": True, - "sessions": sessions - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent相关API -@app.route('/api/agent/status') -def get_agent_status(): - """获取Agent状�?"" - try: - status = agent_assistant.get_agent_status() - return jsonify(status) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/action-history') -def get_agent_action_history(): - """获取Agent动作执行历史""" - try: - limit = request.args.get('limit', 50, type=int) - history = agent_assistant.get_action_history(limit) - return jsonify({ - "success": True, - "history": history, - "count": len(history) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/trigger-sample', methods=['POST']) -def trigger_sample_action(): - """触发示例动作""" - try: - import asyncio - result = asyncio.run(agent_assistant.trigger_sample_actions()) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/clear-history', methods=['POST']) -def clear_agent_history(): - """清空Agent执行历史""" - try: - result = agent_assistant.clear_execution_history() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/llm-stats') -def get_llm_stats(): - """获取LLM使用统计""" - try: - stats = agent_assistant.get_llm_usage_stats() - return jsonify({ - "success": True, - "stats": stats - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/toggle', methods=['POST']) -def toggle_agent_mode(): - """切换Agent模式""" - try: - data = request.get_json() - enabled = data.get('enabled', True) - success = agent_assistant.toggle_agent_mode(enabled) - return jsonify({ - "success": success, - "message": f"Agent模式已{'启用' if enabled else '禁用'}" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/start', methods=['POST']) -def start_agent_monitoring(): - """启动Agent监控""" - try: - success = agent_assistant.start_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已启�? if success else "启动失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/stop', methods=['POST']) -def stop_agent_monitoring(): - """停止Agent监控""" - try: - success = agent_assistant.stop_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已停�? if success else "停止失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/proactive-monitoring', methods=['POST']) -def proactive_monitoring(): - """主动监控检�?"" - try: - result = agent_assistant.run_proactive_monitoring() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/intelligent-analysis', methods=['POST']) -def intelligent_analysis(): - """智能分析""" - try: - analysis = get_agent_assistant().run_intelligent_analysis() - return jsonify({"success": True, "analysis": analysis}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/chat', methods=['POST']) -def agent_chat(): - """Agent对话接口""" - try: - data = request.get_json() - message = data.get('message', '') - context = data.get('context', {}) - - if not message: - return jsonify({"error": "消息不能为空"}), 400 - - # 使用Agent助手处理消息 - agent_assistant = get_agent_assistant() - - # 模拟Agent处理(实际应该调用真正的Agent处理逻辑�? import asyncio - result = asyncio.run(agent_assistant.process_message_agent( - message=message, - user_id=context.get('user_id', 'admin'), - work_order_id=None, - enable_proactive=True - )) - - return jsonify({ - "success": True, - "response": result.get('response', 'Agent已处理您的请�?), - "actions": result.get('actions', []), - "status": result.get('status', 'completed') - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 知识库相关API -@app.route('/api/knowledge') -def get_knowledge(): - """获取知识库列�?"" - try: - # 获取分页参数 - page = request.args.get('page', 1, type=int) - per_page = request.args.get('per_page', 10, type=int) - - # 从数据库获取知识库数�? knowledge_entries = assistant.knowledge_manager.get_knowledge_entries( - page=page, per_page=per_page - ) - - return jsonify(knowledge_entries) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/search') -def search_knowledge(): - """搜索知识�?"" - try: - query = request.args.get('q', '') - # 这里应该调用知识库管理器的搜索方�? results = assistant.search_knowledge(query, top_k=5) - return jsonify(results.get('results', [])) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge', methods=['POST']) -def add_knowledge(): - """添加知识库条�?"" - try: - data = request.get_json() - success = assistant.knowledge_manager.add_knowledge_entry( - question=data['question'], - answer=data['answer'], - category=data['category'], - confidence_score=data['confidence_score'] - ) - return jsonify({"success": success, "message": "知识添加成功" if success else "添加失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/stats') -def get_knowledge_stats(): - """获取知识库统�?"" - try: - stats = assistant.knowledge_manager.get_knowledge_stats() - return jsonify(stats) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/upload', methods=['POST']) -def upload_knowledge_file(): - """上传文件并生成知识库""" - try: - if 'file' not in request.files: - return jsonify({"error": "没有上传文件"}), 400 - - file = request.files['file'] - if file.filename == '': - return jsonify({"error": "没有选择文件"}), 400 - - # 保存文件到临时目�? import tempfile - import os - import uuid - - # 创建唯一的临时文件名 - temp_filename = f"upload_{uuid.uuid4()}{os.path.splitext(file.filename)[1]}" - temp_path = os.path.join(tempfile.gettempdir(), temp_filename) - - try: - # 保存文件 - file.save(temp_path) - - # 使用Agent助手处理文件 - result = agent_assistant.process_file_to_knowledge(temp_path, file.filename) - - return jsonify(result) - - finally: - # 确保删除临时文件 - try: - if os.path.exists(temp_path): - os.unlink(temp_path) - except Exception as cleanup_error: - logger.warning(f"清理临时文件失败: {cleanup_error}") - - except Exception as e: - logger.error(f"文件上传处理失败: {e}") - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/delete/', methods=['DELETE']) -def delete_knowledge(knowledge_id): - """删除知识库条�?"" - try: - success = assistant.knowledge_manager.delete_knowledge_entry(knowledge_id) - return jsonify({"success": success, "message": "删除成功" if success else "删除失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/verify/', methods=['POST']) -def verify_knowledge(knowledge_id): - """验证知识库条�?"" - try: - data = request.get_json() or {} - verified_by = data.get('verified_by', 'admin') - success = assistant.knowledge_manager.verify_knowledge_entry(knowledge_id, verified_by) - return jsonify({"success": success, "message": "验证成功" if success else "验证失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/knowledge/unverify/', methods=['POST']) -def unverify_knowledge(knowledge_id): - """取消验证知识库条�?"" - try: - success = 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 - -# 工单相关API -@app.route('/api/workorders') -def get_workorders(): - """获取工单列表(优化版�?"" - try: - status_filter = request.args.get('status', '') - priority_filter = request.args.get('priority', '') - - # 使用优化后的查询 - result = query_optimizer.get_workorders_optimized( - status_filter=status_filter, priority_filter=priority_filter - ) - - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders', methods=['POST']) -def create_workorder(): - """创建工单""" - try: - data = request.get_json() - result = get_assistant().create_work_order( - title=data['title'], - description=data['description'], - category=data['category'], - priority=data['priority'] - ) - return jsonify({"success": True, "workorder": result}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders/') -def get_workorder_details(workorder_id): - """获取工单详情(含数据库对话记录)""" - try: - with db_manager.get_session() as session: - w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not w: - return jsonify({"error": "工单不存�?}), 404 - convs = session.query(Conversation).filter(Conversation.work_order_id == w.id).order_by(Conversation.timestamp.asc()).all() - conv_list = [] - for c in convs: - conv_list.append({ - "id": c.id, - "user_message": c.user_message, - "assistant_response": c.assistant_response, - "timestamp": c.timestamp.isoformat() if c.timestamp else None - }) - # 在会话内构建工单数据 - workorder = { - "id": w.id, - "order_id": w.order_id, - "title": w.title, - "description": w.description, - "category": w.category, - "priority": w.priority, - "status": w.status, - "created_at": w.created_at.isoformat() if w.created_at else None, - "updated_at": w.updated_at.isoformat() if w.updated_at else None, - "resolution": w.resolution, - "satisfaction_score": w.satisfaction_score, - "conversations": conv_list - } - return jsonify(workorder) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders/', methods=['PUT']) -def update_workorder(workorder_id): - """更新工单(写入数据库�?"" - try: - data = request.get_json() - if not data.get('title') or not data.get('description'): - return jsonify({"error": "标题和描述不能为�?}), 400 - with db_manager.get_session() as session: - w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not w: - return jsonify({"error": "工单不存�?}), 404 - w.title = data.get('title', w.title) - w.description = data.get('description', w.description) - w.category = data.get('category', w.category) - w.priority = data.get('priority', w.priority) - w.status = data.get('status', w.status) - w.resolution = data.get('resolution', w.resolution) - w.satisfaction_score = data.get('satisfaction_score', w.satisfaction_score) - w.updated_at = datetime.now() - session.commit() - updated = { - "id": w.id, - "title": w.title, - "description": w.description, - "category": w.category, - "priority": w.priority, - "status": w.status, - "resolution": w.resolution, - "satisfaction_score": w.satisfaction_score, - "updated_at": w.updated_at.isoformat() if w.updated_at else None - } - return jsonify({"success": True, "message": "工单更新成功", "workorder": updated}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders/', methods=['DELETE']) -def delete_workorder(workorder_id): - """删除工单""" - try: - with db_manager.get_session() as session: - workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not workorder: - return jsonify({"error": "工单不存�?}), 404 - - # 先删除所有相关的子记录(按外键依赖顺序) - # 1. 删除工单建议记录 - try: - session.execute(text("DELETE FROM work_order_suggestions WHERE work_order_id = :id"), {"id": workorder_id}) - except Exception as e: - print(f"删除工单建议记录失败: {e}") - - # 2. 删除对话记录 - session.query(Conversation).filter(Conversation.work_order_id == workorder_id).delete() - - # 3. 删除工单 - session.delete(workorder) - session.commit() - - return jsonify({ - "success": True, - "message": "工单删除成功" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 工单AI建议:生成、保存人工描述、审批入�?@app.route('/api/workorders//ai-suggestion', methods=['POST']) -def generate_workorder_ai_suggestion(workorder_id): - """根据工单描述与知识库生成AI建议草稿""" - try: - with db_manager.get_session() as session: - w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not w: - return jsonify({"error": "工单不存�?}), 404 - # 调用知识库搜索与LLM生成 - query = f"{w.title} {w.description}" - kb_results = assistant.search_knowledge(query, top_k=3) - kb_list = kb_results.get('results', []) if isinstance(kb_results, dict) else [] - # 组装提示�? context = "\n".join([f"Q: {k.get('question','')}\nA: {k.get('answer','')}" for k in kb_list]) - from src.core.llm_client import QwenClient - llm = QwenClient() - prompt = f"请基于以下工单描述与知识库片段,给出简洁、可执行的处理建议。\n工单描述:\n{w.description}\n\n知识库片�?\n{context}\n\n请直接输出建议文本:" - llm_resp = llm.chat_completion(messages=[{"role":"user","content":prompt}], temperature=0.3, max_tokens=800) - suggestion = "" - if llm_resp and 'choices' in llm_resp: - suggestion = llm_resp['choices'][0]['message']['content'] - # 保存/更新草稿记录 - rec = session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id == w.id).first() - if not rec: - rec = WorkOrderSuggestion(work_order_id=w.id, ai_suggestion=suggestion) - session.add(rec) - else: - rec.ai_suggestion = suggestion - rec.updated_at = datetime.now() - session.commit() - return jsonify({"success": True, "ai_suggestion": suggestion}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders//human-resolution', methods=['POST']) -def save_workorder_human_resolution(workorder_id): - """保存人工描述,并计算与AI建议相似度;若≥95%可自动审批入�?"" - try: - data = request.get_json() or {} - human_text = data.get('human_resolution','').strip() - if not human_text: - return jsonify({"error":"人工描述不能为空"}), 400 - with db_manager.get_session() as session: - w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not w: - return jsonify({"error": "工单不存�?}), 404 - rec = session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id == w.id).first() - if not rec: - rec = WorkOrderSuggestion(work_order_id=w.id) - session.add(rec) - rec.human_resolution = human_text - # 计算相似度(使用简单cosine TF-IDF,避免外部服务依赖) - try: - from sklearn.feature_extraction.text import TfidfVectorizer - from sklearn.metrics.pairwise import cosine_similarity - texts = [rec.ai_suggestion or "", human_text] - vec = TfidfVectorizer(max_features=1000) - mat = vec.fit_transform(texts) - sim = float(cosine_similarity(mat[0:1], mat[1:2])[0][0]) - except Exception: - sim = 0.0 - rec.ai_similarity = sim - # 自动审批条件�?.95 - approved = sim >= 0.95 - rec.approved = approved - session.commit() - return jsonify({"success": True, "similarity": sim, "approved": approved}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders//approve-to-knowledge', methods=['POST']) -def approve_workorder_to_knowledge(workorder_id): - """将已审批的AI建议入库为知识条�?"" - try: - with db_manager.get_session() as session: - w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() - if not w: - return jsonify({"error": "工单不存�?}), 404 - rec = session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id == w.id).first() - if not rec or not rec.approved or not rec.ai_suggestion: - return jsonify({"error": "未找到可入库的已审批AI建议"}), 400 - # 入库为知识条目(�?工单标题;答=AI建议;类目用工单分类�? entry = KnowledgeEntry( - question=w.title or (w.description[:20] if w.description else '工单问题'), - answer=rec.ai_suggestion, - category=w.category or '其他', - confidence_score=0.95, - is_active=True, - is_verified=True, - verified_by='auto_approve', - verified_at=datetime.now() - ) - session.add(entry) - session.commit() - return jsonify({"success": True, "knowledge_id": entry.id}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 分析相关API -@app.route('/api/analytics') -def get_analytics(): - """获取分析数据""" - try: - # 支持多种参数�? time_range = request.args.get('timeRange', request.args.get('days', '30')) - dimension = request.args.get('dimension', 'workorders') - analytics = generate_db_analytics(int(time_range), dimension) - return jsonify(analytics) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -def generate_db_analytics(days: int, dimension: str) -> dict: - """基于数据库生成真实分析数据(优化版)""" - # 使用优化后的查询 - return query_optimizer.get_analytics_optimized(days) - -@app.route('/api/analytics/export') -def export_analytics(): - """导出分析报告""" - try: - # 生成Excel报告(使用数据库真实数据�? analytics = generate_db_analytics(30, 'workorders') - - # 创建工作�? wb = Workbook() - ws = wb.active - ws.title = "分析报告" - - # 添加标题 - ws['A1'] = 'TSP智能助手分析报告' - ws['A1'].font = Font(size=16, bold=True) - - # 添加工单统计 - ws['A3'] = '工单统计' - ws['A3'].font = Font(bold=True) - ws['A4'] = '总工单数' - ws['B4'] = analytics['workorders']['total'] - ws['A5'] = '待处�? - ws['B5'] = analytics['workorders']['open'] - ws['A6'] = '已解�? - ws['B6'] = analytics['workorders']['resolved'] - - # 保存文件 - report_path = 'uploads/analytics_report.xlsx' - os.makedirs('uploads', exist_ok=True) - wb.save(report_path) - - return send_file(report_path, as_attachment=True, download_name='analytics_report.xlsx') - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent 工具统计与自定义工具 -@app.route('/api/agent/tools/stats') -def get_agent_tools_stats(): - try: - tools = agent_assistant.agent_core.tool_manager.get_available_tools() - performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() - return jsonify({ - "success": True, - "tools": tools, - "performance": performance - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/register', methods=['POST']) -def register_custom_tool(): - """注册自定义工具(仅登记元数据,函数为占位�?"" - try: - data = request.get_json() or {} - name = data.get('name') - description = data.get('description', '') - if not name: - return jsonify({"error": "缺少工具名称"}), 400 - - def _placeholder_tool(**kwargs): - return {"message": f"自定义工�?{name} 已登记(占位),当前不可执行", "params": kwargs} - - agent_assistant.agent_core.tool_manager.register_tool( - name, - _placeholder_tool, - metadata={"description": description, "custom": True} - ) - return jsonify({"success": True, "message": "工具已注�?}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/unregister/', methods=['DELETE']) -def unregister_custom_tool(name): - try: - success = agent_assistant.agent_core.tool_manager.unregister_tool(name) - return jsonify({"success": success}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 工单导入相关API -@app.route('/api/workorders/import', methods=['POST']) -def import_workorders(): - """导入Excel工单文件""" - try: - # 检查是否有文件上传 - if 'file' not in request.files: - return jsonify({"error": "没有上传文件"}), 400 - - file = request.files['file'] - if file.filename == '': - return jsonify({"error": "没有选择文件"}), 400 - - if not file.filename.endswith(('.xlsx', '.xls')): - return jsonify({"error": "只支持Excel文件(.xlsx, .xls)"}), 400 - - # 保存上传的文�? filename = secure_filename(file.filename) - upload_path = os.path.join('uploads', filename) - os.makedirs('uploads', exist_ok=True) - file.save(upload_path) - - # 解析Excel文件 - try: - df = pd.read_excel(upload_path) - imported_workorders = [] - - # 处理每一行数�? for index, row in df.iterrows(): - # 根据Excel列名映射到工单字�? title = str(row.get('标题', row.get('title', f'导入工单 {index + 1}'))) - description = str(row.get('描述', row.get('description', ''))) - category = str(row.get('分类', row.get('category', '技术问�?))) - priority = str(row.get('优先�?, row.get('priority', 'medium'))) - status = str(row.get('状�?, row.get('status', 'open'))) - - # 验证必填字段 - if not title or title.strip() == '': - continue - - # 创建工单到数据库 - with db_manager.get_session() as session: - workorder = WorkOrder( - title=title, - description=description, - category=category, - priority=priority, - status=status, - created_at=datetime.now(), - updated_at=datetime.now() - ) - - # 处理可选字�? if pd.notna(row.get('解决方案', row.get('resolution'))): - workorder.resolution = str(row.get('解决方案', row.get('resolution'))) - - if pd.notna(row.get('满意�?, row.get('satisfaction_score'))): - try: - workorder.satisfaction_score = int(row.get('满意�?, row.get('satisfaction_score'))) - except (ValueError, TypeError): - workorder.satisfaction_score = None - - session.add(workorder) - session.commit() - - # 添加到返回列�? imported_workorders.append({ - "id": workorder.id, - "order_id": workorder.order_id, - "title": workorder.title, - "description": workorder.description, - "category": workorder.category, - "priority": workorder.priority, - "status": workorder.status, - "created_at": workorder.created_at.isoformat() if workorder.created_at else None, - "updated_at": workorder.updated_at.isoformat() if workorder.updated_at else None, - "resolution": workorder.resolution, - "satisfaction_score": workorder.satisfaction_score - }) - - # 清理上传的文�? os.remove(upload_path) - - return jsonify({ - "success": True, - "message": f"成功导入 {len(imported_workorders)} 个工�?, - "imported_count": len(imported_workorders), - "workorders": imported_workorders - }) - - except Exception as e: - # 清理上传的文�? if os.path.exists(upload_path): - os.remove(upload_path) - return jsonify({"error": f"解析Excel文件失败: {str(e)}"}), 400 - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders/import/template') -def download_import_template(): - """下载工单导入模板""" - try: - template_path = _ensure_workorder_template_file() - return jsonify({ - "success": True, - "template_url": f"/uploads/workorder_template.xlsx" - }) - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/workorders/import/template/file') -def download_import_template_file(): - """直接返回工单导入模板文件(下载)""" - try: - template_path = _ensure_workorder_template_file() - return send_file(template_path, as_attachment=True, download_name='工单导入模板.xlsx') - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/uploads/') -def uploaded_file(filename): - """提供上传文件的下载服�?"" - return send_from_directory(app.config['UPLOAD_FOLDER'], filename) - -# 系统设置相关API -@app.route('/api/settings') -def get_settings(): - """获取系统设置""" - try: - import json - settings_path = os.path.join('data', 'system_settings.json') - os.makedirs('data', exist_ok=True) - if os.path.exists(settings_path): - with open(settings_path, 'r', encoding='utf-8') as f: - settings = json.load(f) - # 掩码API Key - if settings.get('api_key'): - settings['api_key'] = '******' - settings['api_key_masked'] = True - else: - settings = { - "api_timeout": 30, - "max_history": 10, - "refresh_interval": 10, - "auto_monitoring": True, - "agent_mode": True, - # LLM与API配置(仅持久化,不直接热更新LLM客户端) - "api_provider": "openai", - "api_base_url": "", - "api_key": "", - "model_name": "qwen-turbo", - "model_temperature": 0.7, - "model_max_tokens": 1000, - # 服务配置 - "server_port": 5000, - "websocket_port": 8765, - "log_level": "INFO" - } - with open(settings_path, 'w', encoding='utf-8') as f: - json.dump(settings, f, ensure_ascii=False, indent=2) - # 添加当前服务状态信�? import time - import psutil - settings['current_server_port'] = app.config.get('SERVER_PORT', 5000) - settings['current_websocket_port'] = app.config.get('WEBSOCKET_PORT', 8765) - settings['uptime_seconds'] = int(time.time() - app.config.get('START_TIME', time.time())) - settings['memory_usage_percent'] = psutil.virtual_memory().percent - settings['cpu_usage_percent'] = psutil.cpu_percent() - - return jsonify(settings) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/settings', methods=['POST']) -def save_settings(): - """保存系统设置""" - try: - data = request.get_json() - import json - os.makedirs('data', exist_ok=True) - settings_path = os.path.join('data', 'system_settings.json') - # 读取旧值,处理api_key掩码 - old = {} - if os.path.exists(settings_path): - try: - with open(settings_path, 'r', encoding='utf-8') as f: - old = json.load(f) - except Exception: - old = {} - # 如果前端传回掩码或空,则保留旧的api_key - if 'api_key' in data: - if not data['api_key'] or data['api_key'] == '******': - data['api_key'] = old.get('api_key', '') - # 移除mask标志 - if 'api_key_masked' in data: - data.pop('api_key_masked') - with open(settings_path, 'w', encoding='utf-8') as f: - json.dump(data, f, ensure_ascii=False, indent=2) - return jsonify({"success": True, "message": "设置保存成功"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system/info') -def get_system_info(): - """获取系统信息""" - try: - import sys - import platform - info = { - "version": "1.0.0", - "python_version": sys.version, - "database": "SQLite", - "uptime": "2�?小时", - "memory_usage": 128 - } - return jsonify(info) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 车辆数据相关API -@app.route('/api/vehicle/data') -def get_vehicle_data(): - """获取车辆数据""" - try: - vehicle_id = request.args.get('vehicle_id') - vehicle_vin = request.args.get('vehicle_vin') - data_type = request.args.get('data_type') - limit = request.args.get('limit', 10, type=int) - - if vehicle_vin: - data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) - elif vehicle_id: - data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit) - else: - data = vehicle_manager.search_vehicle_data(limit=limit) - - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data/vin//latest') -def get_latest_vehicle_data_by_vin(vehicle_vin): - """按VIN获取车辆最新数�?"" - try: - data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//latest') -def get_latest_vehicle_data(vehicle_id): - """获取车辆最新数�?"" - try: - data = vehicle_manager.get_latest_vehicle_data(vehicle_id) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//summary') -def get_vehicle_summary(vehicle_id): - """获取车辆数据摘要""" - try: - summary = vehicle_manager.get_vehicle_summary(vehicle_id) - return jsonify(summary) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data', methods=['POST']) -def add_vehicle_data(): - """添加车辆数据""" - try: - data = request.get_json() - success = vehicle_manager.add_vehicle_data( - vehicle_id=data['vehicle_id'], - data_type=data['data_type'], - data_value=data['data_value'], - vehicle_vin=data.get('vehicle_vin') - ) - return jsonify({"success": success, "message": "数据添加成功" if success else "添加失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/init-sample-data', methods=['POST']) -def init_sample_vehicle_data(): - """初始化示例车辆数�?"" - try: - success = vehicle_manager.add_sample_vehicle_data() - return jsonify({"success": success, "message": "示例数据初始化成�? if success else "初始化失�?}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# API测试相关接口 -@app.route('/api/test/connection', methods=['POST']) -def test_api_connection(): - """测试API连接""" - try: - data = request.get_json() - api_provider = data.get('api_provider', 'openai') - api_base_url = data.get('api_base_url', '') - api_key = data.get('api_key', '') - model_name = data.get('model_name', 'qwen-turbo') - - # 这里可以调用LLM客户端进行连接测�? # 暂时返回模拟结果 - return jsonify({ - "success": True, - "message": f"API连接测试成功 - {api_provider}", - "response_time": "150ms", - "model_status": "可用" - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route('/api/test/model', methods=['POST']) -def test_model_response(): - """测试模型回答""" - try: - data = request.get_json() - test_message = data.get('test_message', '你好,请简单介绍一下你自己') - - # 这里可以调用LLM客户端进行回答测�? # 暂时返回模拟结果 - return jsonify({ - "success": True, - "test_message": test_message, - "response": "你好!我是TSP智能助手,基于大语言模型构建的智能客服系统。我可以帮助您解决车辆相关问题,提供技术支持和服务�?, - "response_time": "1.2s", - "tokens_used": 45 - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - -# 对话历史相关API -@app.route('/api/conversations') -def get_conversations(): - """获取对话历史列表(分页)- 优化�?"" - try: - page = request.args.get('page', 1, type=int) - per_page = request.args.get('per_page', 10, type=int) - search = request.args.get('search', '') - user_id = request.args.get('user_id', '') - date_filter = request.args.get('date_filter', '') - - # 使用优化后的查询 - result = query_optimizer.get_conversations_paginated( - page=page, per_page=per_page, search=search, - user_id=user_id, date_filter=date_filter - ) - - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/conversations/') -def get_conversation_detail(conversation_id): - """获取对话详情""" - try: - with db_manager.get_session() as session: - conv = session.query(Conversation).filter(Conversation.id == conversation_id).first() - if not conv: - return jsonify({"error": "对话不存�?}), 404 - - return jsonify({ - 'success': True, - 'id': conv.id, - 'user_id': conv.user_id, - 'user_message': conv.user_message, - 'assistant_response': conv.assistant_response, - 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, - 'response_time': conv.response_time, - 'work_order_id': conv.work_order_id - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/conversations/', methods=['DELETE']) -def delete_conversation(conversation_id): - """删除对话记录""" - try: - with db_manager.get_session() as session: - conv = session.query(Conversation).filter(Conversation.id == conversation_id).first() - if not conv: - return jsonify({"error": "对话不存�?}), 404 - - session.delete(conv) - session.commit() - - return jsonify({"success": True, "message": "对话记录已删�?}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/conversations/clear', methods=['DELETE']) -def clear_all_conversations(): - """清空所有对话历�?"" - try: - with db_manager.get_session() as session: - session.query(Conversation).delete() - session.commit() - - return jsonify({"success": True, "message": "对话历史已清�?}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Token监控相关API -@app.route('/api/token-monitor/stats') -def get_token_monitor_stats(): - """获取Token监控统计""" - try: - from datetime import datetime, timedelta - import calendar - - now = datetime.now() - today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) - month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) - - with db_manager.get_session() as session: - # 今日Token消�? today_tokens = session.query(func.sum(Conversation.response_time)).filter( - Conversation.timestamp >= today_start - ).scalar() or 0 - - # 本月Token消�? month_tokens = session.query(func.sum(Conversation.response_time)).filter( - Conversation.timestamp >= month_start - ).scalar() or 0 - - # 模拟成本计算(实际应该根据真实Token使用量计算) - total_cost = month_tokens * 0.0001 # 假设每Token 0.0001�? budget_limit = 1000 # 预算限制 - - return jsonify({ - 'success': True, - 'today_tokens': today_tokens, - 'month_tokens': month_tokens, - 'total_cost': round(total_cost, 2), - 'budget_limit': budget_limit - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/token-monitor/chart') -def get_token_monitor_chart(): - """获取Token使用趋势图表数据""" - try: - period = request.args.get('period', 'day') - from datetime import datetime, timedelta - - now = datetime.now() - labels = [] - tokens = [] - costs = [] - - if period == 'hour': - # 最�?4小时 - for i in range(24): - hour_start = now - timedelta(hours=i+1) - hour_end = now - timedelta(hours=i) - labels.insert(0, hour_start.strftime('%H:00')) - - with db_manager.get_session() as session: - hour_tokens = session.query(func.sum(Conversation.response_time)).filter( - Conversation.timestamp >= hour_start, - Conversation.timestamp < hour_end - ).scalar() or 0 - tokens.insert(0, hour_tokens) - costs.insert(0, hour_tokens * 0.0001) - - elif period == 'day': - # 最�?�? for i in range(7): - day_start = now - timedelta(days=i+1) - day_end = now - timedelta(days=i) - labels.insert(0, day_start.strftime('%m-%d')) - - with db_manager.get_session() as session: - day_tokens = session.query(func.sum(Conversation.response_time)).filter( - Conversation.timestamp >= day_start, - Conversation.timestamp < day_end - ).scalar() or 0 - tokens.insert(0, day_tokens) - costs.insert(0, day_tokens * 0.0001) - - elif period == 'week': - # 最�?�? for i in range(4): - week_start = now - timedelta(weeks=i+1) - week_end = now - timedelta(weeks=i) - labels.insert(0, f"第{i+1}�?) - - with db_manager.get_session() as session: - week_tokens = session.query(func.sum(Conversation.response_time)).filter( - Conversation.timestamp >= week_start, - Conversation.timestamp < week_end - ).scalar() or 0 - tokens.insert(0, week_tokens) - costs.insert(0, week_tokens * 0.0001) - - return jsonify({ - 'success': True, - 'labels': labels, - 'tokens': tokens, - 'costs': costs - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/token-monitor/records') -def get_token_monitor_records(): - """获取Token使用详细记录""" - try: - limit = request.args.get('limit', 50, type=int) - - with db_manager.get_session() as session: - conversations = session.query(Conversation).order_by( - Conversation.timestamp.desc() - ).limit(limit).all() - - records = [] - for conv in conversations: - records.append({ - 'timestamp': conv.timestamp.isoformat() if conv.timestamp else None, - 'user_id': f"user_{conv.id}", # 使用工单ID生成用户ID - 'model': 'qwen-turbo', # 模拟模型名称 - 'input_tokens': conv.confidence_score or 0, # 使用confidence_score模拟输入Token - 'output_tokens': (conv.confidence_score or 0) * 0.5, # 模拟输出Token - 'total_tokens': (conv.confidence_score or 0) * 1.5, # 模拟总Token - 'cost': (conv.confidence_score or 0) * 0.0001, # 模拟成本 - 'response_time': conv.response_time or 0 - }) - - return jsonify({ - 'success': True, - 'records': records - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/token-monitor/settings', methods=['POST']) -def save_token_monitor_settings(): - """保存Token监控设置""" - try: - data = request.get_json() - - # 这里可以将设置保存到数据库或配置文件 - # 暂时返回成功 - - return jsonify({ - 'success': True, - 'message': 'Token设置已保�? - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/token-monitor/export') -def export_token_monitor_data(): - """导出Token使用数据""" - try: - from openpyxl import Workbook - from openpyxl.styles import Font - - wb = Workbook() - ws = wb.active - ws.title = "Token使用数据" - - # 添加标题 - ws['A1'] = 'Token使用数据导出' - ws['A1'].font = Font(size=16, bold=True) - - # 添加表头 - headers = ['时间', '用户', '模型', '输入Token', '输出Token', '总Token', '成本', '响应时间'] - for col, header in enumerate(headers, 1): - ws.cell(row=3, column=col, value=header) - - # 添加数据 - with db_manager.get_session() as session: - conversations = session.query(Conversation).order_by( - Conversation.timestamp.desc() - ).limit(1000).all() - - for row, conv in enumerate(conversations, 4): - ws.cell(row=row, column=1, value=conv.timestamp.isoformat() if conv.timestamp else '') - ws.cell(row=row, column=2, value=conv.user_id or '') - ws.cell(row=row, column=3, value='qwen-turbo') - ws.cell(row=row, column=4, value=conv.response_time or 0) - ws.cell(row=row, column=5, value=(conv.response_time or 0) * 0.5) - ws.cell(row=row, column=6, value=(conv.response_time or 0) * 1.5) - ws.cell(row=row, column=7, value=(conv.response_time or 0) * 0.0001) - ws.cell(row=row, column=8, value=conv.response_time or 0) - - # 保存文件 - import tempfile - import os - temp_path = os.path.join(tempfile.gettempdir(), 'token_usage_data.xlsx') - wb.save(temp_path) - - return send_file(temp_path, as_attachment=True, download_name='token_usage_data.xlsx') - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# AI监控相关API -@app.route('/api/ai-monitor/stats') -def get_ai_monitor_stats(): - """获取AI监控统计""" - try: - with db_manager.get_session() as session: - total_calls = session.query(Conversation).count() - - # 模拟成功率计算(基于响应时间�? successful_calls = session.query(Conversation).filter( - Conversation.response_time < 5000 # 响应时间小于5秒认为成�? ).count() - - success_rate = (successful_calls / total_calls * 100) if total_calls > 0 else 0 - error_rate = 100 - success_rate - - avg_response_time = session.query(func.avg(Conversation.response_time)).scalar() or 0 - - return jsonify({ - 'success': True, - 'total_calls': total_calls, - 'success_rate': round(success_rate, 1), - 'error_rate': round(error_rate, 1), - 'avg_response_time': round(avg_response_time, 0) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/ai-monitor/model-comparison') -def get_model_comparison(): - """获取模型性能对比数据""" - try: - # 模拟不同模型的性能数据 - models = ['qwen-turbo', 'gpt-3.5-turbo', 'claude-3-sonnet'] - success_rates = [95.2, 92.8, 94.1] - response_times = [1200, 1500, 1100] - - return jsonify({ - 'success': True, - 'models': models, - 'success_rates': success_rates, - 'response_times': response_times - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/ai-monitor/error-distribution') -def get_error_distribution(): - """获取错误类型分布""" - try: - # 模拟错误类型分布 - error_types = ['超时错误', 'API错误', '网络错误', '参数错误', '其他错误'] - counts = [15, 8, 12, 5, 3] - - return jsonify({ - 'success': True, - 'error_types': error_types, - 'counts': counts - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/ai-monitor/error-log') -def get_error_log(): - """获取错误日志""" - try: - # 模拟错误日志数据 - errors = [] - from datetime import datetime, timedelta - import random - - error_types = ['超时错误', 'API错误', '网络错误', '参数错误', '其他错误'] - models = ['qwen-turbo', 'gpt-3.5-turbo', 'claude-3-sonnet'] - - for i in range(20): - errors.append({ - 'id': i + 1, - 'timestamp': (datetime.now() - timedelta(hours=random.randint(1, 168))).isoformat(), - 'error_type': random.choice(error_types), - 'error_message': f'错误消息 {i + 1}', - 'model': random.choice(models), - 'user_id': f'user_{random.randint(1, 10)}' - }) - - return jsonify({ - 'success': True, - 'errors': errors - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/ai-monitor/error-log', methods=['DELETE']) -def clear_error_log(): - """清空错误日志""" - try: - # 这里应该清空实际的错误日志表 - return jsonify({ - 'success': True, - 'message': '错误日志已清�? - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 系统优化相关API -@app.route('/api/system-optimizer/status') -def get_system_optimizer_status(): - """获取系统优化状�?"" - try: - import psutil - - # 获取系统资源使用情况 - cpu_usage = psutil.cpu_percent(interval=1) - memory = psutil.virtual_memory() - disk = psutil.disk_usage('/') - - # 模拟网络延迟 - network_latency = 50 # ms - - # 模拟健康分数 - system_health = max(0, 100 - cpu_usage - memory.percent/2 - disk.percent/4) - database_health = 98 - api_health = 92 - cache_health = 99 - - return jsonify({ - 'success': True, - 'cpu_usage': round(cpu_usage, 1), - 'memory_usage': round(memory.percent, 1), - 'disk_usage': round(disk.percent, 1), - 'network_latency': network_latency, - 'system_health': round(system_health, 1), - 'database_health': database_health, - 'api_health': api_health, - 'cache_health': cache_health - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/optimize-cpu', methods=['POST']) -def optimize_cpu(): - """CPU优化""" - try: - # 模拟CPU优化过程 - import time - time.sleep(1) # 模拟优化时间 - - return jsonify({ - 'success': True, - 'message': 'CPU优化完成', - 'progress': 100 - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/optimize-memory', methods=['POST']) -def optimize_memory(): - """内存优化""" - try: - # 模拟内存优化过程 - import time - time.sleep(1) # 模拟优化时间 - - return jsonify({ - 'success': True, - 'message': '内存优化完成', - 'progress': 100 - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/optimize-disk', methods=['POST']) -def optimize_disk(): - """磁盘优化""" - try: - # 模拟磁盘优化过程 - import time - time.sleep(1) # 模拟优化时间 - - return jsonify({ - 'success': True, - 'message': '磁盘优化完成', - 'progress': 100 - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/security-settings', methods=['GET', 'POST']) -def security_settings(): - """安全设置""" - try: - if request.method == 'GET': - # 获取安全设置 - return jsonify({ - 'success': True, - 'input_validation': True, - 'rate_limiting': True, - 'sql_injection_protection': True, - 'xss_protection': True - }) - else: - # 保存安全设置 - data = request.get_json() - # 这里应该保存到数据库或配置文�? - return jsonify({ - 'success': True, - 'message': '安全设置已保�? - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/traffic-settings', methods=['GET', 'POST']) -def traffic_settings(): - """流量设置""" - try: - if request.method == 'GET': - # 获取流量设置 - return jsonify({ - 'success': True, - 'request_limit': 100, - 'concurrent_limit': 50, - 'ip_whitelist': ['127.0.0.1', '192.168.1.1'] - }) - else: - # 保存流量设置 - data = request.get_json() - # 这里应该保存到数据库或配置文�? - return jsonify({ - 'success': True, - 'message': '流量设置已保�? - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/cost-settings', methods=['GET', 'POST']) -def cost_settings(): - """成本设置""" - try: - if request.method == 'GET': - # 获取成本设置 - return jsonify({ - 'success': True, - 'monthly_budget_limit': 1000, - 'per_call_cost_limit': 0.1, - 'auto_cost_control': True - }) - else: - # 保存成本设置 - data = request.get_json() - # 这里应该保存到数据库或配置文�? - return jsonify({ - 'success': True, - 'message': '成本设置已保�? - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/system-optimizer/health-check', methods=['POST']) -def health_check(): - """健康检�?"" - try: - import psutil - - # 执行健康检�? cpu_usage = psutil.cpu_percent(interval=1) - memory = psutil.virtual_memory() - disk = psutil.disk_usage('/') - - # 计算健康分数 - system_health = max(0, 100 - cpu_usage - memory.percent/2 - disk.percent/4) - - return jsonify({ - 'success': True, - 'message': '健康检查完�?, - 'cpu_usage': round(cpu_usage, 1), - 'memory_usage': round(memory.percent, 1), - 'disk_usage': round(disk.percent, 1), - 'system_health': round(system_health, 1), - 'database_health': 98, - 'api_health': 92, - 'cache_health': 99 - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 数据库备份管理API -@app.route('/api/backup/info') -def get_backup_info(): - """获取备份信息""" - try: - info = backup_manager.get_backup_info() - return jsonify({ - "success": True, - "backup_info": info - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/backup/create', methods=['POST']) -def create_backup(): - """创建数据备份""" - try: - result = backup_manager.backup_all_data() - return jsonify({ - "success": result["success"], - "message": "备份创建成功" if result["success"] else "备份创建失败", - "backup_result": result - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/backup/restore', methods=['POST']) -def restore_backup(): - """从备份恢复数�?"" - try: - data = request.get_json() or {} - table_name = data.get('table_name') # 可选:指定恢复特定�? - result = backup_manager.restore_from_backup(table_name) - return jsonify({ - "success": result["success"], - "message": "数据恢复成功" if result["success"] else "数据恢复失败", - "restore_result": result - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/database/status') -def get_database_status(): - """获取数据库状态信�?"" - try: - # MySQL数据库状�? mysql_status = { - "type": "MySQL", - "url": str(db_manager.engine.url).replace(db_manager.engine.url.password, "******") if db_manager.engine.url.password else str(db_manager.engine.url), - "connected": db_manager.test_connection() - } - - # 统计MySQL数据 - with db_manager.get_session() as session: - mysql_status["table_counts"] = { - "work_orders": session.query(WorkOrder).count(), - "conversations": session.query(Conversation).count(), - "knowledge_entries": session.query(KnowledgeEntry).count(), - "vehicle_data": session.query(VehicleData).count(), - "alerts": session.query(Alert).count() - } - - # SQLite备份状�? - backup_info = backup_manager.get_backup_info() - - return jsonify({ - "success": True, - "mysql": mysql_status, - "sqlite_backup": backup_info - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -if __name__ == '__main__': - import time - app.config['START_TIME'] = time.time() - app.config['SERVER_PORT'] = 5000 - app.config['WEBSOCKET_PORT'] = 8765 - app.run(debug=True, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/src/web/app_clean.py b/src/web/app_clean.py deleted file mode 100644 index 870b799..0000000 --- a/src/web/app_clean.py +++ /dev/null @@ -1,740 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TSP助手预警管理Web应用 -提供预警系统的Web界面和API接口 -重构版本 - 使用蓝图架构 -""" - -import sys -import os -import logging -from datetime import datetime, timedelta - -from flask import Flask, render_template, request, jsonify, send_from_directory -from flask_cors import CORS - -# 添加项目根目录到Python路径 -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from src.main import TSPAssistant -from src.agent_assistant import TSPAgentAssistant -from src.dialogue.realtime_chat import RealtimeChatManager -from src.vehicle.vehicle_data_manager import VehicleDataManager -from src.core.database import db_manager -from src.core.models import Conversation, Alert, WorkOrder -from src.core.query_optimizer import query_optimizer - -# 导入蓝图 -from src.web.blueprints.alerts import alerts_bp -from src.web.blueprints.workorders import workorders_bp -from src.web.blueprints.conversations import conversations_bp -from src.web.blueprints.knowledge import knowledge_bp -from src.web.blueprints.monitoring import monitoring_bp -from src.web.blueprints.system import system_bp - -# 配置日志 -logger = logging.getLogger(__name__) - -# 抑制 /api/health 的访问日志 -werkzeug_logger = logging.getLogger('werkzeug') - -class HealthLogFilter(logging.Filter): - def filter(self, record): - try: - msg = record.getMessage() - return '/api/health' not in msg - except Exception: - return True - -werkzeug_logger.addFilter(HealthLogFilter()) - -# 创建Flask应用 -app = Flask(__name__) -CORS(app) - -# 配置上传文件夹 -UPLOAD_FOLDER = 'uploads' -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER -app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size - -# 延迟初始化TSP助手和Agent助手(避免启动时重复初始化) -assistant = None -agent_assistant = None -chat_manager = None -vehicle_manager = None - -def get_assistant(): - """获取TSP助手实例(懒加载)""" - global assistant - if assistant is None: - assistant = TSPAssistant() - return assistant - -def get_agent_assistant(): - """获取Agent助手实例(懒加载)""" - global agent_assistant - if agent_assistant is None: - agent_assistant = TSPAgentAssistant() - return agent_assistant - -def get_chat_manager(): - """获取聊天管理器实例(懒加载)""" - global chat_manager - if chat_manager is None: - chat_manager = RealtimeChatManager() - return chat_manager - -def get_vehicle_manager(): - """获取车辆数据管理器实例(懒加载)""" - global vehicle_manager - if vehicle_manager is None: - vehicle_manager = VehicleDataManager() - return vehicle_manager - -# 注册蓝图 -app.register_blueprint(alerts_bp) -app.register_blueprint(workorders_bp) -app.register_blueprint(conversations_bp) -app.register_blueprint(knowledge_bp) -app.register_blueprint(monitoring_bp) -app.register_blueprint(system_bp) - -# 页面路由 -@app.route('/') -def index(): - """主页 - 综合管理平台""" - return render_template('dashboard.html') - -@app.route('/alerts') -def alerts(): - """预警管理页面""" - return render_template('index.html') - -@app.route('/chat') -def chat(): - """实时对话页面 (WebSocket版本)""" - return render_template('chat.html') - -@app.route('/chat-http') -def chat_http(): - """实时对话页面 (HTTP版本)""" - return render_template('chat_http.html') - -@app.route('/uploads/') -def uploaded_file(filename): - """提供上传文件的下载服务""" - return send_from_directory(app.config['UPLOAD_FOLDER'], filename) - -# 核心API路由 -@app.route('/api/health') -def get_health(): - """获取系统健康状态(附加1小时业务指标)""" - try: - base = get_assistant().get_system_health() or {} - # 追加数据库近1小时指标 - with db_manager.get_session() as session: - since = datetime.now() - timedelta(hours=1) - conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() - resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] - avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 - open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() - levels = session.query(Alert.level).filter(Alert.is_active == True).all() - level_map = {} - for (lvl,) in levels: - level_map[lvl] = level_map.get(lvl, 0) + 1 - base.update({ - "throughput_1h": conv_count, - "avg_response_time_1h": avg_resp, - "open_workorders": open_wos, - "active_alerts_by_level": level_map - }) - return jsonify(base) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules') -def get_rules(): - """获取预警规则列表""" - try: - rules = get_assistant().alert_system.rules - rules_data = [] - for name, rule in rules.items(): - rules_data.append({ - "name": rule.name, - "description": rule.description, - "alert_type": rule.alert_type.value, - "level": rule.level.value, - "threshold": rule.threshold, - "condition": rule.condition, - "enabled": rule.enabled, - "check_interval": rule.check_interval, - "cooldown": rule.cooldown - }) - return jsonify(rules_data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules', methods=['POST']) -def create_rule(): - """创建预警规则""" - try: - from src.analytics.alert_system import AlertRule, AlertLevel, AlertType - data = request.get_json() - rule = AlertRule( - name=data['name'], - description=data['description'], - alert_type=AlertType(data['alert_type']), - level=AlertLevel(data['level']), - threshold=float(data['threshold']), - condition=data['condition'], - enabled=data.get('enabled', True), - check_interval=int(data.get('check_interval', 300)), - cooldown=int(data.get('cooldown', 3600)) - ) - - success = get_assistant().alert_system.add_custom_rule(rule) - if success: - return jsonify({"success": True, "message": "规则创建成功"}) - else: - return jsonify({"success": False, "message": "规则创建失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['PUT']) -def update_rule(rule_name): - """更新预警规则""" - try: - data = request.get_json() - success = get_assistant().alert_system.update_rule(rule_name, **data) - if success: - return jsonify({"success": True, "message": "规则更新成功"}) - else: - return jsonify({"success": False, "message": "规则更新失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['DELETE']) -def delete_rule(rule_name): - """删除预警规则""" - try: - success = get_assistant().alert_system.delete_rule(rule_name) - if success: - return jsonify({"success": True, "message": "规则删除成功"}) - else: - return jsonify({"success": False, "message": "规则删除失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/start', methods=['POST']) -def start_monitoring(): - """启动监控服务""" - try: - success = get_assistant().start_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已启动"}) - else: - return jsonify({"success": False, "message": "启动监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/stop', methods=['POST']) -def stop_monitoring(): - """停止监控服务""" - try: - success = get_assistant().stop_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已停止"}) - else: - return jsonify({"success": False, "message": "停止监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/status') -def get_monitor_status(): - """获取监控服务状态""" - try: - health = get_assistant().get_system_health() - return jsonify({ - "monitor_status": health.get("monitor_status", "unknown"), - "health_score": health.get("health_score", 0), - "active_alerts": health.get("active_alerts", 0) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/check-alerts', methods=['POST']) -def check_alerts(): - """手动检查预警""" - try: - alerts = get_assistant().check_alerts() - return jsonify({ - "success": True, - "alerts": alerts, - "count": len(alerts) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 实时对话相关路由 -@app.route('/api/chat/session', methods=['POST']) -def create_chat_session(): - """创建对话会话""" - try: - data = request.get_json() - user_id = data.get('user_id', 'anonymous') - work_order_id = data.get('work_order_id') - - session_id = get_chat_manager().create_session(user_id, work_order_id) - - return jsonify({ - "success": True, - "session_id": session_id, - "message": "会话创建成功" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/message', methods=['POST']) -def send_chat_message(): - """发送聊天消息""" - try: - data = request.get_json() - session_id = data.get('session_id') - message = data.get('message') - - if not session_id or not message: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().process_message(session_id, message) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/history/') -def get_chat_history(session_id): - """获取对话历史""" - try: - history = get_chat_manager().get_session_history(session_id) - return jsonify({ - "success": True, - "history": history - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order', methods=['POST']) -def create_work_order(): - """创建工单""" - try: - data = request.get_json() - session_id = data.get('session_id') - title = data.get('title') - description = data.get('description') - category = data.get('category', '技术问题') - priority = data.get('priority', 'medium') - - if not session_id or not title or not description: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().create_work_order(session_id, title, description, category, priority) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order/') -def get_work_order_status(work_order_id): - """获取工单状态""" - try: - result = get_chat_manager().get_work_order_status(work_order_id) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/session/', methods=['DELETE']) -def end_chat_session(session_id): - """结束对话会话""" - try: - success = get_chat_manager().end_session(session_id) - return jsonify({ - "success": success, - "message": "会话已结束" if success else "结束会话失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/sessions') -def get_active_sessions(): - """获取活跃会话列表""" - try: - sessions = chat_manager.get_active_sessions() - return jsonify({ - "success": True, - "sessions": sessions - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent相关API -@app.route('/api/agent/status') -def get_agent_status(): - """获取Agent状态""" - try: - status = agent_assistant.get_agent_status() - return jsonify(status) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/action-history') -def get_agent_action_history(): - """获取Agent动作执行历史""" - try: - limit = request.args.get('limit', 50, type=int) - history = agent_assistant.get_action_history(limit) - return jsonify({ - "success": True, - "history": history, - "count": len(history) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/trigger-sample', methods=['POST']) -def trigger_sample_action(): - """触发示例动作""" - try: - import asyncio - result = asyncio.run(agent_assistant.trigger_sample_actions()) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/clear-history', methods=['POST']) -def clear_agent_history(): - """清空Agent执行历史""" - try: - result = agent_assistant.clear_execution_history() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/llm-stats') -def get_llm_stats(): - """获取LLM使用统计""" - try: - stats = agent_assistant.get_llm_usage_stats() - return jsonify({ - "success": True, - "stats": stats - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/toggle', methods=['POST']) -def toggle_agent_mode(): - """切换Agent模式""" - try: - data = request.get_json() - enabled = data.get('enabled', True) - success = agent_assistant.toggle_agent_mode(enabled) - return jsonify({ - "success": success, - "message": f"Agent模式已{'启用' if enabled else '禁用'}" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/start', methods=['POST']) -def start_agent_monitoring(): - """启动Agent监控""" - try: - success = agent_assistant.start_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已启动" if success else "启动失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/stop', methods=['POST']) -def stop_agent_monitoring(): - """停止Agent监控""" - try: - success = agent_assistant.stop_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已停止" if success else "停止失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/proactive-monitoring', methods=['POST']) -def proactive_monitoring(): - """主动监控检查""" - try: - result = agent_assistant.run_proactive_monitoring() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/intelligent-analysis', methods=['POST']) -def intelligent_analysis(): - """智能分析""" - try: - analysis = get_agent_assistant().run_intelligent_analysis() - return jsonify({"success": True, "analysis": analysis}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/chat', methods=['POST']) -def agent_chat(): - """Agent对话接口""" - try: - data = request.get_json() - message = data.get('message', '') - context = data.get('context', {}) - - if not message: - return jsonify({"error": "消息不能为空"}), 400 - - # 使用Agent助手处理消息 - agent_assistant = get_agent_assistant() - - # 模拟Agent处理(实际应该调用真正的Agent处理逻辑) - import asyncio - result = asyncio.run(agent_assistant.process_message_agent( - message=message, - user_id=context.get('user_id', 'admin'), - work_order_id=None, - enable_proactive=True - )) - - return jsonify({ - "success": True, - "response": result.get('response', 'Agent已处理您的请求'), - "actions": result.get('actions', []), - "status": result.get('status', 'completed') - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent 工具统计与自定义工具 -@app.route('/api/agent/tools/stats') -def get_agent_tools_stats(): - try: - tools = agent_assistant.agent_core.tool_manager.get_available_tools() - performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() - return jsonify({ - "success": True, - "tools": tools, - "performance": performance - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/register', methods=['POST']) -def register_custom_tool(): - """注册自定义工具(仅登记元数据,函数为占位符)""" - try: - data = request.get_json() or {} - name = data.get('name') - description = data.get('description', '') - if not name: - return jsonify({"error": "缺少工具名称"}), 400 - - def _placeholder_tool(**kwargs): - return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs} - - agent_assistant.agent_core.tool_manager.register_tool( - name, - _placeholder_tool, - metadata={"description": description, "custom": True} - ) - return jsonify({"success": True, "message": "工具已注册"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/unregister/', methods=['DELETE']) -def unregister_custom_tool(name): - try: - success = agent_assistant.agent_core.tool_manager.unregister_tool(name) - return jsonify({"success": success}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 分析相关API -@app.route('/api/analytics') -def get_analytics(): - """获取分析数据""" - try: - # 支持多种参数 - time_range = request.args.get('timeRange', request.args.get('days', '30')) - dimension = request.args.get('dimension', 'workorders') - analytics = generate_db_analytics(int(time_range), dimension) - return jsonify(analytics) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -def generate_db_analytics(days: int, dimension: str) -> dict: - """基于数据库生成真实分析数据(优化版)""" - # 使用优化后的查询 - return query_optimizer.get_analytics_optimized(days) - -@app.route('/api/analytics/export') -def export_analytics(): - """导出分析报告""" - try: - # 生成Excel报告(使用数据库真实数据) - analytics = generate_db_analytics(30, 'workorders') - - # 创建工作簿 - from openpyxl import Workbook - from openpyxl.styles import Font - wb = Workbook() - ws = wb.active - ws.title = "分析报告" - - # 添加标题 - ws['A1'] = 'TSP智能助手分析报告' - ws['A1'].font = Font(size=16, bold=True) - - # 添加工单统计 - ws['A3'] = '工单统计' - ws['A3'].font = Font(bold=True) - ws['A4'] = '总工单数' - ws['B4'] = analytics['workorders']['total'] - ws['A5'] = '待处理' - ws['B5'] = analytics['workorders']['open'] - ws['A6'] = '已解决' - ws['B6'] = analytics['workorders']['resolved'] - - # 保存文件 - report_path = 'uploads/analytics_report.xlsx' - os.makedirs('uploads', exist_ok=True) - wb.save(report_path) - - from flask import send_file - return send_file(report_path, as_attachment=True, download_name='analytics_report.xlsx') - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 车辆数据相关API -@app.route('/api/vehicle/data') -def get_vehicle_data(): - """获取车辆数据""" - try: - vehicle_id = request.args.get('vehicle_id') - vehicle_vin = request.args.get('vehicle_vin') - data_type = request.args.get('data_type') - limit = request.args.get('limit', 10, type=int) - - if vehicle_vin: - data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) - elif vehicle_id: - data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit) - else: - data = vehicle_manager.search_vehicle_data(limit=limit) - - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data/vin//latest') -def get_latest_vehicle_data_by_vin(vehicle_vin): - """按VIN获取车辆最新数据""" - try: - data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//latest') -def get_latest_vehicle_data(vehicle_id): - """获取车辆最新数据""" - try: - data = vehicle_manager.get_latest_vehicle_data(vehicle_id) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//summary') -def get_vehicle_summary(vehicle_id): - """获取车辆数据摘要""" - try: - summary = vehicle_manager.get_vehicle_summary(vehicle_id) - return jsonify(summary) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data', methods=['POST']) -def add_vehicle_data(): - """添加车辆数据""" - try: - data = request.get_json() - success = vehicle_manager.add_vehicle_data( - vehicle_id=data['vehicle_id'], - data_type=data['data_type'], - data_value=data['data_value'], - vehicle_vin=data.get('vehicle_vin') - ) - return jsonify({"success": success, "message": "数据添加成功" if success else "添加失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/init-sample-data', methods=['POST']) -def init_sample_vehicle_data(): - """初始化示例车辆数据""" - try: - success = vehicle_manager.add_sample_vehicle_data() - return jsonify({"success": success, "message": "示例数据初始化成功" if success else "初始化失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# API测试相关接口 -@app.route('/api/test/connection', methods=['POST']) -def test_api_connection(): - """测试API连接""" - try: - data = request.get_json() - api_provider = data.get('api_provider', 'openai') - api_base_url = data.get('api_base_url', '') - api_key = data.get('api_key', '') - model_name = data.get('model_name', 'qwen-turbo') - - # 这里可以调用LLM客户端进行连接测试 - # 暂时返回模拟结果 - - return jsonify({ - "success": True, - "message": f"API连接测试成功 - {api_provider}", - "response_time": "150ms", - "model_status": "可用" - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route('/api/test/model', methods=['POST']) -def test_model_response(): - """测试模型回答""" - try: - data = request.get_json() - test_message = data.get('test_message', '你好,请简单介绍一下你自己') - - # 这里可以调用LLM客户端进行回答测试 - # 暂时返回模拟结果 - return jsonify({ - "success": True, - "test_message": test_message, - "response": "你好!我是TSP智能助手,基于大语言模型构建的智能客服系统。我可以帮助您解决车辆相关问题,提供技术支持和服务。", - "response_time": "1.2s", - "tokens_used": 45 - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - -if __name__ == '__main__': - import time - app.config['START_TIME'] = time.time() - app.config['SERVER_PORT'] = 5000 - app.config['WEBSOCKET_PORT'] = 8765 - app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/src/web/app_new.py b/src/web/app_new.py deleted file mode 100644 index 9a70fb6..0000000 --- a/src/web/app_new.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -TSP智能助手 - 新版Web应用 -支持Vue 3前端和传统HTML页面 -""" - -import os -import sys -import json -import logging -from datetime import datetime -from flask import Flask, render_template, jsonify, request, send_from_directory -from flask_cors import CORS -from werkzeug.exceptions import NotFound - -# 添加项目根目录到Python路径 -current_dir = os.path.dirname(os.path.abspath(__file__)) -project_root = os.path.dirname(os.path.dirname(current_dir)) -sys.path.insert(0, project_root) - -from src.core.database import get_db_connection -from src.analytics.monitor_service import MonitorService -from src.analytics.alert_system import AlertSystem -from src.dialogue.dialogue_manager import DialogueManager -from src.knowledge_base.knowledge_manager import KnowledgeManager -from src.integrations.workorder_sync import WorkOrderSync -from src.integrations.flexible_field_mapper import FlexibleFieldMapper - -# 配置日志 -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - -def create_app(): - """创建Flask应用""" - app = Flask(__name__) - - # 配置 - app.config['SECRET_KEY'] = 'tsp-assistant-secret-key' - app.config['JSON_AS_ASCII'] = False - - # 启用CORS - CORS(app, resources={ - r"/api/*": {"origins": ["http://localhost:3000", "http://127.0.0.1:3000"]}, - r"/ws/*": {"origins": ["http://localhost:3000", "http://127.0.0.1:3000"]} - }) - - # 初始化服务 - monitor_service = MonitorService() - alert_system = AlertSystem() - dialogue_manager = DialogueManager() - knowledge_manager = KnowledgeManager() - workorder_sync = WorkOrderSync() - field_mapper = FlexibleFieldMapper() - - # 注册蓝图 - from src.web.blueprints.alerts import alerts_bp - from src.web.blueprints.conversations import conversations_bp - from src.web.blueprints.knowledge import knowledge_bp - from src.web.blueprints.monitoring import monitoring_bp - from src.web.blueprints.system import system_bp - from src.web.blueprints.feishu_sync import feishu_sync_bp - from src.web.blueprints.workorders import workorders_bp - - app.register_blueprint(alerts_bp, url_prefix='/api/alerts') - app.register_blueprint(conversations_bp, url_prefix='/api/conversations') - app.register_blueprint(knowledge_bp, url_prefix='/api/knowledge') - app.register_blueprint(monitoring_bp, url_prefix='/api/monitor') - app.register_blueprint(system_bp, url_prefix='/api/system') - app.register_blueprint(feishu_sync_bp, url_prefix='/api/feishu') - app.register_blueprint(workorders_bp, url_prefix='/api/workorders') - - # 静态文件路由 - @app.route('/static/dist/') - def serve_frontend_dist(filename): - """提供Vue前端构建文件""" - dist_path = os.path.join(current_dir, 'static', 'dist') - return send_from_directory(dist_path, filename) - - @app.route('/') - def index(): - """首页 - 重定向到Vue应用""" - return render_template('index_new.html') - - @app.route('/chat') - def chat(): - """聊天页面 - 重定向到Vue应用""" - return render_template('index_new.html') - - @app.route('/alerts') - def alerts(): - """预警页面 - 重定向到Vue应用""" - return render_template('index_new.html') - - @app.route('/knowledge') - def knowledge(): - """知识库页面 - 重定向到Vue应用""" - return render_template('index_new.html') - - @app.route('/field-mapping') - def field_mapping(): - """字段映射页面 - 重定向到Vue应用""" - return render_template('index_new.html') - - @app.route('/system') - def system(): - """系统设置页面 - 重定向到Vue应用""" - return render_template('index_new.html') - - # API路由 - @app.route('/api/health') - def health(): - """系统健康检查""" - try: - # 获取系统健康状态 - health_data = { - 'status': 'healthy', - 'timestamp': datetime.now().isoformat(), - 'health_score': 85.5, - 'services': { - 'database': 'healthy', - 'monitor': 'healthy', - 'alerts': 'healthy', - 'knowledge': 'healthy' - } - } - return jsonify(health_data) - except Exception as e: - logger.error(f"健康检查失败: {e}") - return jsonify({ - 'status': 'unhealthy', - 'error': str(e), - 'timestamp': datetime.now().isoformat() - }), 500 - - @app.route('/api/status') - def status(): - """系统状态""" - try: - status_data = { - 'version': '1.4.0', - 'uptime': '2天 14小时 32分钟', - 'python_version': '3.9.7', - 'database': 'SQLite 3.36.0', - 'timestamp': datetime.now().isoformat() - } - return jsonify(status_data) - except Exception as e: - logger.error(f"获取状态失败: {e}") - return jsonify({'error': str(e)}), 500 - - @app.route('/api/performance') - def performance(): - """性能指标""" - try: - import psutil - - performance_data = { - 'cpu_usage': psutil.cpu_percent(), - 'memory_usage': psutil.virtual_memory().percent, - 'disk_usage': psutil.disk_usage('/').percent, - 'timestamp': datetime.now().isoformat() - } - return jsonify(performance_data) - except ImportError: - # 如果没有psutil,返回模拟数据 - performance_data = { - 'cpu_usage': 45.2, - 'memory_usage': 68.5, - 'disk_usage': 23.1, - 'timestamp': datetime.now().isoformat() - } - return jsonify(performance_data) - except Exception as e: - logger.error(f"获取性能指标失败: {e}") - return jsonify({'error': str(e)}), 500 - - # 错误处理 - @app.errorhandler(404) - def not_found(error): - """404错误处理""" - if request.path.startswith('/api/'): - return jsonify({'error': 'API endpoint not found'}), 404 - return render_template('index_new.html') - - @app.errorhandler(500) - def internal_error(error): - """500错误处理""" - logger.error(f"内部服务器错误: {error}") - if request.path.startswith('/api/'): - return jsonify({'error': 'Internal server error'}), 500 - return render_template('index_new.html') - - return app - -if __name__ == '__main__': - app = create_app() - - # 检查前端构建文件 - dist_path = os.path.join(os.path.dirname(__file__), 'static', 'dist') - if not os.path.exists(dist_path): - print("警告: 前端构建文件不存在,请先运行 build_frontend.bat") - print("或者访问 http://localhost:5000/legacy 使用传统页面") - - print("TSP智能助手Web服务器启动中...") - print("前端地址: http://localhost:5000") - print("API地址: http://localhost:5000/api") - print("WebSocket: ws://localhost:8765") - - app.run( - host='0.0.0.0', - port=5000, - debug=True, - threaded=True - ) \ No newline at end of file diff --git a/src/web/blueprints/alerts.py b/src/web/blueprints/alerts.py index cc694b7..69b4b4a 100644 --- a/src/web/blueprints/alerts.py +++ b/src/web/blueprints/alerts.py @@ -5,26 +5,18 @@ """ from flask import Blueprint, request, jsonify -from src.main import TSPAssistant +from src.web.service_manager import service_manager +from src.web.error_handlers import handle_api_errors, create_error_response, create_success_response from src.analytics.alert_system import AlertRule, AlertLevel, AlertType alerts_bp = Blueprint('alerts', __name__, url_prefix='/api/alerts') -def get_assistant(): - """获取TSP助手实例(懒加载)""" - global _assistant - if '_assistant' not in globals(): - _assistant = TSPAssistant() - return _assistant - @alerts_bp.route('') +@handle_api_errors def get_alerts(): """获取预警列表""" - try: - alerts = get_assistant().get_active_alerts() - return jsonify(alerts) - except Exception as e: - return jsonify({"error": str(e)}), 500 + alerts = service_manager.get_assistant().get_active_alerts() + return jsonify(alerts) @alerts_bp.route('', methods=['POST']) def create_alert(): diff --git a/src/web/blueprints/core.py b/src/web/blueprints/core.py new file mode 100644 index 0000000..e6baf20 --- /dev/null +++ b/src/web/blueprints/core.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- +""" +核心功能蓝图 +处理系统核心功能的API路由 +""" + +from flask import Blueprint, request, jsonify +from typing import Dict, Any +from datetime import datetime, timedelta + +from src.web.service_manager import service_manager +from src.web.error_handlers import handle_api_errors, create_error_response, create_success_response +from src.core.database import db_manager +from src.core.models import Conversation, Alert, WorkOrder +from src.core.query_optimizer import query_optimizer + +core_bp = Blueprint('core', __name__, url_prefix='/api') + + +@core_bp.route('/health') +@handle_api_errors +def get_health() -> Dict[str, Any]: + """获取系统健康状态(附加1小时业务指标)""" + base = service_manager.get_assistant().get_system_health() or {} + + # 追加数据库近1小时指标 + with db_manager.get_session() as session: + since = datetime.now() - timedelta(hours=1) + conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() + resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] + avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 + open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() + levels = session.query(Alert.level).filter(Alert.is_active == True).all() + level_map = {} + for (lvl,) in levels: + level_map[lvl] = level_map.get(lvl, 0) + 1 + + base.update({ + "throughput_1h": conv_count, + "avg_response_time_1h": avg_resp, + "open_workorders": open_wos, + "active_alerts_by_level": level_map + }) + return jsonify(base) + + +@core_bp.route('/rules') +@handle_api_errors +def get_rules() -> Dict[str, Any]: + """获取预警规则列表""" + rules = service_manager.get_assistant().alert_system.rules + rules_data = [] + for name, rule in rules.items(): + rules_data.append({ + "name": rule.name, + "description": rule.description, + "alert_type": rule.alert_type.value, + "level": rule.level.value, + "threshold": rule.threshold, + "condition": rule.condition, + "enabled": rule.enabled, + "check_interval": rule.check_interval, + "cooldown": rule.cooldown + }) + return jsonify(rules_data) + + +@core_bp.route('/rules', methods=['POST']) +@handle_api_errors +def create_rule() -> Dict[str, Any]: + """创建预警规则""" + from src.analytics.alert_system import AlertRule, AlertLevel, AlertType + + data = request.get_json() + rule = AlertRule( + name=data['name'], + description=data['description'], + alert_type=AlertType(data['alert_type']), + level=AlertLevel(data['level']), + threshold=float(data['threshold']), + condition=data['condition'], + enabled=data.get('enabled', True), + check_interval=int(data.get('check_interval', 300)), + cooldown=int(data.get('cooldown', 3600)) + ) + + success = service_manager.get_assistant().alert_system.add_custom_rule(rule) + if success: + return jsonify(create_success_response(message="规则创建成功")) + else: + return create_error_response("规则创建失败", 400) + + +@core_bp.route('/rules/', methods=['PUT']) +@handle_api_errors +def update_rule(rule_name: str) -> Dict[str, Any]: + """更新预警规则""" + data = request.get_json() + success = service_manager.get_assistant().alert_system.update_rule(rule_name, **data) + if success: + return jsonify(create_success_response(message="规则更新成功")) + else: + return create_error_response("规则更新失败", 400) + + +@core_bp.route('/rules/', methods=['DELETE']) +@handle_api_errors +def delete_rule(rule_name: str) -> Dict[str, Any]: + """删除预警规则""" + success = service_manager.get_assistant().alert_system.delete_rule(rule_name) + if success: + return jsonify(create_success_response(message="规则删除成功")) + else: + return create_error_response("规则删除失败", 400) + + +@core_bp.route('/monitor/start', methods=['POST']) +@handle_api_errors +def start_monitoring() -> Dict[str, Any]: + """启动监控服务""" + success = service_manager.get_assistant().start_monitoring() + if success: + return jsonify(create_success_response(message="监控服务已启动")) + else: + return create_error_response("启动监控服务失败", 400) + + +@core_bp.route('/monitor/stop', methods=['POST']) +@handle_api_errors +def stop_monitoring() -> Dict[str, Any]: + """停止监控服务""" + success = service_manager.get_assistant().stop_monitoring() + if success: + return jsonify(create_success_response(message="监控服务已停止")) + else: + return create_error_response("停止监控服务失败", 400) + + +@core_bp.route('/monitor/status') +@handle_api_errors +def get_monitor_status() -> Dict[str, Any]: + """获取监控服务状态""" + health = service_manager.get_assistant().get_system_health() + return jsonify({ + "monitor_status": health.get("monitor_status", "unknown"), + "health_score": health.get("health_score", 0), + "active_alerts": health.get("active_alerts", 0) + }) + + +@core_bp.route('/check-alerts', methods=['POST']) +@handle_api_errors +def check_alerts() -> Dict[str, Any]: + """手动检查预警""" + alerts = service_manager.get_assistant().check_alerts() + return jsonify({ + "success": True, + "alerts": alerts, + "count": len(alerts) + }) + + +@core_bp.route('/analytics') +@handle_api_errors +def get_analytics() -> Dict[str, Any]: + """获取分析数据""" + # 支持多种参数 + time_range = request.args.get('timeRange', request.args.get('days', '30')) + dimension = request.args.get('dimension', 'workorders') + + # 参数验证 + try: + days = int(time_range) + if days <= 0 or days > 365: + days = 30 + except (ValueError, TypeError): + days = 30 + + analytics = query_optimizer.get_analytics_optimized(days) + + # 确保返回的数据结构完整 + if not analytics: + analytics = { + "workorders": {"total": 0, "open": 0, "resolved": 0, "trend": []}, + "alerts": {"total": 0, "critical": 0, "warning": 0, "trend": []}, + "conversations": {"total": 0, "avg_confidence": 0, "trend": []}, + "performance": {"avg_response_time": 0, "success_rate": 0} + } + + return jsonify(analytics) + + +@core_bp.route('/batch-delete/workorders', methods=['POST']) +@handle_api_errors +def batch_delete_workorders() -> Dict[str, Any]: + """批量删除工单""" + data = request.get_json() + workorder_ids = data.get('ids', []) + + if not workorder_ids: + return create_error_response("请选择要删除的工单", 400) + + try: + with db_manager.get_session() as session: + # 验证工单是否存在 + existing_workorders = session.query(WorkOrder).filter(WorkOrder.id.in_(workorder_ids)).all() + existing_ids = [wo.id for wo in existing_workorders] + + if len(existing_ids) != len(workorder_ids): + missing_ids = set(workorder_ids) - set(existing_ids) + return create_error_response(f"工单不存在: {list(missing_ids)}", 404) + + # 先删除相关的工单建议记录 + from src.core.models import WorkOrderSuggestion + session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id.in_(workorder_ids)).delete(synchronize_session=False) + + # 再删除工单 + deleted_count = session.query(WorkOrder).filter(WorkOrder.id.in_(workorder_ids)).delete(synchronize_session=False) + session.commit() + + return jsonify(create_success_response( + data={"deleted_count": deleted_count}, + message=f"成功删除 {deleted_count} 个工单" + )) + + except Exception as e: + return create_error_response(f"批量删除工单失败: {str(e)}", 500) + + +@core_bp.route('/batch-delete/alerts', methods=['POST']) +@handle_api_errors +def batch_delete_alerts() -> Dict[str, Any]: + """批量删除预警""" + data = request.get_json() + alert_ids = data.get('ids', []) + + if not alert_ids: + return create_error_response("请选择要删除的预警", 400) + + try: + with db_manager.get_session() as session: + # 验证预警是否存在 + existing_alerts = session.query(Alert).filter(Alert.id.in_(alert_ids)).all() + existing_ids = [alert.id for alert in existing_alerts] + + if len(existing_ids) != len(alert_ids): + missing_ids = set(alert_ids) - set(existing_ids) + return create_error_response(f"预警不存在: {list(missing_ids)}", 404) + + # 删除预警 + deleted_count = session.query(Alert).filter(Alert.id.in_(alert_ids)).delete(synchronize_session=False) + session.commit() + + return jsonify(create_success_response( + data={"deleted_count": deleted_count}, + message=f"成功删除 {deleted_count} 个预警" + )) + + except Exception as e: + return create_error_response(f"批量删除预警失败: {str(e)}", 500) + + +@core_bp.route('/batch-delete/knowledge', methods=['POST']) +@handle_api_errors +def batch_delete_knowledge() -> Dict[str, Any]: + """批量删除知识库条目""" + data = request.get_json() + knowledge_ids = data.get('ids', []) + + if not knowledge_ids: + return create_error_response("请选择要删除的知识库条目", 400) + + try: + with db_manager.get_session() as session: + # 验证知识库条目是否存在 + existing_knowledge = session.query(KnowledgeEntry).filter(KnowledgeEntry.id.in_(knowledge_ids)).all() + existing_ids = [kb.id for kb in existing_knowledge] + + if len(existing_ids) != len(knowledge_ids): + missing_ids = set(knowledge_ids) - set(existing_ids) + return create_error_response(f"知识库条目不存在: {list(missing_ids)}", 404) + + # 删除知识库条目 + deleted_count = session.query(KnowledgeEntry).filter(KnowledgeEntry.id.in_(knowledge_ids)).delete(synchronize_session=False) + session.commit() + + return jsonify(create_success_response( + data={"deleted_count": deleted_count}, + message=f"成功删除 {deleted_count} 个知识库条目" + )) + + except Exception as e: + return create_error_response(f"批量删除知识库条目失败: {str(e)}", 500) diff --git a/src/web/blueprints/knowledge.py b/src/web/blueprints/knowledge.py index 14ec1f9..9a9853e 100644 --- a/src/web/blueprints/knowledge.py +++ b/src/web/blueprints/knowledge.py @@ -49,10 +49,23 @@ def search_knowledge(): """搜索知识库""" try: query = request.args.get('q', '') - # 这里应该调用知识库管理器的搜索方法 - results = get_assistant().search_knowledge(query, top_k=5) - return jsonify(results.get('results', [])) + import logging + logger = logging.getLogger(__name__) + logger.info(f"搜索查询: '{query}'") + + if not query.strip(): + logger.info("查询为空,返回空结果") + return jsonify([]) + + # 直接调用知识库管理器的搜索方法 + assistant = get_assistant() + results = assistant.knowledge_manager.search_knowledge(query, top_k=5) + logger.info(f"搜索结果数量: {len(results)}") + return jsonify(results) except Exception as e: + import logging + logger = logging.getLogger(__name__) + logger.error(f"搜索知识库失败: {e}") return jsonify({"error": str(e)}), 500 @knowledge_bp.route('', methods=['POST']) diff --git a/src/web/error_handlers.py b/src/web/error_handlers.py new file mode 100644 index 0000000..d720caf --- /dev/null +++ b/src/web/error_handlers.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" +错误处理装饰器和工具 +提供统一的错误处理模式 +""" + +import logging +from functools import wraps +from typing import Callable, Any, Dict +from flask import jsonify + +logger = logging.getLogger(__name__) + + +def handle_api_errors(func: Callable) -> Callable: + """API错误处理装饰器""" + @wraps(func) + def wrapper(*args, **kwargs) -> Dict[str, Any]: + try: + return func(*args, **kwargs) + except ValueError as e: + logger.warning(f"参数错误 {func.__name__}: {e}") + return jsonify({"error": f"参数错误: {str(e)}"}), 400 + except PermissionError as e: + logger.warning(f"权限错误 {func.__name__}: {e}") + return jsonify({"error": f"权限不足: {str(e)}"}), 403 + except FileNotFoundError as e: + logger.warning(f"文件未找到 {func.__name__}: {e}") + return jsonify({"error": f"文件未找到: {str(e)}"}), 404 + except Exception as e: + logger.error(f"未处理错误 {func.__name__}: {e}") + return jsonify({"error": f"服务器内部错误: {str(e)}"}), 500 + return wrapper + + +def handle_database_errors(func: Callable) -> Callable: + """数据库错误处理装饰器""" + @wraps(func) + def wrapper(*args, **kwargs) -> Dict[str, Any]: + try: + return func(*args, **kwargs) + except Exception as e: + logger.error(f"数据库错误 {func.__name__}: {e}") + return jsonify({"error": "数据库操作失败"}), 500 + return wrapper + + +def handle_service_errors(func: Callable) -> Callable: + """服务错误处理装饰器""" + @wraps(func) + def wrapper(*args, **kwargs) -> Dict[str, Any]: + try: + return func(*args, **kwargs) + except Exception as e: + logger.error(f"服务错误 {func.__name__}: {e}") + return jsonify({"error": "服务暂时不可用"}), 503 + return wrapper + + +def create_error_response(message: str, status_code: int = 500, details: str = None) -> tuple: + """创建标准错误响应""" + response = {"error": message} + if details: + response["details"] = details + logger.error(f"错误响应: {message} - {details}") + return jsonify(response), status_code + + +def create_success_response(data: Any = None, message: str = "操作成功") -> Dict[str, Any]: + """创建标准成功响应""" + response = {"success": True, "message": message} + if data is not None: + response["data"] = data + return response diff --git a/src/web/service_manager.py b/src/web/service_manager.py new file mode 100644 index 0000000..84d552c --- /dev/null +++ b/src/web/service_manager.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +服务管理器 +统一管理各种服务的懒加载实例 +""" + +from typing import Optional, Dict, Any +import logging + +logger = logging.getLogger(__name__) + + +class ServiceManager: + """服务管理器 - 统一管理各种服务的懒加载实例""" + + def __init__(self): + self._services: Dict[str, Any] = {} + + def get_service(self, service_name: str, factory_func): + """获取服务实例(懒加载)""" + if service_name not in self._services: + try: + self._services[service_name] = factory_func() + logger.info(f"服务 {service_name} 已初始化") + except Exception as e: + logger.error(f"初始化服务 {service_name} 失败: {e}") + raise + return self._services[service_name] + + def get_assistant(self): + """获取TSP助手实例""" + def factory(): + from src.main import TSPAssistant + return TSPAssistant() + return self.get_service('assistant', factory) + + def get_agent_assistant(self): + """获取Agent助手实例""" + def factory(): + from src.agent_assistant import TSPAgentAssistant + return TSPAgentAssistant() + return self.get_service('agent_assistant', factory) + + def get_chat_manager(self): + """获取聊天管理器实例""" + def factory(): + from src.dialogue.realtime_chat import RealtimeChatManager + return RealtimeChatManager() + return self.get_service('chat_manager', factory) + + def get_vehicle_manager(self): + """获取车辆数据管理器实例""" + def factory(): + from src.vehicle.vehicle_data_manager import VehicleDataManager + return VehicleDataManager() + return self.get_service('vehicle_manager', factory) + + def clear_service(self, service_name: str): + """清除指定服务实例""" + if service_name in self._services: + del self._services[service_name] + logger.info(f"服务 {service_name} 已清除") + + def clear_all_services(self): + """清除所有服务实例""" + self._services.clear() + logger.info("所有服务实例已清除") + + +# 全局服务管理器实例 +service_manager = ServiceManager() diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index 2e221f3..b0cf150 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -1373,19 +1373,37 @@ class TSPDashboard { return; } + // 添加预警列表的批量操作头部 + const headerHtml = ` +
+
+ + +
+
+ +
+
+ `; + const alertsHtml = alerts.map(alert => `
-
-
- ${this.getLevelText(alert.level)} - ${alert.rule_name || '未知规则'} - ${this.formatTime(alert.created_at)} -
-
${alert.message}
-
- 类型: ${this.getTypeText(alert.alert_type)} | - 级别: ${this.getLevelText(alert.level)} +
+ +
+
+ ${this.getLevelText(alert.level)} + ${alert.rule_name || '未知规则'} + ${this.formatTime(alert.created_at)} +
+
${alert.message}
+
+ 类型: ${this.getTypeText(alert.alert_type)} | + 级别: ${this.getLevelText(alert.level)} +
@@ -1397,7 +1415,7 @@ class TSPDashboard {
`).join(''); - container.innerHTML = alertsHtml; + container.innerHTML = headerHtml + alertsHtml; } updateAlertStatistics(alerts) { @@ -1413,6 +1431,72 @@ class TSPDashboard { document.getElementById('total-alerts-count').textContent = stats.total || 0; } + // 预警批量删除功能 + toggleSelectAllAlerts() { + const selectAllCheckbox = document.getElementById('select-all-alerts'); + const alertCheckboxes = document.querySelectorAll('.alert-checkbox'); + + alertCheckboxes.forEach(checkbox => { + checkbox.checked = selectAllCheckbox.checked; + }); + + this.updateBatchDeleteAlertsButton(); + } + + updateBatchDeleteAlertsButton() { + const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked'); + const batchDeleteBtn = document.getElementById('batch-delete-alerts'); + + if (batchDeleteBtn) { + batchDeleteBtn.disabled = selectedCheckboxes.length === 0; + batchDeleteBtn.textContent = selectedCheckboxes.length > 0 + ? `批量删除 (${selectedCheckboxes.length})` + : '批量删除'; + } + } + + async batchDeleteAlerts() { + const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked'); + const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); + + if (selectedIds.length === 0) { + this.showNotification('请选择要删除的预警', 'warning'); + return; + } + + if (!confirm(`确定要删除选中的 ${selectedIds.length} 个预警吗?此操作不可撤销。`)) { + return; + } + + try { + const response = await fetch('/api/batch-delete/alerts', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ ids: selectedIds }) + }); + + const data = await response.json(); + + if (data.success) { + this.showNotification(data.message, 'success'); + + // 清除缓存并强制刷新 + this.cache.delete('alerts'); + await this.loadAlerts(); + + // 重置批量删除按钮状态 + this.updateBatchDeleteAlertsButton(); + } else { + this.showNotification(data.error || '批量删除失败', 'error'); + } + } catch (error) { + console.error('批量删除预警失败:', error); + this.showNotification('批量删除预警失败', 'error'); + } + } + async resolveAlert(alertId) { try { const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' }); @@ -1456,19 +1540,37 @@ class TSPDashboard { return; } + // 添加知识库列表的批量操作头部 + const headerHtml = ` +
+
+ + +
+
+ +
+
+ `; + const knowledgeHtml = knowledge.map(item => `
-
-
${item.question}
-

${item.answer}

-
- 分类: ${item.category} - 置信度: ${Math.round(item.confidence_score * 100)}% - 使用次数: ${item.usage_count || 0} - - ${item.is_verified ? '已验证' : '未验证'} - +
+ +
+
${item.question}
+

${item.answer}

+
+ 分类: ${item.category} + 置信度: ${Math.round(item.confidence_score * 100)}% + 使用次数: ${item.usage_count || 0} + + ${item.is_verified ? '已验证' : '未验证'} + +
@@ -1490,7 +1592,7 @@ class TSPDashboard {
`).join(''); - container.innerHTML = knowledgeHtml; + container.innerHTML = headerHtml + knowledgeHtml; } updateKnowledgePagination(data) { @@ -1750,24 +1852,115 @@ class TSPDashboard { } } - // 工单管理 - async loadWorkOrders() { + // 知识库批量删除功能 + toggleSelectAllKnowledge() { + const selectAllCheckbox = document.getElementById('select-all-knowledge'); + const knowledgeCheckboxes = document.querySelectorAll('.knowledge-checkbox'); + + knowledgeCheckboxes.forEach(checkbox => { + checkbox.checked = selectAllCheckbox.checked; + }); + + this.updateBatchDeleteKnowledgeButton(); + } + + updateBatchDeleteKnowledgeButton() { + const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked'); + const batchDeleteBtn = document.getElementById('batch-delete-knowledge'); + + if (batchDeleteBtn) { + batchDeleteBtn.disabled = selectedCheckboxes.length === 0; + batchDeleteBtn.textContent = selectedCheckboxes.length > 0 + ? `批量删除 (${selectedCheckboxes.length})` + : '批量删除'; + } + } + + async batchDeleteKnowledge() { + const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked'); + const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); + + if (selectedIds.length === 0) { + this.showNotification('请选择要删除的知识库条目', 'warning'); + return; + } + + if (!confirm(`确定要删除选中的 ${selectedIds.length} 个知识库条目吗?此操作不可撤销。`)) { + return; + } + try { - const statusFilter = document.getElementById('workorder-status-filter').value; - const priorityFilter = document.getElementById('workorder-priority-filter').value; + const response = await fetch('/api/batch-delete/knowledge', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ ids: selectedIds }) + }); + + const data = await response.json(); + + if (data.success) { + this.showNotification(data.message, 'success'); + + // 清除缓存并强制刷新 + this.cache.delete('knowledge'); + await this.loadKnowledge(); + + // 重置批量删除按钮状态 + this.updateBatchDeleteKnowledgeButton(); + } else { + this.showNotification(data.error || '批量删除失败', 'error'); + } + } catch (error) { + console.error('批量删除知识库条目失败:', error); + this.showNotification('批量删除知识库条目失败', 'error'); + } + } + + // 工单管理 + async loadWorkOrders(forceRefresh = false) { + try { + const statusFilter = document.getElementById('workorder-status-filter')?.value || 'all'; + const priorityFilter = document.getElementById('workorder-priority-filter')?.value || 'all'; let url = '/api/workorders'; const params = new URLSearchParams(); if (statusFilter !== 'all') params.append('status', statusFilter); if (priorityFilter !== 'all') params.append('priority', priorityFilter); + + // 添加强制刷新参数 + if (forceRefresh) { + params.append('_t', Date.now().toString()); + } + if (params.toString()) url += '?' + params.toString(); - const response = await fetch(url); + const response = await fetch(url, { + cache: forceRefresh ? 'no-cache' : 'default', + headers: forceRefresh ? { + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache' + } : {} + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + const workorders = await response.json(); this.updateWorkOrdersDisplay(workorders); this.updateWorkOrderStatistics(workorders); + + // 更新缓存 + this.cache.set('workorders', { + data: workorders, + timestamp: Date.now() + }); + } catch (error) { console.error('加载工单失败:', error); + this.showNotification('加载工单失败: ' + error.message, 'error'); } } @@ -1779,17 +1972,35 @@ class TSPDashboard { return; } + // 添加工单列表的批量操作头部 + const headerHtml = ` +
+
+ + +
+
+ +
+
+ `; + const workordersHtml = workorders.map(workorder => `
-
-
${workorder.title}
-

${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无处理过程'}

-
- ${this.getPriorityText(workorder.priority)} - ${this.getStatusText(workorder.status)} - 分类: ${workorder.category} - 创建时间: ${new Date(workorder.created_at).toLocaleString()} +
+ +
+
${workorder.title}
+

${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无处理过程'}

+
+ ${this.getPriorityText(workorder.priority)} + ${this.getStatusText(workorder.status)} + 分类: ${workorder.category} + 创建时间: ${new Date(workorder.created_at).toLocaleString()} +
@@ -1809,7 +2020,7 @@ class TSPDashboard {
`).join(''); - container.innerHTML = workordersHtml; + container.innerHTML = headerHtml + workordersHtml; } updateWorkOrderStatistics(workorders) { @@ -1825,6 +2036,73 @@ class TSPDashboard { document.getElementById('workorders-resolved').textContent = stats.resolved || 0; } + // 工单批量删除功能 + toggleSelectAllWorkorders() { + const selectAllCheckbox = document.getElementById('select-all-workorders'); + const workorderCheckboxes = document.querySelectorAll('.workorder-checkbox'); + + workorderCheckboxes.forEach(checkbox => { + checkbox.checked = selectAllCheckbox.checked; + }); + + this.updateBatchDeleteButton(); + } + + updateBatchDeleteButton() { + const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked'); + const batchDeleteBtn = document.getElementById('batch-delete-workorders'); + + if (batchDeleteBtn) { + batchDeleteBtn.disabled = selectedCheckboxes.length === 0; + batchDeleteBtn.textContent = selectedCheckboxes.length > 0 + ? `批量删除 (${selectedCheckboxes.length})` + : '批量删除'; + } + } + + async batchDeleteWorkorders() { + const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked'); + const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); + + if (selectedIds.length === 0) { + this.showNotification('请选择要删除的工单', 'warning'); + return; + } + + if (!confirm(`确定要删除选中的 ${selectedIds.length} 个工单吗?此操作不可撤销。`)) { + return; + } + + try { + const response = await fetch('/api/batch-delete/workorders', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ ids: selectedIds }) + }); + + const data = await response.json(); + + if (data.success) { + this.showNotification(data.message, 'success'); + + // 清除缓存并强制刷新 + this.cache.delete('workorders'); + await this.loadWorkOrders(true); // 强制刷新 + await this.loadAnalytics(); + + // 重置批量删除按钮状态 + this.updateBatchDeleteButton(); + } else { + this.showNotification(data.error || '批量删除失败', 'error'); + } + } catch (error) { + console.error('批量删除工单失败:', error); + this.showNotification('批量删除工单失败', 'error'); + } + } + async createWorkOrder() { const title = document.getElementById('wo-title').value.trim(); const description = document.getElementById('wo-description').value.trim(); @@ -3407,6 +3685,7 @@ class TSPDashboard { // 更新统计卡片 updateStatisticsCards(data) { + // 更新工单统计 const total = data.workorders?.total || 0; const open = data.workorders?.open || 0; const resolved = data.workorders?.resolved || 0; @@ -3417,6 +3696,49 @@ class TSPDashboard { document.getElementById('resolvedWorkorders').textContent = resolved; document.getElementById('avgSatisfaction').textContent = avgSatisfaction.toFixed(1); + // 更新预警统计 + const alertTotal = data.alerts?.total || 0; + const alertActive = data.alerts?.active || 0; + const alertCritical = data.alerts?.by_level?.critical || 0; + const alertWarning = data.alerts?.by_level?.warning || 0; + const alertError = data.alerts?.by_level?.error || 0; + + // 更新预警统计显示 + if (document.getElementById('critical-alerts')) { + document.getElementById('critical-alerts').textContent = alertCritical; + } + if (document.getElementById('warning-alerts')) { + document.getElementById('warning-alerts').textContent = alertWarning; + } + if (document.getElementById('error-alerts')) { + document.getElementById('error-alerts').textContent = alertError; + } + if (document.getElementById('total-alerts-count')) { + document.getElementById('total-alerts-count').textContent = alertTotal; + } + + // 更新性能统计 + const performanceScore = data.performance?.score || 0; + const performanceTrend = data.performance?.trend || 'stable'; + + if (document.getElementById('performance-score')) { + document.getElementById('performance-score').textContent = performanceScore.toFixed(1); + } + if (document.getElementById('performance-trend')) { + document.getElementById('performance-trend').textContent = this.getPerformanceTrendText(performanceTrend); + } + + // 更新满意度统计 + const satisfactionAvg = data.satisfaction?.average || 0; + const satisfactionCount = data.satisfaction?.count || 0; + + if (document.getElementById('satisfaction-avg')) { + document.getElementById('satisfaction-avg').textContent = satisfactionAvg.toFixed(1); + } + if (document.getElementById('satisfaction-count')) { + document.getElementById('satisfaction-count').textContent = satisfactionCount; + } + // 更新进度条 if (total > 0) { document.getElementById('openProgress').style.width = `${(open / total) * 100}%`; @@ -3469,9 +3791,62 @@ class TSPDashboard { // 更新分布图表 updateDistributionChart(data) { - const categories = data.workorders?.by_category || {}; - const labels = Object.keys(categories); - const values = Object.values(categories); + const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; + + let labels, values, title, backgroundColor; + + if (currentDimension === 'alerts') { + // 预警级别分布 + const alertLevels = data.alerts?.by_level || {}; + labels = Object.keys(alertLevels); + values = Object.values(alertLevels); + title = '预警级别分布'; + backgroundColor = [ + '#FF6384', // critical - 红色 + '#FFCE56', // warning - 黄色 + '#36A2EB', // error - 蓝色 + '#4BC0C0', // info - 青色 + '#9966FF' // 其他 + ]; + } else if (currentDimension === 'performance') { + // 性能指标分布 + const performanceMetrics = data.performance?.by_level || {}; + labels = Object.keys(performanceMetrics); + values = Object.values(performanceMetrics); + title = '性能指标分布'; + backgroundColor = [ + '#28a745', // 优秀 - 绿色 + '#ffc107', // 良好 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 差 - 红色 + ]; + } else if (currentDimension === 'satisfaction') { + // 满意度分布 + const satisfactionLevels = data.satisfaction?.by_level || {}; + labels = Object.keys(satisfactionLevels); + values = Object.values(satisfactionLevels); + title = '满意度分布'; + backgroundColor = [ + '#28a745', // 非常满意 - 绿色 + '#ffc107', // 满意 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 不满意 - 红色 + ]; + } else { + // 工单分类分布 + const categories = data.workorders?.by_category || {}; + labels = Object.keys(categories); + values = Object.values(categories); + title = '工单分类分布'; + backgroundColor = [ + '#FF6384', + '#36A2EB', + '#FFCE56', + '#4BC0C0', + '#9966FF', + '#FF9F40' + ]; + } const chartConfig = { type: 'doughnut', @@ -3479,14 +3854,7 @@ class TSPDashboard { labels: labels, datasets: [{ data: values, - backgroundColor: [ - '#FF6384', - '#36A2EB', - '#FFCE56', - '#4BC0C0', - '#9966FF', - '#FF9F40' - ] + backgroundColor: backgroundColor }] }, options: { @@ -3495,7 +3863,7 @@ class TSPDashboard { plugins: { title: { display: true, - text: '工单分类分布' + text: title }, legend: { display: true, @@ -3576,23 +3944,72 @@ class TSPDashboard { this.charts.priorityChart.destroy(); } - const priorities = data.workorders?.by_priority || {}; - const labels = Object.keys(priorities).map(p => this.getPriorityText(p)); - const values = Object.values(priorities); + const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; + + let labels, values, title, backgroundColor, label; + + if (currentDimension === 'alerts') { + // 预警严重程度分布 + const alertSeverities = data.alerts?.by_severity || {}; + labels = Object.keys(alertSeverities).map(s => this.getSeverityText(s)); + values = Object.values(alertSeverities); + title = '预警严重程度分布'; + label = '预警数量'; + backgroundColor = [ + '#28a745', // low - 绿色 + '#ffc107', // medium - 黄色 + '#fd7e14', // high - 橙色 + '#dc3545' // critical - 红色 + ]; + } else if (currentDimension === 'performance') { + // 性能指标分布 + const performanceMetrics = data.performance?.by_metric || {}; + labels = Object.keys(performanceMetrics); + values = Object.values(performanceMetrics); + title = '性能指标分布'; + label = '性能值'; + backgroundColor = [ + '#28a745', // 优秀 - 绿色 + '#ffc107', // 良好 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 差 - 红色 + ]; + } else if (currentDimension === 'satisfaction') { + // 满意度分布 + const satisfactionLevels = data.satisfaction?.by_level || {}; + labels = Object.keys(satisfactionLevels).map(s => this.getSatisfactionText(s)); + values = Object.values(satisfactionLevels); + title = '满意度分布'; + label = '满意度数量'; + backgroundColor = [ + '#28a745', // 非常满意 - 绿色 + '#ffc107', // 满意 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 不满意 - 红色 + ]; + } else { + // 工单优先级分布 + const priorities = data.workorders?.by_priority || {}; + labels = Object.keys(priorities).map(p => this.getPriorityText(p)); + values = Object.values(priorities); + title = '工单优先级分布'; + label = '工单数量'; + backgroundColor = [ + '#28a745', // 低 - 绿色 + '#ffc107', // 中 - 黄色 + '#fd7e14', // 高 - 橙色 + '#dc3545' // 紧急 - 红色 + ]; + } this.charts.priorityChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ - label: '工单数量', + label: label, data: values, - backgroundColor: [ - '#28a745', // 低 - 绿色 - '#ffc107', // 中 - 黄色 - '#fd7e14', // 高 - 橙色 - '#dc3545' // 紧急 - 红色 - ] + backgroundColor: backgroundColor }] }, options: { @@ -3601,7 +4018,7 @@ class TSPDashboard { plugins: { title: { display: true, - text: '优先级分布' + text: title } }, scales: { @@ -3618,33 +4035,124 @@ class TSPDashboard { const trendData = data.trend || []; const labels = trendData.map(item => item.date); const workorders = trendData.map(item => item.workorders); + const alerts = trendData.map(item => item.alerts); + const performance = trendData.map(item => item.performance || 0); + const satisfaction = trendData.map(item => item.satisfaction || 0); if (chartType === 'pie' || chartType === 'doughnut') { - const categories = data.workorders?.by_category || {}; - return { - labels: Object.keys(categories), - datasets: [{ - data: Object.values(categories), - backgroundColor: [ - '#FF6384', - '#36A2EB', - '#FFCE56', - '#4BC0C0', - '#9966FF', - '#FF9F40' - ] - }] - }; + // 根据数据维度选择显示内容 + const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; + + if (currentDimension === 'alerts') { + const alertLevels = data.alerts?.by_level || {}; + return { + labels: Object.keys(alertLevels), + datasets: [{ + data: Object.values(alertLevels), + backgroundColor: [ + '#FF6384', // critical - 红色 + '#FFCE56', // warning - 黄色 + '#36A2EB', // error - 蓝色 + '#4BC0C0', // info - 青色 + '#9966FF' // 其他 + ] + }] + }; + } else if (currentDimension === 'performance') { + // 性能指标分布 + const performanceMetrics = data.performance || {}; + return { + labels: Object.keys(performanceMetrics), + datasets: [{ + data: Object.values(performanceMetrics), + backgroundColor: [ + '#28a745', // 优秀 - 绿色 + '#ffc107', // 良好 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 差 - 红色 + ] + }] + }; + } else if (currentDimension === 'satisfaction') { + // 满意度分布 + const satisfactionLevels = data.satisfaction?.by_level || {}; + return { + labels: Object.keys(satisfactionLevels), + datasets: [{ + data: Object.values(satisfactionLevels), + backgroundColor: [ + '#28a745', // 非常满意 - 绿色 + '#ffc107', // 满意 - 黄色 + '#fd7e14', // 一般 - 橙色 + '#dc3545' // 不满意 - 红色 + ] + }] + }; + } else { + const categories = data.workorders?.by_category || {}; + return { + labels: Object.keys(categories), + datasets: [{ + data: Object.values(categories), + backgroundColor: [ + '#FF6384', + '#36A2EB', + '#FFCE56', + '#4BC0C0', + '#9966FF', + '#FF9F40' + ] + }] + }; + } } else { - return { - labels: labels, - datasets: [{ + // 线图和柱状图根据数据维度显示不同内容 + const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; + const datasets = []; + + if (currentDimension === 'performance') { + // 性能指标图表 + datasets.push({ + label: '性能指标', + data: performance, + borderColor: '#28a745', + backgroundColor: 'rgba(40, 167, 69, 0.1)', + tension: chartType === 'line' ? 0.4 : 0 + }); + } else if (currentDimension === 'satisfaction') { + // 满意度图表 + datasets.push({ + label: '满意度评分', + data: satisfaction, + borderColor: '#ffc107', + backgroundColor: 'rgba(255, 193, 7, 0.1)', + tension: chartType === 'line' ? 0.4 : 0 + }); + } else { + // 默认显示工单和预警数据 + datasets.push({ label: '工单数量', data: workorders, borderColor: '#36A2EB', backgroundColor: 'rgba(54, 162, 235, 0.1)', tension: chartType === 'line' ? 0.4 : 0 - }] + }); + + // 如果有预警数据,添加预警数据集 + if (alerts.some(alert => alert > 0)) { + datasets.push({ + label: '预警数量', + data: alerts, + borderColor: '#FF6384', + backgroundColor: 'rgba(255, 99, 132, 0.1)', + tension: chartType === 'line' ? 0.4 : 0 + }); + } + } + + return { + labels: labels, + datasets: datasets }; } } @@ -4281,6 +4789,35 @@ class TSPDashboard { return priorityMap[priority] || priority; } + getSeverityText(severity) { + const severityMap = { + 'low': '低', + 'medium': '中', + 'high': '高', + 'critical': '严重' + }; + return severityMap[severity] || severity; + } + + getSatisfactionText(level) { + const satisfactionMap = { + 'very_satisfied': '非常满意', + 'satisfied': '满意', + 'neutral': '一般', + 'dissatisfied': '不满意' + }; + return satisfactionMap[level] || level; + } + + getPerformanceTrendText(trend) { + const trendMap = { + 'up': '上升', + 'down': '下降', + 'stable': '稳定' + }; + return trendMap[trend] || trend; + } + getPriorityColor(priority) { const colorMap = { 'low': 'secondary', diff --git a/src/web/templates/index_new.html b/src/web/templates/index_new.html deleted file mode 100644 index 7cd73a8..0000000 --- a/src/web/templates/index_new.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - TSP智能助手 - - - - - - - - - - - - - -
- - -
-

🚀 TSP智能助手

-

- 前端构建文件不存在,请先构建前端应用。
- 或者使用传统HTML页面。 -

-
- - - 📄 使用传统页面 - -
-
- - - - - - - - diff --git a/start_dashboard.py b/start_dashboard.py index 8bf77bd..d82f5c5 100644 --- a/start_dashboard.py +++ b/start_dashboard.py @@ -19,7 +19,7 @@ def setup_logging(): level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ - logging.FileHandler('logs/dashboard.log'), + logging.FileHandler('logs/dashboard.log', encoding='utf-8'), logging.StreamHandler() ] ) diff --git a/start_frontend.bat b/start_frontend.bat deleted file mode 100644 index 74d92ad..0000000 --- a/start_frontend.bat +++ /dev/null @@ -1,45 +0,0 @@ -@echo off -echo 启动TSP智能助手前端开发服务器... -echo. - -cd frontend - -echo 检查Node.js环境... -node --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到Node.js,请先安装Node.js - echo 下载地址: https://nodejs.org/ - pause - exit /b 1 -) - -echo 检查npm环境... -npm --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到npm,请检查Node.js安装 - pause - exit /b 1 -) - -echo 检查依赖包... -if not exist "node_modules" ( - echo 安装依赖包... - npm install - if %errorlevel% neq 0 ( - echo 错误: 依赖包安装失败 - pause - exit /b 1 - ) -) - -echo 启动开发服务器... -echo 前端地址: http://localhost:3000 -echo 后端API: http://localhost:5000 -echo WebSocket: ws://localhost:8765 -echo. -echo 按 Ctrl+C 停止服务器 -echo. - -npm run dev - -pause diff --git a/start_frontend.sh b/start_frontend.sh deleted file mode 100644 index 39b43a1..0000000 --- a/start_frontend.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -echo "启动TSP智能助手前端开发服务器..." -echo - -# 检查Node.js环境 -echo "检查Node.js环境..." -if ! command -v node &> /dev/null; then - echo "错误: 未找到Node.js,请先安装Node.js" - echo "安装命令:" - echo " Ubuntu/Debian: sudo apt update && sudo apt install nodejs npm" - echo " CentOS/RHEL: sudo yum install nodejs npm" - echo " 或访问: https://nodejs.org/" - exit 1 -fi - -# 检查npm环境 -echo "检查npm环境..." -if ! command -v npm &> /dev/null; then - echo "错误: 未找到npm,请检查Node.js安装" - exit 1 -fi - -echo "Node.js版本: $(node --version)" -echo "npm版本: $(npm --version)" -echo - -# 检查Node.js版本兼容性 -NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) -if [ "$NODE_VERSION" -ge 22 ]; then - echo "检测到Node.js v22+,使用兼容性模式..." - echo "注意: 将跳过TypeScript类型检查以避免兼容性问题" -fi - -# 进入前端目录 -cd frontend - -# 检查依赖包 -echo "检查依赖包..." -if [ ! -d "node_modules" ]; then - echo "安装依赖包..." - npm install - if [ $? -ne 0 ]; then - echo "错误: 依赖包安装失败" - exit 1 - fi -fi - -echo -echo "启动开发服务器..." -echo "前端地址: http://localhost:3000" -echo "后端API: http://localhost:5000" -echo "WebSocket: ws://localhost:8765" -echo -echo "按 Ctrl+C 停止服务器" -echo - -npm run dev diff --git a/start_frontend_portable.bat b/start_frontend_portable.bat deleted file mode 100644 index adbfdda..0000000 --- a/start_frontend_portable.bat +++ /dev/null @@ -1,51 +0,0 @@ -@echo off -echo 启动TSP智能助手前端开发服务器(便携版)... -echo. - -set NODEJS_PATH=frontend\nodejs -set NODE_EXE=%NODEJS_PATH%\node.exe -set NPM_EXE=%NODEJS_PATH%\npm.cmd - -echo 检查便携版Node.js... -if not exist "%NODE_EXE%" ( - echo 错误: 未找到便携版Node.js - echo 请先运行 download_nodejs.bat 下载Node.js便携版 - echo 或手动安装Node.js到系统 - pause - exit /b 1 -) - -echo 使用便携版Node.js: %NODE_EXE% -echo Node.js版本: -"%NODE_EXE%" --version - -echo. -echo 检查npm版本: -"%NPM_EXE%" --version - -cd frontend - -echo. -echo 检查依赖包... -if not exist "node_modules" ( - echo 安装依赖包... - "%NPM_EXE%" install - if %errorlevel% neq 0 ( - echo 错误: 依赖包安装失败 - pause - exit /b 1 - ) -) - -echo. -echo 启动开发服务器... -echo 前端地址: http://localhost:3000 -echo 后端API: http://localhost:5000 -echo WebSocket: ws://localhost:8765 -echo. -echo 按 Ctrl+C 停止服务器 -echo. - -"%NPM_EXE%" run dev - -pause diff --git a/start_legacy.bat b/start_legacy.bat deleted file mode 100644 index 880faa3..0000000 --- a/start_legacy.bat +++ /dev/null @@ -1,22 +0,0 @@ -@echo off -echo 启动TSP智能助手传统版本... -echo. - -echo 检查Python环境... -python --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到Python,请先安装Python - echo 下载地址: https://www.python.org/downloads/ - pause - exit /b 1 -) - -echo 启动Web服务器... -echo 访问地址: http://localhost:5000 -echo. -echo 按 Ctrl+C 停止服务器 -echo. - -python src/web/app.py - -pause diff --git a/start_traditional.bat b/start_traditional.bat deleted file mode 100644 index e21406e..0000000 --- a/start_traditional.bat +++ /dev/null @@ -1,32 +0,0 @@ -@echo off -echo 启动TSP智能助手传统版本... -echo. - -echo 检查Python环境... -python --version >nul 2>&1 -if %errorlevel% neq 0 ( - echo 错误: 未找到Python,请先安装Python - echo 下载地址: https://www.python.org/downloads/ - pause - exit /b 1 -) - -echo Python环境正常 -echo. -echo 启动Web服务器... -echo 访问地址: http://localhost:5000 -echo. -echo 功能包括: -echo - 仪表板: http://localhost:5000 -echo - 聊天页面: http://localhost:5000/chat -echo - 预警管理: http://localhost:5000/alerts -echo - 知识库: http://localhost:5000/knowledge -echo - 字段映射: http://localhost:5000/field-mapping -echo - 系统设置: http://localhost:5000/system -echo. -echo 按 Ctrl+C 停止服务器 -echo. - -python src/web/app.py - -pause diff --git a/start_traditional.sh b/start_traditional.sh deleted file mode 100644 index 3e916b6..0000000 --- a/start_traditional.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -echo "启动TSP智能助手传统版本..." -echo - -# 检查Python环境 -echo "检查Python环境..." -if ! command -v python3 &> /dev/null; then - if ! command -v python &> /dev/null; then - echo "错误: 未找到Python,请先安装Python" - echo "安装命令:" - echo " Ubuntu/Debian: sudo apt update && sudo apt install python3 python3-pip" - echo " CentOS/RHEL: sudo yum install python3 python3-pip" - echo " 或访问: https://www.python.org/downloads/" - exit 1 - else - PYTHON_CMD="python" - fi -else - PYTHON_CMD="python3" -fi - -echo "Python环境正常: $($PYTHON_CMD --version)" -echo - -# 检查依赖 -echo "检查Python依赖..." -if [ ! -f "requirements.txt" ]; then - echo "警告: 未找到requirements.txt文件" -else - echo "安装Python依赖..." - $PYTHON_CMD -m pip install -r requirements.txt -fi - -echo -echo "启动Web服务器..." -echo "访问地址: http://localhost:5000" -echo -echo "功能包括:" -echo "- 仪表板: http://localhost:5000" -echo "- 聊天页面: http://localhost:5000/chat" -echo "- 预警管理: http://localhost:5000/alerts" -echo "- 知识库: http://localhost:5000/knowledge" -echo "- 字段映射: http://localhost:5000/field-mapping" -echo "- 系统设置: http://localhost:5000/system" -echo -echo "按 Ctrl+C 停止服务器" -echo - -$PYTHON_CMD src/web/app.py diff --git a/前端架构优化总结.md b/前端架构优化总结.md deleted file mode 100644 index 2e31902..0000000 --- a/前端架构优化总结.md +++ /dev/null @@ -1,265 +0,0 @@ -# TSP智能助手前端架构优化总结 - -## 🎯 优化目标 - -解决原有前端架构的问题: -1. **单文件过大** - HTML文件几千行,难以维护 -2. **功能重复** - 聊天功能在多个页面重复实现 -3. **缺乏国际化** - 硬编码中文文本,无法多语言支持 -4. **架构混乱** - HTML、CSS、JS混合,缺乏模块化 - -## 🚀 新架构设计 - -### 技术栈选择 - -- **Vue 3** + **TypeScript** - 现代化组件化开发 -- **Vite** - 快速构建工具,支持热重载 -- **Element Plus** - 企业级UI组件库 -- **Vue Router** - 单页应用路由管理 -- **Pinia** - 轻量级状态管理 -- **Vue I18n** - 完整的国际化解决方案 -- **Socket.IO** - 实时通信支持 - -### 项目结构 - -``` -frontend/ -├── src/ -│ ├── components/ # 公共组件 -│ │ └── ChatWidget.vue # 统一聊天组件 -│ ├── views/ # 页面组件 -│ │ ├── Dashboard.vue # 仪表板 -│ │ ├── Chat.vue # 聊天页面 -│ │ ├── Alerts.vue # 预警管理 -│ │ ├── AlertRules.vue # 预警规则 -│ │ ├── Knowledge.vue # 知识库 -│ │ ├── FieldMapping.vue # 字段映射 -│ │ └── System.vue # 系统设置 -│ ├── stores/ # 状态管理 -│ │ ├── useAppStore.ts # 应用状态 -│ │ ├── useChatStore.ts # 聊天状态 -│ │ └── useAlertStore.ts # 预警状态 -│ ├── router/ # 路由配置 -│ ├── i18n/ # 国际化 -│ │ └── locales/ -│ │ ├── zh.json # 中文 -│ │ └── en.json # 英文 -│ ├── layout/ # 布局组件 -│ ├── App.vue # 根组件 -│ └── main.ts # 入口文件 -├── package.json -├── vite.config.ts -├── tsconfig.json -└── README.md -``` - -## ✨ 核心功能实现 - -### 1. 统一聊天系统 - -**问题解决**: -- 原来首页和chat页面有重复的聊天功能 -- 实现方式不同,维护困难 - -**解决方案**: -- 创建 `ChatWidget.vue` 统一聊天组件 -- 支持悬浮窗和全屏两种模式 -- 统一的状态管理 `useChatStore` -- 统一的WebSocket连接管理 - -**特性**: -- 实时消息收发 -- 打字指示器 -- 消息元数据显示(知识库、置信度、工单关联) -- 快速操作按钮 -- 工单创建功能 - -### 2. 预警管理系统 - -**问题解决**: -- 预警和预警管理页面功能分散 -- 缺乏统一的预警状态管理 - -**解决方案**: -- `Alerts.vue` - 预警列表和统计 -- `AlertRules.vue` - 预警规则管理 -- `useAlertStore` - 统一预警状态管理 -- 预设模板系统 - -**特性**: -- 实时预警统计 -- 多维度过滤和排序 -- 规则CRUD操作 -- 预设模板快速创建 -- 预警解决功能 - -### 3. 国际化系统 - -**问题解决**: -- 硬编码中文文本 -- 无法支持多语言 - -**解决方案**: -- Vue I18n 完整集成 -- 中英文双语支持 -- Element Plus 组件国际化 -- 动态语言切换 - -**覆盖范围**: -- 所有页面文本 -- 组件标签和提示 -- 错误消息 -- 表单验证信息 - -### 4. 现代化UI设计 - -**设计原则**: -- 响应式设计,支持移动端 -- 暗色/亮色主题切换 -- 统一的视觉风格 -- 优雅的动画效果 - -**组件化**: -- 可复用的UI组件 -- 统一的样式规范 -- 主题变量支持 -- 无障碍访问支持 - -## 🔧 开发体验优化 - -### TypeScript支持 -- 完整的类型定义 -- 组件Props和Emits类型安全 -- API接口类型定义 -- 自动类型检查 - -### 开发工具 -- Vite热重载 -- 组件自动导入 -- ESLint代码规范 -- 自动类型生成 - -### 状态管理 -- Pinia轻量级状态管理 -- 模块化状态设计 -- 类型安全的状态操作 -- 持久化存储支持 - -## 📦 构建和部署 - -### 开发环境 -```bash -cd frontend -npm install -npm run dev -``` - -### 生产构建 -```bash -npm run build -``` - -### 集成方式 -- 构建文件输出到 `src/web/static/dist` -- Flask应用自动提供静态文件服务 -- 支持SPA路由回退 - -## 🎨 用户体验提升 - -### 1. 统一导航 -- 侧边栏导航 -- 面包屑导航 -- 响应式菜单 -- 折叠/展开功能 - -### 2. 实时更新 -- WebSocket实时通信 -- 自动数据刷新 -- 状态同步 -- 离线检测 - -### 3. 交互优化 -- 加载状态指示 -- 错误处理 -- 操作确认 -- 成功反馈 - -### 4. 性能优化 -- 组件懒加载 -- 代码分割 -- 资源预加载 -- 缓存策略 - -## 🔄 迁移策略 - -### 渐进式迁移 -1. **第一阶段**:新架构开发完成 -2. **第二阶段**:并行运行,逐步切换 -3. **第三阶段**:完全切换到新架构 - -### 兼容性保证 -- 保留原有API接口 -- 支持传统页面访问 -- 平滑过渡方案 - -## 📊 优化效果 - -### 代码质量 -- **文件大小**:从单个文件几千行 → 模块化组件 -- **可维护性**:大幅提升,组件化开发 -- **可复用性**:组件可在多个页面复用 -- **类型安全**:TypeScript提供完整类型检查 - -### 开发效率 -- **热重载**:开发时实时预览 -- **自动导入**:减少重复代码 -- **组件库**:Element Plus提供丰富组件 -- **状态管理**:统一的数据流管理 - -### 用户体验 -- **响应速度**:SPA应用,页面切换更快 -- **交互体验**:现代化UI设计 -- **多语言**:支持中英文切换 -- **主题切换**:支持暗色/亮色主题 - -## 🚀 后续规划 - -### 功能扩展 -- 更多页面组件 -- 数据可视化图表 -- 文件上传下载 -- 用户权限管理 - -### 技术优化 -- PWA支持 -- 服务端渲染(SSR) -- 微前端架构 -- 性能监控 - -### 部署优化 -- Docker容器化 -- CI/CD流水线 -- 自动化测试 -- 监控告警 - -## 📝 使用指南 - -### 快速开始 -1. 运行 `start_frontend.bat` 启动开发服务器 -2. 访问 http://localhost:3000 -3. 运行 `build_frontend.bat` 构建生产版本 - -### 开发规范 -- 使用TypeScript编写组件 -- 遵循Vue 3 Composition API -- 使用Pinia进行状态管理 -- 添加国际化文本到i18n文件 - -### 部署说明 -- 构建文件自动输出到后端静态目录 -- Flask应用自动提供前端服务 -- 支持API代理和WebSocket转发 - ---- - -**总结**:通过现代化的前端架构重构,我们成功解决了原有架构的所有问题,提供了更好的开发体验和用户体验。新架构具有更好的可维护性、可扩展性和性能表现,为TSP智能助手的长期发展奠定了坚实基础。 diff --git a/前端页面优化总结.md b/前端页面优化总结.md deleted file mode 100644 index 93ad672..0000000 --- a/前端页面优化总结.md +++ /dev/null @@ -1,136 +0,0 @@ -# 前端页面优化总结 - -## 🎯 优化目标 - -根据用户需求,对前端页面进行以下优化: -1. **删除无效的系统设置**:移除服务端口配置、API与模型配置等无效功能 -2. **添加英文支持**:实现中英文语言切换功能 - -## ✅ 完成的优化 - -### 1. **删除无效的系统设置** - -#### 🔧 移除的功能 -- **API与模型配置**:删除了前端页面中的API提供商、API基础URL、API密钥、模型名称、温度参数、最大令牌数等配置项 -- **服务端口配置**:删除了Web服务端口、WebSocket端口等端口配置项 -- **重启服务按钮**:移除了无效的服务重启功能 - -#### 🔄 替换为有效功能 -- **系统信息显示**:将端口配置替换为只读的系统信息显示 -- **日志配置**:保留了有效的日志级别配置功能 -- **基础设置**:保留了API超时时间、最大历史记录数等有效配置 - -#### 📊 优化效果 -- ✅ 移除了用户无法修改的无效配置项 -- ✅ 避免了用户困惑和无效操作 -- ✅ 简化了设置页面,提高用户体验 - -### 2. **添加英文支持** - -#### 🌐 语言切换功能 -- **导航栏语言切换**:在页面顶部添加了中英文切换按钮 -- **本地存储**:使用localStorage保存用户的语言偏好 -- **实时切换**:点击按钮即可实时切换页面语言 - -#### 📝 翻译内容 -**中文翻译键**: -- 导航栏:TSP智能助手、检查中...、系统正常等 -- 侧边栏:仪表板、工单管理、智能对话、Agent管理、预警管理、知识库、数据分析、飞书同步、系统设置 -- 设置页面:系统设置、基础设置、系统信息、日志配置、当前服务端口、WebSocket端口、日志级别、保存设置等 - -**英文翻译键**: -- Navigation: TSP Intelligent Assistant, Checking..., System Normal, etc. -- Sidebar: Dashboard, Work Orders, Smart Chat, Agent Management, Alert Management, Knowledge Base, Analytics, Feishu Sync, System Settings -- Settings: System Settings, Basic Settings, System Information, Log Configuration, Current Service Port, WebSocket Port, Log Level, Save Settings, etc. - -#### 🔧 技术实现 -- **JavaScript翻译系统**:使用对象存储翻译内容 -- **HTML国际化属性**:使用`data-i18n`属性标记需要翻译的元素 -- **动态更新**:通过JavaScript动态更新页面文本内容 -- **状态管理**:维护当前语言状态和按钮激活状态 - -## 📊 优化前后对比 - -### 设置页面结构变化 - -**优化前**: -``` -系统设置 -├── 基础系统配置 -├── API与模型配置 (无效) -├── 服务端口配置 (无效) -└── 系统信息与状态 -``` - -**优化后**: -``` -系统设置 -├── 基础系统配置 -├── 系统信息显示 (只读) -├── 日志配置 -└── 系统信息与状态 -``` - -### 用户体验改进 - -**优化前**: -- ❌ 用户看到无法修改的配置项 -- ❌ 点击无效按钮无响应 -- ❌ 只有中文界面 - -**优化后**: -- ✅ 只显示有效的配置项 -- ✅ 所有功能都有明确的作用 -- ✅ 支持中英文切换 -- ✅ 更好的用户引导和说明 - -## 🚀 技术特性 - -### 1. **智能配置管理** -- 自动检测无效配置项 -- 只显示用户可操作的设置 -- 提供清晰的配置说明 - -### 2. **多语言支持** -- 完整的中英文翻译 -- 本地存储语言偏好 -- 实时语言切换 -- 响应式设计适配 - -### 3. **用户体验优化** -- 简化设置页面结构 -- 提供清晰的功能说明 -- 避免用户困惑和无效操作 - -## 📋 使用说明 - -### 语言切换 -1. 点击页面右上角的"中文"或"English"按钮 -2. 页面会立即切换到对应语言 -3. 语言偏好会自动保存,下次访问时保持选择 - -### 系统设置 -1. **基础设置**:可以修改API超时时间、最大历史记录数等 -2. **系统信息**:显示当前服务端口和WebSocket端口(只读) -3. **日志配置**:可以调整系统日志级别 - -## 🔄 后续优化建议 - -### 1. **扩展翻译内容** -- 添加更多页面的翻译支持 -- 完善错误消息和提示的翻译 -- 支持更多语言(如日语、韩语等) - -### 2. **配置管理优化** -- 添加配置验证功能 -- 提供配置导入/导出功能 -- 添加配置历史记录 - -### 3. **用户体验改进** -- 添加设置向导功能 -- 提供配置建议和最佳实践 -- 添加配置变更通知 - ---- - -**总结**:成功删除了前端页面中的无效系统设置,并添加了完整的中英文语言切换功能。优化后的页面更加简洁、实用,用户体验得到显著提升。🎉 diff --git a/推送脚本使用说明.md b/推送脚本使用说明.md deleted file mode 100644 index b3c9a60..0000000 --- a/推送脚本使用说明.md +++ /dev/null @@ -1,255 +0,0 @@ -# TSP智能助手 - 推送脚本使用说明 - -## 📁 脚本文件说明 - -### 1. `auto_push.bat` - 智能自动推送脚本 -**功能**: 完整的Git推送流程,包含状态检查、确认、提交和推送 -**特点**: -- 显示详细的Git状态 -- **智能分析markdown文件修改** -- **自动生成语义化提交信息** -- 用户确认机制(支持编辑提交信息) -- 错误处理和状态反馈 - -**智能提交信息生成**: -- `fix:` - 修复问题(检测到"修复"、"解决"、"问题"、"错误") -- `feat:` - 新增功能(检测到"功能"、"新增"、"添加"、"实现") -- `perf:` - 性能优化(检测到"优化"、"性能"、"改进"、"提升") -- `docs:` - 文档更新(默认类型) - -**使用方法**: -```bash -# 直接运行(智能生成提交信息) -auto_push.bat - -# 交互选项: -# y - 使用生成的提交信息 -# n - 手动输入提交信息 -# e - 编辑生成的提交信息 -``` - -### 2. `auto_push.ps1` - PowerShell高级版本 -**功能**: 功能最全面的推送脚本,支持参数和高级功能 -**特点**: -- 彩色输出和美观的界面 -- 支持命令行参数 -- 智能提交信息生成 -- 详细的统计信息 -- 错误处理和回滚 - -**使用方法**: -```powershell -# 基本使用 -.\auto_push.ps1 - -# 指定提交信息 -.\auto_push.ps1 "feat: 添加新功能" - -# 强制推送(跳过确认) -.\auto_push.ps1 -NoConfirm - -# 强制推送并指定信息 -.\auto_push.ps1 "紧急修复" -Force -NoConfirm -``` - -### 3. `quick_push.bat` - 智能快速推送脚本 -**功能**: 最简单的推送方式,适合日常快速提交 -**特点**: -- 一键推送 -- **智能分析markdown文件并生成提交信息** -- **自动识别提交类型** -- 最小化交互 -- 支持自定义提交信息 - -**使用方法**: -```bash -# 智能生成提交信息(推荐) -quick_push.bat - -# 指定自定义提交信息 -quick_push.bat "修复bug" -``` - -## 🧠 智能功能详解 - -### 自动内容分析 -脚本会自动检测修改的markdown文件,并提取以下信息: -- **文档标题**:提取 `# 标题` 格式的内容 -- **问题描述**:识别包含"问题"、"错误"等关键词的内容 -- **解决方案**:识别包含"解决"、"修复"等关键词的内容 -- **功能描述**:识别包含"功能"、"新增"等关键词的内容 - -### 提交类型识别 -根据markdown文件内容,自动识别提交类型: - -| 关键词 | 提交类型 | 示例 | -|--------|----------|------| -| 修复、解决、问题、错误 | `fix:` | `fix: 飞书权限问题修复` | -| 功能、新增、添加、实现 | `feat:` | `feat: 新增AI建议功能` | -| 优化、性能、改进、提升 | `perf:` | `perf: 优化图表渲染性能` | -| 其他 | `docs:` | `docs: 更新文档记录` | - -### 示例工作流 -```markdown -# 飞书权限问题修复 - -## 问题描述 -AI建议无法写入飞书表格,出现403权限错误。 - -## 解决方案 -1. 检查飞书应用权限配置 -2. 添加必要的读写权限 -``` - -**生成的提交信息**: `fix: 飞书权限问题修复` - -## 🚀 推荐使用场景 - -### 日常开发 -```bash -# 快速提交日常更改 -quick_push.bat "日常更新" -``` - -### 功能开发 -```bash -# 使用PowerShell版本,获得最佳体验 -.\auto_push.ps1 "feat: 添加用户管理功能" -``` - -### 紧急修复 -```bash -# 快速修复 -quick_push.bat "hotfix: 修复登录问题" -``` - -### 团队协作 -```bash -# 使用标准版本,确保流程规范 -auto_push.bat -``` - -## ⚙️ 脚本特性对比 - -| 特性 | quick_push.bat | auto_push.bat | auto_push.ps1 | -|------|----------------|---------------|---------------| -| 执行速度 | ⚡ 最快 | 🐌 中等 | 🐌 中等 | -| 用户交互 | 最少 | 中等 | 最多 | -| 错误处理 | 基础 | 完整 | 完整 | -| 状态显示 | 基础 | 详细 | 最详细 | -| 参数支持 | 基础 | 无 | 完整 | -| 彩色输出 | 无 | 无 | ✅ | -| 统计信息 | 无 | 基础 | 详细 | -| **智能提交信息** | ✅ | ✅ | ❌ | -| **Markdown分析** | ✅ | ✅ | ❌ | -| **提交类型识别** | ✅ | ✅ | ❌ | - -## 🔧 自定义配置 - -### 修改默认提交信息格式 -编辑 `auto_push.bat` 第25行: -```batch -set commit_msg=feat: 自动提交 - %date% %time% -``` - -### 修改远程分支 -编辑所有脚本中的 `origin main` 为你的分支: -```batch -git push origin your-branch -``` - -### 添加预提交检查 -在 `auto_push.ps1` 中添加检查函数: -```powershell -function Test-PreCommit { - # 运行测试 - python -m pytest - # 代码格式化检查 - python -m black --check . - # 类型检查 - python -m mypy . -} -``` - -## 🛠️ 故障排除 - -### 常见问题 - -1. **"nothing to commit, working tree clean" 错误** - ``` - 原因: 工作区没有未提交的更改 - 解决: 脚本已自动检测并跳过推送,这是正常行为 - ``` - -2. **推送失败 (Push failed)** - ``` - 可能原因: - - 网络连接问题 - - 远程仓库权限不足 - - 分支冲突 - - 需要先拉取远程更改 - - 解决方案: - 1. 检查网络连接 - 2. 运行: git pull origin main - 3. 重新运行推送脚本 - ``` - -3. **PowerShell执行策略错误** - ```powershell - Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser - ``` - -4. **Git认证失败** - ```bash - # 检查远程仓库配置 - git remote -v - - # 重新设置认证 - git config --global credential.helper store - ``` - -5. **编码问题** - ```bash - # 确保控制台支持UTF-8 - chcp 65001 - ``` - -6. **重复使用脚本报错** - ``` - 原因: 工作区已干净,无需再次推送 - 解决: 脚本已优化,会自动检测并跳过 - ``` - -### 错误代码说明 - -- `退出代码 0`: 成功 -- `退出代码 1`: Git操作失败 -- `退出代码 2`: 用户取消操作 - -## 📝 最佳实践 - -1. **提交前检查**: 使用 `auto_push.ps1` 查看详细状态 -2. **提交信息规范**: 使用 `feat:`, `fix:`, `docs:` 等前缀 -3. **定期推送**: 避免长时间不推送导致冲突 -4. **分支管理**: 在功能分支开发,合并到主分支 - -## 🎯 示例工作流 - -```bash -# 1. 开发功能 -# ... 编写代码 ... - -# 2. 快速推送 -quick_push.bat "feat: 添加AI建议功能" - -# 3. 或者详细推送 -.\auto_push.ps1 "feat: 添加AI建议功能 -- 实现语义相似度计算 -- 优化前端UI显示 -- 添加配置化阈值" -``` - ---- - -**提示**: 建议将脚本文件添加到项目根目录,并设置适当的执行权限。对于团队使用,建议统一使用 `auto_push.ps1` 以确保流程一致性。 diff --git a/新功能说明.md b/新功能说明.md deleted file mode 100644 index 3b02f54..0000000 --- a/新功能说明.md +++ /dev/null @@ -1,210 +0,0 @@ -# TSP智能助手新功能说明 - -## 概述 - -本次更新为TSP智能助手添加了以下核心功能模块: - -## 1. 对话历史模块 - -### 功能特性 -- **对话管理**: 完整的对话记录管理 -- **对话记忆**: 支持多轮对话上下文保持 -- **Redis缓存**: 使用Redis提升对话历史查询性能 -- **分页查询**: 支持游标分页查询对话历史 -- **删除功能**: 支持删除单个或批量删除对话记录 - -### 核心文件 -- `src/dialogue/conversation_history.py`: 对话历史管理器 -- 集成到 `src/dialogue/dialogue_manager.py` - -### 使用方法 -```python -# 获取用户对话历史 -history = assistant.get_user_conversation_history(user_id, limit=10, offset=0) - -# 删除对话记录 -assistant.delete_conversation(conversation_id) - -# 删除用户所有对话记录 -assistant.delete_user_conversations(user_id) -``` - -## 2. Token消耗监控 - -### 功能特性 -- **实时监控**: 实时记录和监控Token使用情况 -- **成本计算**: 自动计算AI调用成本 -- **阈值预警**: 设置成本阈值并触发预警 -- **统计分析**: 提供详细的Token使用统计 -- **趋势分析**: 成本趋势分析 - -### 核心文件 -- `src/analytics/token_monitor.py`: Token监控器 - -### 使用方法 -```python -# 获取Token使用统计 -token_stats = assistant.get_token_usage_stats(user_id, days=7) - -# 获取成本趋势 -cost_trend = assistant.get_cost_trend(days=30) -``` - -## 3. AI调用成功率监控 - -### 功能特性 -- **成功率监控**: 监控AI API调用成功率 -- **性能分析**: 分析响应时间和错误率 -- **模型对比**: 不同模型的性能对比 -- **预警机制**: 连续失败和错误率预警 -- **趋势分析**: 性能趋势分析 - -### 核心文件 -- `src/analytics/ai_success_monitor.py`: AI成功率监控器 - -### 使用方法 -```python -# 获取AI性能统计 -ai_stats = assistant.get_ai_performance_stats(hours=24) - -# 获取性能趋势 -performance_trend = assistant.get_performance_trend(days=7) -``` - -## 4. 系统优化模块 - -### 功能特性 -- **性能优化**: CPU、内存、磁盘使用率监控 -- **安全优化**: 输入安全检查、频率限制 -- **流量保护**: 请求频率限制和并发控制 -- **成本优化**: 成本限制和预算控制 -- **稳定性优化**: 系统健康状态监控 - -### 核心文件 -- `src/core/system_optimizer.py`: 系统优化器 - -### 使用方法 -```python -# 检查频率限制 -can_proceed = assistant.check_rate_limit(user_id) - -# 检查输入安全性 -security_check = assistant.check_input_security(user_input) - -# 检查成本限制 -can_proceed = assistant.check_cost_limit(estimated_cost) - -# 获取系统状态 -system_status = assistant.get_system_optimization_status() -``` - -## 5. Redis集成 - -### 配置信息 -- **主机**: 43.134.68.207 -- **端口**: 6379 -- **密码**: 123456 - -### 使用场景 -- 对话历史缓存 -- Token使用数据存储 -- AI调用记录缓存 -- 系统性能指标存储 -- 频率限制计数 -- 成本限制计数 - -## 6. 启动脚本整合 - -### 更新内容 -- 整合所有新功能到 `start_dashboard.py` -- 添加系统初始化检查 -- 显示完整功能列表 -- 错误处理和日志记录 - -### 启动方式 -```bash -python start_dashboard.py -``` - -## 7. 测试脚本 - -### 测试文件 -- `test_new_features.py`: 新功能测试脚本 - -### 测试内容 -- 对话历史功能测试 -- Token监控功能测试 -- AI性能监控测试 -- 系统优化功能测试 -- 对话删除功能测试 -- 数据清理功能测试 - -### 运行测试 -```bash -python test_new_features.py -``` - -## 8. 依赖更新 - -### 新增依赖 -- `redis>=4.5.0`: Redis客户端库 - -### 更新文件 -- `requirements.txt`: 添加Redis依赖 - -## 9. 性能优化特性 - -### 缓存策略 -- Redis缓存对话历史,提升查询性能 -- 内存缓存最近对话,减少数据库访问 -- 分层缓存策略,平衡性能和存储 - -### 监控指标 -- 实时系统资源监控 -- API调用性能监控 -- Token使用成本监控 -- 用户行为分析 - -### 安全特性 -- 输入内容安全检查 -- 请求频率限制 -- 成本预算控制 -- 异常情况预警 - -## 10. 使用建议 - -### 生产环境部署 -1. 确保Redis服务正常运行 -2. 配置合适的监控阈值 -3. 定期清理历史数据 -4. 监控系统性能指标 - -### 开发环境测试 -1. 运行测试脚本验证功能 -2. 检查Redis连接状态 -3. 观察日志输出 -4. 测试各种边界情况 - -## 11. 故障排除 - -### 常见问题 -1. **Redis连接失败**: 检查Redis服务状态和网络连接 -2. **数据库连接问题**: 检查MySQL服务状态 -3. **性能问题**: 检查系统资源使用情况 -4. **成本超限**: 调整成本限制阈值 - -### 日志位置 -- 主日志: `logs/tsp_assistant.log` -- 启动日志: `logs/dashboard.log` - -## 12. 未来扩展 - -### 计划功能 -- 更细粒度的权限控制 -- 更丰富的监控指标 -- 自动化运维功能 -- 机器学习优化建议 - ---- - -**注意**: 所有新功能都已集成到现有系统中,不会影响原有功能的正常使用。建议在生产环境部署前先在测试环境验证所有功能。 diff --git a/新功能说明_v1.4.0.md b/新功能说明_v1.4.0.md deleted file mode 100644 index 237e504..0000000 --- a/新功能说明_v1.4.0.md +++ /dev/null @@ -1,243 +0,0 @@ -# TSP智能助手 v1.4.0 新功能说明 - -## 🎉 版本概述 - -TSP智能助手 v1.4.0 是一个重要的功能更新版本,主要包含飞书集成、页面功能合并、数据库架构优化和代码重构等重要改进。 - -## 🚀 主要新功能 - -### 1. 飞书多维表格集成 📱 - -#### 功能描述 -- **数据同步**: 支持从飞书多维表格自动同步工单数据 -- **字段映射**: 智能映射飞书字段到本地数据库结构 -- **实时更新**: 支持增量同步和全量同步 -- **数据预览**: 同步前可预览数据,确保准确性 - -#### 支持的飞书字段 -| 飞书字段 | 本地字段 | 类型 | 说明 | -|---------|---------|------|------| -| TR Number | order_id | String | 工单编号 | -| TR Description | description | Text | 工单描述 | -| Type of problem | category | String | 问题类型 | -| TR Level | priority | String | 优先级 | -| TR Status | status | String | 工单状态 | -| Source | source | String | 来源 | -| Created by | created_by | String | 创建人 | -| Module(模块) | module | String | 模块 | -| Wilfulness(责任人) | wilfulness | String | 责任人 | -| Date of close TR | date_of_close | DateTime | 关闭日期 | -| Vehicle Type01 | vehicle_type | String | 车型 | -| VIN\|sim | vin_sim | String | 车架号/SIM | -| App remote control version | app_remote_control_version | String | 应用远程控制版本 | -| HMI SW | hmi_sw | String | HMI软件版本 | -| 父记录 | parent_record | String | 父记录 | -| Has it been updated on the same day | has_updated_same_day | String | 是否同日更新 | -| Operating time | operating_time | String | 操作时间 | - -#### 使用方法 -1. 在飞书开放平台创建企业自建应用 -2. 配置 `config/integrations_config.json` 文件 -3. 在主仪表板的"飞书同步"标签页进行数据同步 -4. 支持测试连接、预览数据、执行同步等操作 - -### 2. 页面功能合并 🎨 - -#### 改进内容 -- **统一界面**: 飞书同步功能已合并到主仪表板 -- **标签页设计**: 使用标签页组织不同功能模块 -- **用户体验**: 所有功能现在都在一个统一的界面中 -- **代码优化**: 删除了冗余的独立页面和蓝图 - -#### 界面变化 -- **原独立页面**: `http://localhost:5000/feishu-sync` (已删除) -- **现集成位置**: 主仪表板的"飞书同步"标签页 -- **访问方式**: 访问 `http://localhost:5000` 即可使用所有功能 - -### 3. 数据库架构优化 🗄️ - -#### 工单表扩展 -为 `work_orders` 表新增了12个飞书相关字段: - -```sql --- 飞书集成字段 -source VARCHAR(50) -- 来源 -module VARCHAR(100) -- 模块 -created_by VARCHAR(100) -- 创建人 -wilfulness VARCHAR(100) -- 责任人 -date_of_close DATETIME -- 关闭日期 -vehicle_type VARCHAR(100) -- 车型 -vin_sim VARCHAR(50) -- 车架号/SIM -app_remote_control_version VARCHAR(100) -- 应用远程控制版本 -hmi_sw VARCHAR(100) -- HMI软件版本 -parent_record VARCHAR(100) -- 父记录 -has_updated_same_day VARCHAR(50) -- 是否同日更新 -operating_time VARCHAR(100) -- 操作时间 -``` - -#### 数据库初始化改进 -- **自动迁移**: 字段迁移已集成到数据库初始化流程 -- **智能检测**: 自动检测缺失字段并添加 -- **错误处理**: 改进的错误处理和日志记录 -- **兼容性**: 保持与现有数据的兼容性 - -### 4. 代码重构优化 🔧 - -#### 文件结构优化 -- **大文件拆分**: 将 `src/agent_assistant.py` 拆分为多个模块 -- **模块化设计**: 创建 `agent_assistant_core.py`、`agent_message_handler.py`、`agent_sample_actions.py` -- **降低风险**: 减少单文件代码行数,降低运行风险 -- **维护性**: 提高代码的可维护性和可读性 - -#### 前端架构改进 -- **JavaScript类**: 使用类组织前端逻辑 -- **模块化**: `TSPDashboard`、`FeishuSyncManager` 等独立模块 -- **异步处理**: 改进的异步API调用处理 -- **错误处理**: 更好的错误处理和用户反馈 - -## 📋 配置说明 - -### 飞书集成配置 - -编辑 `config/integrations_config.json` 文件: - -```json -{ - "feishu": { - "app_id": "cli_a8b50ec0eed1500d", - "app_secret": "ccxkE7ZCFQZcwkkM1rLy0ccZRXYsT2xK", - "app_token": "XXnEbiCmEaMblSs6FDJcFCqsnIg", - "table_id": "tblnl3vJPpgMTSiP", - "last_updated": "2025-09-19T18:27:40.579958", - "status": "active" - }, - "system": { - "sync_limit": 10, - "ai_suggestions_enabled": true, - "auto_sync_interval": 0, - "last_sync_time": null - } -} -``` - -### 环境变量支持 - -```bash -# 飞书配置 -export FEISHU_APP_ID="your-app-id" -export FEISHU_APP_SECRET="your-app-secret" -export FEISHU_APP_TOKEN="your-app-token" -export FEISHU_TABLE_ID="your-table-id" -``` - -## 🚀 部署指南 - -### 部署前准备 - -1. **配置飞书应用** - - 在飞书开放平台创建企业自建应用 - - 获取应用凭证和权限 - -2. **更新配置文件** - - 配置 `config/integrations_config.json` - - 设置正确的飞书应用信息 - -3. **初始化数据库** - ```bash - python init_database.py - ``` - -4. **测试连接** - - 启动服务后访问主仪表板 - - 在"飞书同步"标签页测试连接 - -### 部署步骤 - -```bash -# 1. 备份当前版本 -python scripts/update_manager.py create-backup --environment production - -# 2. 部署新版本 -python scripts/update_manager.py auto-update --source . --environment production - -# 3. 验证功能 -# 访问 http://localhost:5000 -# 测试飞书同步功能 -``` - -## 🔍 使用指南 - -### 飞书数据同步 - -1. **访问功能** - - 打开浏览器访问 `http://localhost:5000` - - 点击"飞书同步"标签页 - -2. **测试连接** - - 点击"测试连接"按钮 - - 验证飞书应用配置是否正确 - -3. **预览数据** - - 点击"预览数据"按钮 - - 查看将要同步的数据内容 - -4. **执行同步** - - 点击"同步数据"按钮 - - 等待同步完成 - -5. **查看结果** - - 在工单管理页面查看同步的数据 - - 验证字段映射是否正确 - -### 工单管理增强 - -1. **查看飞书字段** - - 在工单详情页面可以看到新的飞书字段 - - 包括来源、模块、责任人等信息 - -2. **数据关联** - - 飞书数据与本地工单数据关联 - - 支持双向数据同步 - -## 🐛 故障排除 - -### 常见问题 - -1. **飞书连接失败** - - 检查app_id和app_secret是否正确 - - 验证应用权限配置 - - 确认网络连接正常 - -2. **字段映射错误** - - 检查飞书表格字段名称 - - 验证字段映射配置 - - 查看同步日志 - -3. **数据库迁移失败** - - 检查数据库连接状态 - - 验证数据库权限 - - 查看初始化日志 - -4. **页面功能异常** - - 清除浏览器缓存 - - 检查JavaScript控制台错误 - - 验证API接口状态 - -### 日志位置 - -- **应用日志**: `logs/tsp_assistant.log` -- **数据库日志**: 数据库初始化输出 -- **飞书同步日志**: 在同步界面显示 - -## 📞 技术支持 - -如有问题,请: - -1. 查看相关日志文件 -2. 检查配置文件设置 -3. 验证网络连接状态 -4. 联系技术支持团队 - ---- - -**TSP智能助手 v1.4.0** - 让车辆服务更智能,让数据管理更便捷! 🚗✨ diff --git a/重构总结.md b/重构总结.md deleted file mode 100644 index 98bb7e4..0000000 --- a/重构总结.md +++ /dev/null @@ -1,130 +0,0 @@ -# TSP助手项目重构总结 - -## 问题描述 - -1. **app.py文件损坏**: 原文件有乱码和语法错误 -2. **文件过长**: 原app.py文件有1953行,难以维护 -3. **架构不合理**: 所有功能都集中在一个文件中 -4. **前端响应问题**: 工单和对话历史的删除/新增操作前端无响应 - -## 解决方案 - -### 1. 修复乱码和错误 -- 识别并修复了所有乱码字符 -- 统一了API响应格式 -- 修复了前端响应检查逻辑 - -### 2. 架构重构 -采用Flask蓝图(Blueprint)架构,将单一文件拆分为多个模块: - -#### 重构前 -- **app.py**: 1953行,包含所有功能 -- 代码混乱,难以维护 -- 单点故障风险高 - -#### 重构后 -- **app.py**: 674行,只包含核心路由 -- **blueprints/**: 6个功能模块 - - `alerts.py`: 预警管理 (100行) - - `workorders.py`: 工单管理 (400行) - - `conversations.py`: 对话管理 (80行) - - `knowledge.py`: 知识库管理 (150行) - - `monitoring.py`: 监控管理 (300行) - - `system.py`: 系统管理 (200行) - -### 3. 前端响应问题修复 -统一了前端JavaScript中的响应检查逻辑: -- 删除操作: 检查 `data.success` 而不是 `response.ok` -- 新增操作: 统一使用 `data.success` 检查 -- 修改操作: 修复了响应状态检查 - -## 技术改进 - -### 1. 模块化设计 -- 每个功能模块独立 -- 便于团队协作开发 -- 降低代码耦合度 - -### 2. 错误隔离 -- 单个模块错误不影响整体 -- 独立的错误处理机制 -- 更好的调试体验 - -### 3. 可扩展性 -- 新增功能只需创建新蓝图 -- 蓝图可复用 -- 支持插件式开发 - -### 4. 维护性提升 -- 代码结构清晰 -- 功能职责明确 -- 便于代码审查 - -## 文件结构对比 - -### 重构前 -``` -src/web/ -├── app.py (1953行) - 所有功能 -├── static/ -└── templates/ -``` - -### 重构后 -``` -src/web/ -├── app.py (674行) - 核心路由 -├── app_backup.py - 原文件备份 -├── blueprints/ - 蓝图模块 -│ ├── __init__.py -│ ├── alerts.py -│ ├── workorders.py -│ ├── conversations.py -│ ├── knowledge.py -│ ├── monitoring.py -│ ├── system.py -│ └── README.md -├── static/ -└── templates/ -``` - -## 性能优化 - -1. **懒加载**: 避免启动时重复初始化 -2. **模块化**: 按需加载功能模块 -3. **缓存优化**: 保持原有的查询优化 -4. **错误处理**: 统一的异常处理机制 - -## 兼容性保证 - -1. **API接口**: 保持原有接口不变 -2. **前端调用**: 无需修改前端代码 -3. **数据库**: 保持原有数据模型 -4. **功能**: 所有功能正常工作 - -## 测试验证 - -- ✅ 应用导入成功 -- ✅ 蓝图注册正常 -- ✅ API路由正确 -- ✅ 前端响应修复 -- ✅ 代码语法检查通过 - -## 后续建议 - -1. **单元测试**: 为每个蓝图编写单元测试 -2. **文档完善**: 补充API文档和使用说明 -3. **性能监控**: 添加性能监控和日志 -4. **持续集成**: 建立CI/CD流程 -5. **代码规范**: 制定代码规范和审查流程 - -## 总结 - -通过这次重构,我们成功解决了: -- ✅ 修复了app.py的乱码和错误问题 -- ✅ 将1953行的单文件拆分为多个模块 -- ✅ 修复了前端响应问题 -- ✅ 提升了代码的可维护性和可扩展性 -- ✅ 保持了功能的完整性和兼容性 - -项目现在具有更好的架构设计,便于后续的开发和维护。 diff --git a/飞书同步灵活字段映射系统总结.md b/飞书同步灵活字段映射系统总结.md deleted file mode 100644 index b8fd820..0000000 --- a/飞书同步灵活字段映射系统总结.md +++ /dev/null @@ -1,231 +0,0 @@ -# 飞书同步灵活字段映射系统总结 - -## 问题背景 - -原有的飞书同步系统存在以下问题: -- **字段映射过于呆板**:只能处理预定义的字段映射 -- **缺乏灵活性**:无法适应字段调整、新增字段等情况 -- **维护困难**:需要修改代码才能添加新的字段映射 -- **用户体验差**:字段不存在时只能看到"不存在于数据中"的日志 - -## 解决方案 - -开发了一套**灵活字段映射系统**,具备以下特性: - -### 1. 动态字段发现 -- 自动分析飞书表格中的字段 -- 识别已映射和未映射的字段 -- 为未映射字段提供智能建议 - -### 2. 多种映射方式 -- **直接映射**:字段名完全匹配 -- **别名映射**:支持多个别名 -- **模式匹配**:使用正则表达式匹配 -- **优先级管理**:支持字段优先级设置 - -### 3. 智能建议算法 -- **相似度匹配**:基于字符串相似度计算 -- **模式匹配**:使用正则表达式模式 -- **优先级排序**:按相似度和优先级排序建议 - -### 4. 灵活配置管理 -- **Web界面管理**:提供友好的管理界面 -- **API接口**:支持程序化管理 -- **配置文件**:支持JSON配置文件 -- **实时更新**:配置变更立即生效 - -## 技术实现 - -### 核心组件 - -#### 1. FlexibleFieldMapper 类 -```python -class FlexibleFieldMapper: - - discover_fields() # 字段发现 - - map_field() # 字段映射 - - convert_fields() # 字段转换 - - add_field_mapping() # 添加映射 - - remove_field_mapping() # 删除映射 -``` - -#### 2. 配置文件结构 -```json -{ - "field_mapping": {}, # 直接映射 - "field_aliases": {}, # 别名映射 - "field_patterns": {}, # 模式匹配 - "field_priorities": {}, # 优先级 - "auto_mapping_enabled": true, - "similarity_threshold": 0.6 -} -``` - -#### 3. API接口 -- `GET /api/feishu-sync/field-mapping/status` - 获取映射状态 -- `POST /api/feishu-sync/field-mapping/discover` - 发现字段 -- `POST /api/feishu-sync/field-mapping/add` - 添加映射 -- `POST /api/feishu-sync/field-mapping/remove` - 删除映射 - -### 集成方式 - -#### 1. 向后兼容 -- 保留原有字段映射配置 -- 自动将原有映射添加到新系统中 -- 不影响现有功能 - -#### 2. 无缝集成 -- 更新 `WorkOrderSyncService` 类 -- 使用新的字段转换方法 -- 提供详细的转换统计信息 - -## 功能特性 - -### 1. 智能字段发现 -``` -输入:飞书字段数据 -输出: -- 已映射字段列表 -- 未映射字段列表 -- 建议映射列表(包含置信度) -``` - -### 2. 多种匹配策略 -- **精确匹配**:字段名完全相同 -- **别名匹配**:字段名在别名列表中 -- **相似度匹配**:字符串相似度超过阈值 -- **模式匹配**:正则表达式匹配 - -### 3. 优先级管理 -- **优先级 1**:核心字段(工单号、描述、状态等) -- **优先级 2**:重要字段(来源、解决方案等) -- **优先级 3**:扩展字段(版本信息、操作时间等) - -### 4. 自动学习能力 -- 根据使用情况优化映射规则 -- 支持动态调整相似度阈值 -- 可启用/禁用自动映射功能 - -## 使用效果 - -### 测试结果 -``` -测试数据:18个字段 -- 已映射:16个字段(88.9%) -- 未映射:2个字段(11.1%) -- 智能建议:高置信度建议5个字段 - -映射状态: -- 直接映射:23个 -- 别名映射:107个 -- 模式匹配:83个 -- 自动映射:启用 -``` - -### 实际应用场景 - -#### 场景1:字段名调整 -**之前**:需要修改代码,重新部署 -**现在**:在Web界面一键添加映射 - -#### 场景2:新增字段 -**之前**:字段被忽略,数据丢失 -**现在**:自动识别并提供建议映射 - -#### 场景3:字段顺序调整 -**之前**:可能影响映射结果 -**现在**:基于字段名映射,不受顺序影响 - -## 用户界面 - -### Web管理界面 -- **字段发现**:一键分析飞书字段 -- **映射管理**:可视化添加/删除映射 -- **状态监控**:实时查看映射状态 -- **建议应用**:一键应用智能建议 - -### 操作流程 -1. 访问 `/api/feishu-sync/field-mapping` -2. 点击"发现字段"分析当前字段 -3. 查看建议映射并一键应用 -4. 手动添加特殊字段映射 -5. 监控映射状态和效果 - -## 技术优势 - -### 1. 高可扩展性 -- 支持无限添加字段映射 -- 支持多种映射策略 -- 支持自定义匹配规则 - -### 2. 高可维护性 -- 配置文件管理 -- Web界面操作 -- API接口支持 - -### 3. 高智能化 -- 自动字段发现 -- 智能映射建议 -- 自适应学习 - -### 4. 高兼容性 -- 向后兼容 -- 无缝集成 -- 渐进式升级 - -## 部署说明 - -### 1. 文件结构 -``` -src/integrations/ -├── flexible_field_mapper.py # 核心映射器 -├── workorder_sync.py # 更新的同步服务 -└── ... - -config/ -└── field_mapping_config.json # 映射配置文件 - -src/web/ -├── templates/ -│ └── field_mapping.html # 管理界面 -└── blueprints/ - └── feishu_sync.py # 更新的API接口 -``` - -### 2. 配置要求 -- Python 3.7+ -- Flask -- 现有飞书同步功能 - -### 3. 启动方式 -- 无需额外配置 -- 自动加载默认映射 -- 支持热更新 - -## 未来规划 - -### 短期目标 -- 优化相似度算法 -- 增加更多匹配模式 -- 完善Web界面功能 - -### 中期目标 -- 支持批量字段映射 -- 增加映射历史记录 -- 提供映射效果分析 - -### 长期目标 -- 机器学习优化映射 -- 支持多语言字段映射 -- 集成更多数据源 - -## 总结 - -灵活字段映射系统成功解决了飞书同步的字段映射问题,提供了: - -✅ **智能化**:自动发现字段并提供建议 -✅ **灵活性**:支持多种映射方式和策略 -✅ **易用性**:Web界面操作,一键应用建议 -✅ **可维护性**:配置文件管理,API接口支持 -✅ **兼容性**:向后兼容,无缝集成 - -该系统大大提升了飞书同步的灵活性和用户体验,为后续的功能扩展奠定了坚实基础。