refactor: 移除冗余文件并优化代码结构
- 删除多个不再使用的脚本和配置文件,包括 `auto_push.bat`, `check_and_fix_users.py`, `init.sql` 等。 - 新增 `git_push.bat` 和 `git_push.sh` 脚本以简化 Git 推送流程。 - 更新 `README.md` 以反映最新的功能和结构变化。 - 优化前端代码,添加新的页面和组件,提升用户体验。 此提交旨在清理项目结构并增强代码可维护性。
This commit is contained in:
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(curl:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
144
LLM配置统一说明.md
144
LLM配置统一说明.md
@@ -1,144 +0,0 @@
|
|||||||
# LLM配置统一管理说明
|
|
||||||
|
|
||||||
## ? 概述
|
|
||||||
|
|
||||||
本项目已将LLM配置统一管理,确保整个项目只在一个地方配置千问模型,所有地方都从统一配置获取。
|
|
||||||
|
|
||||||
## ?? 配置架构
|
|
||||||
|
|
||||||
### 1. 核心配置文件:`config/llm_config.py`
|
|
||||||
|
|
||||||
这是**唯一的**LLM配置源,定义了千问模型的所有配置:
|
|
||||||
|
|
||||||
```python
|
|
||||||
QWEN_CONFIG = LLMConfig(
|
|
||||||
provider="qwen",
|
|
||||||
api_key="sk-c0dbefa1718d46eaa897199135066f00",
|
|
||||||
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
||||||
model="qwen-plus-latest",
|
|
||||||
temperature=0.7,
|
|
||||||
max_tokens=2000
|
|
||||||
)
|
|
||||||
|
|
||||||
# 默认使用千问模型
|
|
||||||
DEFAULT_CONFIG = QWEN_CONFIG
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 统一配置管理器:`src/config/unified_config.py`
|
|
||||||
|
|
||||||
统一配置管理器在初始化时自动从 `config/llm_config.py` 加载配置:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def _load_default_llm_config(self) -> LLMConfig:
|
|
||||||
"""加载默认LLM配置"""
|
|
||||||
try:
|
|
||||||
from config.llm_config import DEFAULT_CONFIG
|
|
||||||
# 转换配置格式
|
|
||||||
return LLMConfig(...)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"无法加载默认LLM配置,使用内置默认值: {e}")
|
|
||||||
return LLMConfig()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 全局配置实例
|
|
||||||
|
|
||||||
通过 `get_config()` 函数获取全局配置实例:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.config.unified_config import get_config
|
|
||||||
|
|
||||||
config = get_config()
|
|
||||||
llm_config = config.llm # 获取LLM配置
|
|
||||||
```
|
|
||||||
|
|
||||||
## ? 如何使用
|
|
||||||
|
|
||||||
### 在任何需要使用LLM的地方:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.config.unified_config import get_config
|
|
||||||
|
|
||||||
# 获取LLM配置
|
|
||||||
llm_config = get_config().llm
|
|
||||||
|
|
||||||
# 使用配置
|
|
||||||
print(f"Provider: {llm_config.provider}")
|
|
||||||
print(f"Model: {llm_config.model}")
|
|
||||||
print(f"API Key: {llm_config.api_key}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 示例:AI建议服务
|
|
||||||
|
|
||||||
```python
|
|
||||||
class AISuggestionService:
|
|
||||||
def __init__(self):
|
|
||||||
# 从统一配置管理器获取LLM配置
|
|
||||||
self.llm_config = get_config().llm
|
|
||||||
logger.info(f"使用LLM配置: {self.llm_config.provider} - {self.llm_config.model}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## ? 配置优先级
|
|
||||||
|
|
||||||
1. **第一优先级**:统一配置管理器中的配置(可通过配置文件或环境变量设置)
|
|
||||||
2. **第二优先级**:`config/llm_config.py` 中的 `DEFAULT_CONFIG`
|
|
||||||
3. **最后备选**:内置的默认值
|
|
||||||
|
|
||||||
## ? 修改配置
|
|
||||||
|
|
||||||
### 方法1:修改配置文件(推荐)
|
|
||||||
|
|
||||||
直接编辑 `config/llm_config.py`,修改API密钥或模型:
|
|
||||||
|
|
||||||
```python
|
|
||||||
QWEN_CONFIG = LLMConfig(
|
|
||||||
provider="qwen",
|
|
||||||
api_key="你的新API密钥", # 修改这里
|
|
||||||
model="qwen-max", # 或修改模型
|
|
||||||
...
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方法2:通过统一配置文件
|
|
||||||
|
|
||||||
编辑 `config/unified_config.json`(如果存在):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"llm": {
|
|
||||||
"provider": "qwen",
|
|
||||||
"api_key": "你的新API密钥",
|
|
||||||
"model": "qwen-plus-latest",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方法3:环境变量(可选)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export LLM_API_KEY="你的API密钥"
|
|
||||||
export LLM_MODEL="qwen-plus-latest"
|
|
||||||
```
|
|
||||||
|
|
||||||
## ? 优势
|
|
||||||
|
|
||||||
1. **单一配置源**:只需要在 `config/llm_config.py` 配置一次
|
|
||||||
2. **统一管理**:所有模块都通过统一配置管理器获取
|
|
||||||
3. **易于维护**:修改配置不需要修改多处代码
|
|
||||||
4. **自动同步**:修改配置后,所有使用该配置的地方自动更新
|
|
||||||
5. **向后兼容**:保留fallback机制,确保系统稳定运行
|
|
||||||
|
|
||||||
## ? 已更新的文件
|
|
||||||
|
|
||||||
- ? `config/llm_config.py` - 添加了 `get_default_llm_config()` 函数
|
|
||||||
- ? `src/config/unified_config.py` - 从 `config/llm_config.py` 加载默认配置
|
|
||||||
- ? `src/integrations/ai_suggestion_service.py` - 使用统一配置
|
|
||||||
- ? `src/agent/agent_assistant_core.py` - 使用统一配置
|
|
||||||
|
|
||||||
## ? 注意事项
|
|
||||||
|
|
||||||
- **不要**在代码中硬编码OpenAI或其他模型的配置
|
|
||||||
- **不要**直接从 `config/llm_config.py` 导入(除非作为fallback)
|
|
||||||
- **总是**通过 `get_config().llm` 获取配置
|
|
||||||
- 修改配置后,请重启应用使配置生效
|
|
||||||
|
|
||||||
34
README.md
34
README.md
@@ -1,6 +1,6 @@
|
|||||||
# TSP智能助手 (TSP Assistant)
|
# TSP智能助手 (TSP Assistant)
|
||||||
|
|
||||||
[](version.json)
|
[](version.json)
|
||||||
[](requirements.txt)
|
[](requirements.txt)
|
||||||
[](Dockerfile)
|
[](Dockerfile)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
@@ -15,19 +15,22 @@
|
|||||||
- **智能规划**: 基于目标驱动的任务规划和执行
|
- **智能规划**: 基于目标驱动的任务规划和执行
|
||||||
- **自主学习**: 从用户反馈中持续优化响应质量
|
- **自主学习**: 从用户反馈中持续优化响应质量
|
||||||
- **实时监控**: 主动监控系统状态和异常情况
|
- **实时监控**: 主动监控系统状态和异常情况
|
||||||
- **模块化重构**: 代码优化,降低运行风险,提升维护性
|
- **模块化重构**: 后端服务(Agent, 车辆数据, 分析, 测试)蓝图化,提高可维护性和扩展性
|
||||||
|
- **前端模块化**: 引入ES6模块化架构,优化UI组件、状态管理和API服务
|
||||||
|
|
||||||
### 💬 智能对话系统
|
### 💬 智能对话系统
|
||||||
- **实时通信**: WebSocket支持,毫秒级响应
|
- **实时通信**: WebSocket支持,毫秒级响应,已修复连接稳定性问题
|
||||||
- **上下文理解**: 多轮对话记忆和上下文关联
|
- **上下文理解**: 多轮对话记忆和上下文关联
|
||||||
- **VIN识别**: 自动识别车辆VIN码并获取实时数据
|
- **VIN识别**: 自动识别车辆VIN码并获取实时数据
|
||||||
- **知识库集成**: 基于TF-IDF和余弦相似度的智能检索
|
- **知识库集成**: 基于TF-IDF和余弦相似度的智能检索
|
||||||
|
- **自定义提示词**: 支持飞书同步和实时对话不同场景的LLM提示词
|
||||||
|
|
||||||
### 📊 数据驱动分析
|
### 📊 数据驱动分析
|
||||||
- **真实数据**: 基于数据库的真实性能趋势分析
|
- **真实数据**: 基于数据库的真实性能趋势分析
|
||||||
- **多维度统计**: 工单、预警、满意度、性能指标
|
- **多维度统计**: 工单、预警、满意度、性能指标
|
||||||
- **可视化展示**: Chart.js图表,直观的数据呈现
|
- **可视化展示**: Chart.js图表,直观的数据呈现
|
||||||
- **系统监控**: 实时CPU、内存、健康状态监控
|
- **系统监控**: 实时CPU、内存、健康状态监控
|
||||||
|
- **专属蓝图**: 独立的数据分析API模块,提供专业数据报告导出
|
||||||
|
|
||||||
### 🔧 企业级管理
|
### 🔧 企业级管理
|
||||||
- **多环境部署**: 开发、测试、生产环境隔离
|
- **多环境部署**: 开发、测试、生产环境隔离
|
||||||
@@ -35,6 +38,7 @@
|
|||||||
- **热更新**: 支持前端文件热更新,无需重启服务
|
- **热更新**: 支持前端文件热更新,无需重启服务
|
||||||
- **自动备份**: 更新前自动备份,支持一键回滚
|
- **自动备份**: 更新前自动备份,支持一键回滚
|
||||||
- **飞书集成**: 支持飞书多维表格数据同步和管理
|
- **飞书集成**: 支持飞书多维表格数据同步和管理
|
||||||
|
- **统一错误处理**: 后端API统一异常处理,提高系统健壮性
|
||||||
|
|
||||||
## 🏗️ 系统架构
|
## 🏗️ 系统架构
|
||||||
|
|
||||||
@@ -66,18 +70,21 @@
|
|||||||
- **VIN识别**: 自动识别车辆VIN并获取实时数据
|
- **VIN识别**: 自动识别车辆VIN并获取实时数据
|
||||||
- **知识库检索**: 智能匹配相关技术文档和解决方案
|
- **知识库检索**: 智能匹配相关技术文档和解决方案
|
||||||
- **工单创建**: 对话中直接创建和关联工单
|
- **工单创建**: 对话中直接创建和关联工单
|
||||||
|
- **错误修复**: 解决了WebSocket连接TypeError问题
|
||||||
|
|
||||||
### 2. Agent管理 🤖
|
### 2. Agent管理 🤖
|
||||||
- **工具管理**: 10+内置工具,支持自定义工具注册
|
- **工具管理**: 10+内置工具,支持自定义工具注册
|
||||||
- **执行监控**: 实时监控Agent任务执行状态
|
- **执行监控**: 实时监控Agent任务执行状态
|
||||||
- **性能统计**: 工具使用频率和成功率分析
|
- **性能统计**: 工具使用频率和成功率分析
|
||||||
- **智能规划**: 基于目标的任务分解和执行
|
- **智能规划**: 基于目标的任务分解和执行
|
||||||
|
- **专用蓝图**: Agent相关API已独立为蓝图管理
|
||||||
|
|
||||||
### 3. 工单系统 📋
|
### 3. 工单系统 📋
|
||||||
- **AI建议**: 基于知识库生成工单处理建议
|
- **AI建议**: 基于知识库生成工单处理建议
|
||||||
- **人工审核**: 支持人工输入和AI建议对比
|
- **人工审核**: 支持人工输入和AI建议对比
|
||||||
- **相似度评估**: 自动计算AI与人工建议的相似度
|
- **相似度评估**: 自动计算AI与人工建议的相似度
|
||||||
- **知识库更新**: 高相似度建议自动入库
|
- **知识库更新**: 高相似度建议自动入库
|
||||||
|
- **飞书AI提示词**: 针对飞书同步场景提供更详细的AI建议提示词
|
||||||
|
|
||||||
### 4. 知识库管理 📚
|
### 4. 知识库管理 📚
|
||||||
- **多格式支持**: TXT、PDF、DOC、DOCX、MD文件
|
- **多格式支持**: TXT、PDF、DOC、DOCX、MD文件
|
||||||
@@ -90,12 +97,14 @@
|
|||||||
- **多维度统计**: 工单、预警、满意度等关键指标
|
- **多维度统计**: 工单、预警、满意度等关键指标
|
||||||
- **系统健康**: CPU、内存、响应时间监控
|
- **系统健康**: CPU、内存、响应时间监控
|
||||||
- **可视化展示**: 丰富的图表和仪表板
|
- **可视化展示**: 丰富的图表和仪表板
|
||||||
|
- **专用蓝图**: 数据分析API已独立为蓝图管理,并支持Excel报告导出
|
||||||
|
|
||||||
### 6. 系统设置 ⚙️
|
### 6. 系统设置 ⚙️
|
||||||
- **API管理**: 支持多种LLM提供商配置
|
- **API管理**: 支持多种LLM提供商配置
|
||||||
- **模型参数**: 温度、最大令牌数等参数调节
|
- **模型参数**: 温度、最大令牌数等参数调节
|
||||||
- **端口配置**: Web服务和WebSocket端口管理
|
- **端口配置**: Web服务和WebSocket端口管理
|
||||||
- **日志级别**: 灵活的日志级别控制
|
- **日志级别**: 灵活的日志级别控制
|
||||||
|
- **数据库健壮性**: 优化了数据库连接配置和错误处理
|
||||||
|
|
||||||
### 7. 飞书集成 📱
|
### 7. 飞书集成 📱
|
||||||
- **多维表格同步**: 自动同步飞书多维表格数据
|
- **多维表格同步**: 自动同步飞书多维表格数据
|
||||||
@@ -377,6 +386,25 @@ TZ=Asia/Shanghai
|
|||||||
|
|
||||||
## 📝 更新日志
|
## 📝 更新日志
|
||||||
|
|
||||||
|
### v2.1.0 (2025-12-08) - 全面架构优化与问题修复
|
||||||
|
- ⚙️ **后端架构重构**:
|
||||||
|
- 将Agent、车辆数据、数据分析、API测试相关路由拆分为独立蓝图。
|
||||||
|
- 精简 `app.py` 主应用文件,提升模块化和可维护性。
|
||||||
|
- 引入统一错误处理装饰器和依赖注入机制。
|
||||||
|
- 🎨 **前端架构优化**:
|
||||||
|
- 实现了JavaScript模块化架构,划分 `core`, `services`, `components` 目录。
|
||||||
|
- 引入了统一状态管理 (`store.js`) 和API服务 (`api.js`)。
|
||||||
|
- 优化了通知管理和预警显示组件。
|
||||||
|
- 🛠️ **关键问题修复**:
|
||||||
|
- 修复了WebSocket连接中 `TypeError: missing 1 required positional argument: 'path'` 错误。
|
||||||
|
- 改进了数据库连接的健壮性,优化MySQL连接池配置,并增强了异常处理和重连机制。
|
||||||
|
- 解决了 `generator didn't stop` 错误,确保数据库会话的正确关闭。
|
||||||
|
- 增强了预警系统异常处理,并在规则检查失败时生成系统预警。
|
||||||
|
- 优化了API错误响应,包含更详细的错误信息。
|
||||||
|
- ✨ **新功能增强**:
|
||||||
|
- 为飞书同步和实时对话场景引入了不同的LLM提示词,提升AI建议的针对性。
|
||||||
|
- 增加了对`Analysising`工单状态的映射处理。
|
||||||
|
|
||||||
### v2.0.0 (2025-09-22) - Docker环境全面升级
|
### v2.0.0 (2025-09-22) - Docker环境全面升级
|
||||||
- 🐳 **Docker环境重构**: 升级到Python 3.11,优化镜像构建
|
- 🐳 **Docker环境重构**: 升级到Python 3.11,优化镜像构建
|
||||||
- 🐳 **多服务编排**: MySQL 8.0 + Redis 7 + Nginx + Prometheus + Grafana
|
- 🐳 **多服务编排**: MySQL 8.0 + Redis 7 + Nginx + Prometheus + Grafana
|
||||||
|
|||||||
524
TSP智能助手完整文档.md
524
TSP智能助手完整文档.md
@@ -1,524 +0,0 @@
|
|||||||
# TSP智能助手完整文档
|
|
||||||
|
|
||||||
## 📋 目录
|
|
||||||
- [项目概述](#项目概述)
|
|
||||||
- [系统架构](#系统架构)
|
|
||||||
- [核心功能](#核心功能)
|
|
||||||
- [技术栈](#技术栈)
|
|
||||||
- [安装部署](#安装部署)
|
|
||||||
- [配置说明](#配置说明)
|
|
||||||
- [使用指南](#使用指南)
|
|
||||||
- [API接口](#api接口)
|
|
||||||
- [数据库设计](#数据库设计)
|
|
||||||
- [开发指南](#开发指南)
|
|
||||||
- [故障排除](#故障排除)
|
|
||||||
- [更新日志](#更新日志)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 项目概述
|
|
||||||
|
|
||||||
TSP智能助手是一个基于大语言模型的智能客服系统,专为TSP(Telematics Service Provider)车辆服务提供商设计。系统集成了智能对话、工单管理、知识库、数据分析、飞书集成等核心功能。
|
|
||||||
|
|
||||||
### 核心特性
|
|
||||||
- **智能Agent架构**: 多工具集成,智能规划,自主学习
|
|
||||||
- **实时对话系统**: WebSocket支持,上下文理解,VIN识别
|
|
||||||
- **数据驱动分析**: 真实数据统计,可视化展示,系统监控
|
|
||||||
- **企业级管理**: 多环境部署,版本控制,热更新,自动备份
|
|
||||||
- **飞书集成**: 支持飞书多维表格数据同步和管理
|
|
||||||
- **AI准确率优化**: 智能判断AI建议质量,优先使用高质量内容入库
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏗️ 系统架构
|
|
||||||
|
|
||||||
### 整体架构
|
|
||||||
```
|
|
||||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
||||||
│ 前端界面 │ │ 后端服务 │ │ 数据存储 │
|
|
||||||
│ │ │ │ │ │
|
|
||||||
│ • 仪表板 │◄──►│ • Flask API │◄──►│ • MySQL DB │
|
|
||||||
│ • 智能对话 │ │ • WebSocket │ │ • 知识库 │
|
|
||||||
│ • Agent管理 │ │ • Agent核心 │ │ • 工单系统 │
|
|
||||||
│ • 数据分析 │ │ • LLM集成 │ │ • 车辆数据 │
|
|
||||||
│ • 飞书同步 │ │ • 备份系统 │ │ • SQLite备份 │
|
|
||||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 模块化设计
|
|
||||||
- **Web层**: Flask蓝图架构,模块化API设计
|
|
||||||
- **业务层**: Agent核心、对话管理、工单处理
|
|
||||||
- **数据层**: MySQL主库 + SQLite备份,ORM映射
|
|
||||||
- **集成层**: 飞书API、LLM服务、监控系统
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 核心功能
|
|
||||||
|
|
||||||
### 1. 智能对话系统 💬
|
|
||||||
- **多轮对话**: 支持上下文关联的连续对话
|
|
||||||
- **VIN识别**: 自动识别车辆VIN并获取实时数据
|
|
||||||
- **知识库检索**: 基于TF-IDF和余弦相似度的智能检索
|
|
||||||
- **工单创建**: 对话中直接创建和关联工单
|
|
||||||
|
|
||||||
### 2. Agent管理系统 🤖
|
|
||||||
- **工具管理**: 10+内置工具,支持自定义工具注册
|
|
||||||
- **执行监控**: 实时监控Agent任务执行状态
|
|
||||||
- **性能统计**: 工具使用频率和成功率分析
|
|
||||||
- **智能规划**: 基于目标的任务分解和执行
|
|
||||||
|
|
||||||
### 3. 工单管理系统 📋
|
|
||||||
- **AI建议生成**: 基于知识库生成工单处理建议
|
|
||||||
- **人工审核**: 支持人工输入和AI建议对比
|
|
||||||
- **语义相似度**: 使用sentence-transformers进行准确度评估
|
|
||||||
- **智能入库**: AI准确率<90%时优先使用人工描述入库
|
|
||||||
- **知识库更新**: 高相似度建议自动入库
|
|
||||||
|
|
||||||
### 4. 知识库管理 📚
|
|
||||||
- **多格式支持**: TXT、PDF、DOC、DOCX、MD文件
|
|
||||||
- **智能提取**: 自动从文档中提取Q&A对
|
|
||||||
- **向量化检索**: TF-IDF + 余弦相似度搜索
|
|
||||||
- **质量验证**: 支持知识条目验证和置信度设置
|
|
||||||
|
|
||||||
### 5. 数据分析系统 📊
|
|
||||||
- **实时趋势**: 基于真实数据的性能趋势分析
|
|
||||||
- **多维度统计**: 工单、预警、满意度等关键指标
|
|
||||||
- **系统健康**: CPU、内存、响应时间监控
|
|
||||||
- **可视化展示**: 丰富的图表和仪表板
|
|
||||||
|
|
||||||
### 6. 飞书集成系统 📱
|
|
||||||
- **多维表格同步**: 自动同步飞书多维表格数据
|
|
||||||
- **字段映射**: 智能映射飞书字段到本地数据库
|
|
||||||
- **实时更新**: 支持增量同步和全量同步
|
|
||||||
- **数据预览**: 同步前预览数据,确保准确性
|
|
||||||
|
|
||||||
### 7. 系统设置管理 ⚙️
|
|
||||||
- **API管理**: 支持多种LLM提供商配置
|
|
||||||
- **模型参数**: 温度、最大令牌数等参数调节
|
|
||||||
- **端口配置**: Web服务和WebSocket端口管理
|
|
||||||
- **日志级别**: 灵活的日志级别控制
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ 技术栈
|
|
||||||
|
|
||||||
### 后端技术
|
|
||||||
- **Python 3.8+**: 核心开发语言
|
|
||||||
- **Flask**: Web框架和API服务
|
|
||||||
- **SQLAlchemy**: ORM数据库操作
|
|
||||||
- **WebSocket**: 实时通信支持
|
|
||||||
- **psutil**: 系统资源监控
|
|
||||||
|
|
||||||
### 前端技术
|
|
||||||
- **Bootstrap 5**: UI框架
|
|
||||||
- **Chart.js**: 数据可视化
|
|
||||||
- **JavaScript ES6+**: 前端逻辑
|
|
||||||
- **WebSocket**: 实时通信客户端
|
|
||||||
|
|
||||||
### AI/ML技术
|
|
||||||
- **大语言模型**: 支持OpenAI、通义千问等
|
|
||||||
- **sentence-transformers**: 语义相似度计算
|
|
||||||
- **TF-IDF**: 文本向量化
|
|
||||||
- **余弦相似度**: 语义相似度计算
|
|
||||||
- **Agent框架**: 智能任务规划
|
|
||||||
|
|
||||||
### 部署运维
|
|
||||||
- **Docker**: 容器化部署
|
|
||||||
- **Nginx**: 反向代理和静态文件服务
|
|
||||||
- **Systemd**: 服务管理
|
|
||||||
- **Git**: 版本控制
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 安装部署
|
|
||||||
|
|
||||||
### 环境要求
|
|
||||||
- Python 3.8+
|
|
||||||
- Node.js 16+ (可选,用于前端构建)
|
|
||||||
- Git
|
|
||||||
- MySQL 8.0+
|
|
||||||
|
|
||||||
### 安装步骤
|
|
||||||
|
|
||||||
1. **克隆项目**
|
|
||||||
```bash
|
|
||||||
git clone http://jeason.online:3000/zhaojie/assist.git
|
|
||||||
cd assist
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **安装依赖**
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **配置数据库**
|
|
||||||
```bash
|
|
||||||
# 创建MySQL数据库
|
|
||||||
mysql -u root -p
|
|
||||||
CREATE DATABASE tsp_assistant CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **初始化数据库**
|
|
||||||
```bash
|
|
||||||
python init_database.py
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **启动服务**
|
|
||||||
```bash
|
|
||||||
python start_dashboard.py
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **访问系统**
|
|
||||||
- 打开浏览器访问: `http://localhost:5000`
|
|
||||||
- 默认端口: 5000 (可在系统设置中修改)
|
|
||||||
|
|
||||||
### Windows快速启动
|
|
||||||
```cmd
|
|
||||||
# 双击运行
|
|
||||||
快速启动.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚙️ 配置说明
|
|
||||||
|
|
||||||
### 环境变量
|
|
||||||
```bash
|
|
||||||
# 数据库配置
|
|
||||||
DATABASE_URL=mysql+pymysql://user:password@host:port/database
|
|
||||||
|
|
||||||
# LLM配置
|
|
||||||
LLM_PROVIDER=openai
|
|
||||||
LLM_API_KEY=your_api_key
|
|
||||||
LLM_MODEL=gpt-3.5-turbo
|
|
||||||
|
|
||||||
# 服务配置
|
|
||||||
SERVER_PORT=5000
|
|
||||||
WEBSOCKET_PORT=8765
|
|
||||||
LOG_LEVEL=INFO
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置文件结构
|
|
||||||
```
|
|
||||||
config/
|
|
||||||
├── llm_config.py # LLM客户端配置
|
|
||||||
├── integrations_config.json # 飞书集成配置
|
|
||||||
├── ai_accuracy_config.py # AI准确率配置
|
|
||||||
└── README.md # 配置说明文档
|
|
||||||
```
|
|
||||||
|
|
||||||
### 飞书集成配置
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"feishu": {
|
|
||||||
"app_id": "cli_a8b50ec0eed1500d",
|
|
||||||
"app_secret": "ccxkE7ZCFQZcwkkM1rLy0ccZRXYsT2xK",
|
|
||||||
"app_token": "XXnEbiCmEaMblSs6FDJcFCqsnIg",
|
|
||||||
"table_id": "tblnl3vJPpgMTSiP",
|
|
||||||
"status": "active"
|
|
||||||
},
|
|
||||||
"system": {
|
|
||||||
"sync_limit": 10,
|
|
||||||
"ai_suggestions_enabled": true,
|
|
||||||
"auto_sync_interval": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### AI准确率配置
|
|
||||||
```python
|
|
||||||
# 默认配置
|
|
||||||
auto_approve_threshold = 0.95 # 自动审批阈值
|
|
||||||
use_human_resolution_threshold = 0.90 # 使用人工描述阈值
|
|
||||||
manual_review_threshold = 0.80 # 人工审核阈值
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 使用指南
|
|
||||||
|
|
||||||
### 基础操作
|
|
||||||
|
|
||||||
#### 1. 智能对话
|
|
||||||
1. 在"智能对话"页面输入问题
|
|
||||||
2. 系统自动检索知识库并生成回答
|
|
||||||
3. 支持VIN码识别和车辆数据查询
|
|
||||||
|
|
||||||
#### 2. 工单管理
|
|
||||||
1. 创建工单并获取AI建议
|
|
||||||
2. 输入人工处理描述
|
|
||||||
3. 系统自动计算语义相似度
|
|
||||||
4. 根据相似度决定入库策略
|
|
||||||
|
|
||||||
#### 3. 知识库维护
|
|
||||||
1. 手动添加Q&A对
|
|
||||||
2. 上传文档自动提取知识
|
|
||||||
3. 设置置信度和验证状态
|
|
||||||
|
|
||||||
#### 4. 飞书数据同步
|
|
||||||
1. 配置飞书应用凭证
|
|
||||||
2. 在主仪表板"飞书同步"标签页
|
|
||||||
3. 测试连接并执行数据同步
|
|
||||||
|
|
||||||
### 高级功能
|
|
||||||
|
|
||||||
#### 1. Agent工具管理
|
|
||||||
- 查看工具使用统计
|
|
||||||
- 注册自定义工具
|
|
||||||
- 监控执行历史
|
|
||||||
|
|
||||||
#### 2. 数据分析
|
|
||||||
- 多维度数据统计
|
|
||||||
- 自定义时间范围
|
|
||||||
- 导出分析报告
|
|
||||||
|
|
||||||
#### 3. 系统配置
|
|
||||||
- API和模型参数配置
|
|
||||||
- 端口和日志级别设置
|
|
||||||
- 环境变量管理
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔌 API接口
|
|
||||||
|
|
||||||
### 工单管理API
|
|
||||||
```http
|
|
||||||
POST /api/workorders/{id}/ai-suggestion
|
|
||||||
POST /api/workorders/{id}/human-resolution
|
|
||||||
POST /api/workorders/{id}/approve-to-knowledge
|
|
||||||
GET /api/workorders
|
|
||||||
POST /api/workorders
|
|
||||||
PUT /api/workorders/{id}
|
|
||||||
DELETE /api/workorders/{id}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 知识库API
|
|
||||||
```http
|
|
||||||
GET /api/knowledge
|
|
||||||
POST /api/knowledge
|
|
||||||
PUT /api/knowledge/{id}
|
|
||||||
DELETE /api/knowledge/{id}
|
|
||||||
POST /api/knowledge/search
|
|
||||||
POST /api/knowledge/upload
|
|
||||||
```
|
|
||||||
|
|
||||||
### 对话API
|
|
||||||
```http
|
|
||||||
POST /api/chat/session
|
|
||||||
GET /api/chat/session/{id}
|
|
||||||
DELETE /api/chat/session/{id}
|
|
||||||
POST /api/chat/message
|
|
||||||
```
|
|
||||||
|
|
||||||
### Agent管理API
|
|
||||||
```http
|
|
||||||
GET /api/agent/status
|
|
||||||
POST /api/agent/tools/execute
|
|
||||||
POST /api/agent/tools/register
|
|
||||||
GET /api/agent/tools/stats
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🗄️ 数据库设计
|
|
||||||
|
|
||||||
### 核心表结构
|
|
||||||
|
|
||||||
#### work_orders (工单表)
|
|
||||||
```sql
|
|
||||||
CREATE TABLE work_orders (
|
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
order_id VARCHAR(50) UNIQUE NOT NULL,
|
|
||||||
title VARCHAR(200) NOT NULL,
|
|
||||||
description TEXT NOT NULL,
|
|
||||||
category VARCHAR(100) NOT NULL,
|
|
||||||
priority VARCHAR(20) NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
resolution TEXT,
|
|
||||||
satisfaction_score FLOAT,
|
|
||||||
|
|
||||||
-- 飞书集成字段
|
|
||||||
feishu_record_id VARCHAR(100),
|
|
||||||
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),
|
|
||||||
app_remote_control_version VARCHAR(100),
|
|
||||||
hmi_sw VARCHAR(100),
|
|
||||||
parent_record VARCHAR(100),
|
|
||||||
has_updated_same_day VARCHAR(50),
|
|
||||||
operating_time VARCHAR(100)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### work_order_suggestions (工单建议表)
|
|
||||||
```sql
|
|
||||||
CREATE TABLE work_order_suggestions (
|
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
work_order_id INT NOT NULL,
|
|
||||||
ai_suggestion TEXT,
|
|
||||||
human_resolution TEXT,
|
|
||||||
ai_similarity FLOAT,
|
|
||||||
approved BOOLEAN DEFAULT FALSE,
|
|
||||||
use_human_resolution BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (work_order_id) REFERENCES work_orders(id)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### knowledge_entries (知识库表)
|
|
||||||
```sql
|
|
||||||
CREATE TABLE knowledge_entries (
|
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
question TEXT NOT NULL,
|
|
||||||
answer TEXT NOT NULL,
|
|
||||||
category VARCHAR(100),
|
|
||||||
confidence_score FLOAT DEFAULT 0.5,
|
|
||||||
usage_count INT DEFAULT 0,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
is_verified BOOLEAN DEFAULT FALSE,
|
|
||||||
verified_by VARCHAR(100),
|
|
||||||
verified_at DATETIME,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 👨💻 开发指南
|
|
||||||
|
|
||||||
### 项目结构
|
|
||||||
```
|
|
||||||
tsp-assistant/
|
|
||||||
├── src/ # 源代码
|
|
||||||
│ ├── agent/ # Agent核心模块
|
|
||||||
│ ├── analytics/ # 数据分析模块
|
|
||||||
│ ├── config/ # 配置模块
|
|
||||||
│ ├── core/ # 核心模块
|
|
||||||
│ ├── dialogue/ # 对话模块
|
|
||||||
│ ├── integrations/ # 集成模块
|
|
||||||
│ ├── knowledge_base/ # 知识库模块
|
|
||||||
│ ├── utils/ # 工具模块
|
|
||||||
│ ├── vehicle/ # 车辆数据模块
|
|
||||||
│ └── web/ # Web应用模块
|
|
||||||
├── config/ # 配置文件
|
|
||||||
├── scripts/ # 脚本文件
|
|
||||||
├── uploads/ # 上传文件
|
|
||||||
├── requirements.txt # 依赖文件
|
|
||||||
├── init_database.py # 数据库初始化
|
|
||||||
└── start_dashboard.py # 启动脚本
|
|
||||||
```
|
|
||||||
|
|
||||||
### 代码规范
|
|
||||||
- **Python**: 遵循PEP 8规范
|
|
||||||
- **JavaScript**: 使用ES6+语法
|
|
||||||
- **提交信息**: 使用约定式提交格式
|
|
||||||
- **文档**: 新功能需要添加相应的文档
|
|
||||||
|
|
||||||
### 开发流程
|
|
||||||
1. Fork项目到个人仓库
|
|
||||||
2. 创建功能分支: `git checkout -b feature/new-feature`
|
|
||||||
3. 提交更改: `git commit -m "Add new feature"`
|
|
||||||
4. 推送分支: `git push origin feature/new-feature`
|
|
||||||
5. 创建Pull Request
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚨 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
#### 1. 数据库连接失败
|
|
||||||
```bash
|
|
||||||
# 检查数据库服务状态
|
|
||||||
systemctl status mysql
|
|
||||||
|
|
||||||
# 检查连接配置
|
|
||||||
python -c "from src.core.database import db_manager; print(db_manager.test_connection())"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. 飞书集成问题
|
|
||||||
- 检查飞书应用权限配置
|
|
||||||
- 验证app_token和table_id是否正确
|
|
||||||
- 确认网络连接和API访问权限
|
|
||||||
|
|
||||||
#### 3. AI建议生成失败
|
|
||||||
- 检查LLM API配置
|
|
||||||
- 验证知识库数据完整性
|
|
||||||
- 查看应用日志
|
|
||||||
|
|
||||||
#### 4. 数据库字段缺失
|
|
||||||
```bash
|
|
||||||
# 运行数据库迁移
|
|
||||||
python init_database.py
|
|
||||||
|
|
||||||
# 手动添加字段
|
|
||||||
mysql -u root -p tsp_assistant
|
|
||||||
ALTER TABLE work_order_suggestions ADD COLUMN use_human_resolution BOOLEAN DEFAULT FALSE;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 日志位置
|
|
||||||
- **应用日志**: `logs/tsp_assistant.log`
|
|
||||||
- **访问日志**: Nginx访问日志
|
|
||||||
- **错误追踪**: 详细的错误堆栈信息
|
|
||||||
|
|
||||||
### 性能优化
|
|
||||||
- **数据库索引**: 为常用查询字段添加索引
|
|
||||||
- **缓存策略**: 使用Redis缓存热点数据
|
|
||||||
- **异步处理**: 耗时操作使用异步处理
|
|
||||||
- **连接池**: 配置数据库连接池
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 更新日志
|
|
||||||
|
|
||||||
### v1.4.0 (2025-09-19)
|
|
||||||
- ✅ 飞书集成功能:支持飞书多维表格数据同步
|
|
||||||
- ✅ 页面功能合并:飞书同步页面合并到主仪表板
|
|
||||||
- ✅ 数据库架构优化:扩展工单表字段,支持飞书数据
|
|
||||||
- ✅ 代码重构优化:大文件拆分,降低运行风险
|
|
||||||
- ✅ 字段映射完善:智能映射飞书字段到本地数据库
|
|
||||||
- ✅ 数据库初始化改进:集成字段迁移到初始化流程
|
|
||||||
- ✅ AI准确率优化:AI准确率<90%时优先使用人工描述入库
|
|
||||||
- ✅ 语义相似度计算:使用sentence-transformers提升准确度
|
|
||||||
|
|
||||||
### v1.3.0 (2025-09-17)
|
|
||||||
- ✅ 数据库架构优化:MySQL主数据库+SQLite备份系统
|
|
||||||
- ✅ 工单详情API修复:解决数据库会话管理问题
|
|
||||||
- ✅ 备份管理系统:自动备份MySQL数据到SQLite
|
|
||||||
- ✅ 数据库状态监控:实时监控MySQL和SQLite状态
|
|
||||||
- ✅ 备份管理API:支持数据备份和恢复操作
|
|
||||||
|
|
||||||
### v1.2.0 (2025-09-16)
|
|
||||||
- ✅ 系统设置扩展:API管理、模型参数配置、端口管理
|
|
||||||
- ✅ 真实数据分析:修复性能趋势图表显示问题
|
|
||||||
- ✅ 工单AI建议功能:智能生成处理建议
|
|
||||||
- ✅ 知识库搜索优化:提升检索准确率
|
|
||||||
- ✅ Agent管理改进:工具使用统计和自定义工具
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 许可证
|
|
||||||
|
|
||||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情
|
|
||||||
|
|
||||||
## 📞 支持与联系
|
|
||||||
|
|
||||||
- **项目地址**: http://jeason.online:3000/zhaojie/assist
|
|
||||||
- **问题反馈**: 请在Issues中提交问题
|
|
||||||
- **功能建议**: 欢迎提交Feature Request
|
|
||||||
|
|
||||||
## 🙏 致谢
|
|
||||||
|
|
||||||
感谢所有为项目做出贡献的开发者和用户!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**TSP智能助手** - 让车辆服务更智能,让客户体验更美好! 🚗✨
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
# UTF-8 编码规范
|
|
||||||
|
|
||||||
## 项目编码标准
|
|
||||||
|
|
||||||
本项目所有文件必须使用 **UTF-8** 编码格式,以确保中文和特殊字符的正确显示和处理。
|
|
||||||
|
|
||||||
## 文件编码要求
|
|
||||||
|
|
||||||
### 1. Python 文件
|
|
||||||
- **必须** 在文件开头添加编码声明:
|
|
||||||
```python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
```
|
|
||||||
或
|
|
||||||
```python
|
|
||||||
# coding: utf-8
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 文件保存
|
|
||||||
- 所有文件保存时使用 **UTF-8** 编码(无BOM)
|
|
||||||
- 禁止使用 GBK、GB2312 等其他编码格式
|
|
||||||
|
|
||||||
### 3. 文件读取/写入
|
|
||||||
- 所有文件操作必须明确指定 `encoding='utf-8'`:
|
|
||||||
```python
|
|
||||||
with open('file.txt', 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
with open('file.txt', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(content)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cursor/VS Code 配置
|
|
||||||
|
|
||||||
项目已配置 `.vscode/settings.json`,确保:
|
|
||||||
- 默认文件编码:UTF-8
|
|
||||||
- 自动检测编码:禁用(避免误判)
|
|
||||||
- 文件行尾:LF(Unix风格)
|
|
||||||
|
|
||||||
## 控制台输出
|
|
||||||
|
|
||||||
### Windows 系统
|
|
||||||
在 Python 脚本中,需要设置标准输出编码:
|
|
||||||
```python
|
|
||||||
import sys
|
|
||||||
import io
|
|
||||||
|
|
||||||
if sys.platform == 'win32':
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
|
||||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
|
||||||
```
|
|
||||||
|
|
||||||
## 检查脚本
|
|
||||||
|
|
||||||
使用 `check_encoding.py` 脚本检查所有文件的编码格式:
|
|
||||||
```bash
|
|
||||||
python check_encoding.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
### 1. 控制台输出乱码
|
|
||||||
- 确保文件以 UTF-8 保存
|
|
||||||
- 在脚本开头设置标准输出编码
|
|
||||||
- Windows 系统运行 `chcp 65001` 设置控制台代码页
|
|
||||||
|
|
||||||
### 2. 文件读取乱码
|
|
||||||
- 检查文件实际编码(可用 `check_encoding.py`)
|
|
||||||
- 确保使用 `encoding='utf-8'` 参数
|
|
||||||
|
|
||||||
### 3. 文件保存乱码
|
|
||||||
- 检查编辑器编码设置
|
|
||||||
- 确保 Cursor/VS Code 设置为 UTF-8
|
|
||||||
|
|
||||||
## 验证清单
|
|
||||||
|
|
||||||
创建新文件时,请确认:
|
|
||||||
- [ ] 文件以 UTF-8 编码保存
|
|
||||||
- [ ] Python 文件包含编码声明
|
|
||||||
- [ ] 文件读写操作指定 `encoding='utf-8'`
|
|
||||||
- [ ] 控制台输出脚本设置了 UTF-8 编码
|
|
||||||
- [ ] 测试输出中文显示正常
|
|
||||||
|
|
||||||
@@ -200,56 +200,18 @@ echo ✅ 提交成功
|
|||||||
echo.
|
echo.
|
||||||
echo [4/4] 推送到远程仓库...
|
echo [4/4] 推送到远程仓库...
|
||||||
|
|
||||||
:: 获取当前分支名称(在延迟变量扩展内)
|
|
||||||
set current_branch=
|
|
||||||
for /f "tokens=*" %%b in ('git branch --show-current 2^>nul') do set current_branch=%%b
|
|
||||||
|
|
||||||
if "!current_branch!"=="" (
|
|
||||||
echo ❌ 无法获取当前分支名称
|
|
||||||
echo 尝试使用默认分支 main...
|
|
||||||
set current_branch=main
|
|
||||||
) else (
|
|
||||||
echo 📍 当前分支: !current_branch!
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 先尝试拉取最新更改
|
:: 先尝试拉取最新更改
|
||||||
echo 🔄 检查远程更新...
|
echo 🔄 检查远程更新...
|
||||||
git fetch origin !current_branch! >nul 2>&1
|
git fetch origin main
|
||||||
set fetch_result=!errorlevel!
|
if %errorlevel% neq 0 (
|
||||||
if !fetch_result! neq 0 (
|
|
||||||
echo ⚠️ 无法获取远程更新,尝试获取所有分支...
|
|
||||||
git fetch origin >nul 2>&1
|
|
||||||
set fetch_all_result=!errorlevel!
|
|
||||||
if !fetch_all_result! neq 0 (
|
|
||||||
echo ⚠️ 无法获取远程更新,继续推送...
|
echo ⚠️ 无法获取远程更新,继续推送...
|
||||||
) else (
|
) else (
|
||||||
echo ✅ 远程更新检查完成
|
echo ✅ 远程更新检查完成
|
||||||
)
|
)
|
||||||
) else (
|
|
||||||
echo ✅ 远程更新检查完成
|
|
||||||
)
|
|
||||||
|
|
||||||
:: 检查远程分支是否存在,如果不存在则设置上游
|
:: 推送到远程
|
||||||
echo 🔍 检查远程分支状态...
|
git push origin main
|
||||||
git ls-remote --heads origin !current_branch! >nul 2>&1
|
if %errorlevel% neq 0 (
|
||||||
set remote_exists=!errorlevel!
|
|
||||||
set push_result=0
|
|
||||||
|
|
||||||
if !remote_exists! equ 0 (
|
|
||||||
echo 远程分支 !current_branch! 已存在
|
|
||||||
:: 推送到远程(分支已存在)
|
|
||||||
git push origin !current_branch!
|
|
||||||
set push_result=!errorlevel!
|
|
||||||
) else (
|
|
||||||
echo 远程分支 !current_branch! 不存在,将创建并设置上游
|
|
||||||
:: 推送到远程并设置上游(分支不存在)
|
|
||||||
git push -u origin !current_branch!
|
|
||||||
set push_result=!errorlevel!
|
|
||||||
)
|
|
||||||
|
|
||||||
if !push_result! neq 0 (
|
|
||||||
echo ❌ 推送失败
|
echo ❌ 推送失败
|
||||||
echo.
|
echo.
|
||||||
echo 💡 可能的原因:
|
echo 💡 可能的原因:
|
||||||
@@ -259,20 +221,18 @@ if !push_result! neq 0 (
|
|||||||
echo - 需要先拉取远程更改
|
echo - 需要先拉取远程更改
|
||||||
echo.
|
echo.
|
||||||
echo 🔧 尝试自动解决冲突...
|
echo 🔧 尝试自动解决冲突...
|
||||||
git pull origin !current_branch! --rebase
|
git pull origin main --rebase
|
||||||
set pull_result=!errorlevel!
|
if %errorlevel% equ 0 (
|
||||||
if !pull_result! equ 0 (
|
|
||||||
echo ✅ 冲突已解决,重新推送...
|
echo ✅ 冲突已解决,重新推送...
|
||||||
git push origin !current_branch!
|
git push origin main
|
||||||
set final_push_result=!errorlevel!
|
if %errorlevel% equ 0 (
|
||||||
if !final_push_result! equ 0 (
|
|
||||||
echo ✅ 推送成功!
|
echo ✅ 推送成功!
|
||||||
) else (
|
) else (
|
||||||
echo ❌ 重新推送失败
|
echo ❌ 重新推送失败
|
||||||
echo.
|
echo.
|
||||||
echo 🔧 建议手动解决:
|
echo 🔧 建议手动解决:
|
||||||
echo 1. 运行: git pull origin !current_branch!
|
echo 1. 运行: git pull origin main
|
||||||
echo 2. 解决冲突后运行: git push origin !current_branch!
|
echo 2. 解决冲突后运行: git push origin main
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
@@ -280,8 +240,8 @@ if !push_result! neq 0 (
|
|||||||
echo ❌ 无法自动解决冲突
|
echo ❌ 无法自动解决冲突
|
||||||
echo.
|
echo.
|
||||||
echo 🔧 建议手动解决:
|
echo 🔧 建议手动解决:
|
||||||
echo 1. 运行: git pull origin !current_branch!
|
echo 1. 运行: git pull origin main
|
||||||
echo 2. 解决冲突后运行: git push origin !current_branch!
|
echo 2. 解决冲突后运行: git push origin main
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|||||||
268
auto_push.ps1
268
auto_push.ps1
@@ -1,268 +0,0 @@
|
|||||||
# TSP智能助手 - 自动推送脚本 (PowerShell版本)
|
|
||||||
# 使用方法: .\auto_push.ps1 [提交信息]
|
|
||||||
|
|
||||||
param(
|
|
||||||
[string]$CommitMessage = "",
|
|
||||||
[switch]$Force = $false,
|
|
||||||
[switch]$NoConfirm = $false
|
|
||||||
)
|
|
||||||
|
|
||||||
# 设置控制台编码
|
|
||||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
||||||
|
|
||||||
# 颜色输出函数
|
|
||||||
function Write-ColorOutput {
|
|
||||||
param(
|
|
||||||
[string]$Message,
|
|
||||||
[string]$Color = "White"
|
|
||||||
)
|
|
||||||
Write-Host $Message -ForegroundColor $Color
|
|
||||||
}
|
|
||||||
|
|
||||||
function Write-Step {
|
|
||||||
param(
|
|
||||||
[int]$Step,
|
|
||||||
[int]$Total,
|
|
||||||
[string]$Message
|
|
||||||
)
|
|
||||||
Write-ColorOutput "[$Step/$Total] $Message" "Cyan"
|
|
||||||
}
|
|
||||||
|
|
||||||
function Show-GitStatus {
|
|
||||||
Write-ColorOutput "`n📋 当前Git状态:" "Yellow"
|
|
||||||
$status = git status --porcelain
|
|
||||||
if ($status) {
|
|
||||||
$status | ForEach-Object {
|
|
||||||
$line = $_
|
|
||||||
if ($line.StartsWith("??")) {
|
|
||||||
Write-ColorOutput " + $($line.Substring(3))" "Green"
|
|
||||||
} elseif ($line.StartsWith(" M")) {
|
|
||||||
Write-ColorOutput " ~ $($line.Substring(3))" "Yellow"
|
|
||||||
} elseif ($line.StartsWith(" D")) {
|
|
||||||
Write-ColorOutput " - $($line.Substring(3))" "Red"
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput " $line" "White"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput " 工作区干净,无更改" "Green"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-CommitMessage {
|
|
||||||
if ($CommitMessage) {
|
|
||||||
return $CommitMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
# 尝试从最近提交生成智能消息
|
|
||||||
$lastCommit = git log --oneline -1 2>$null
|
|
||||||
if ($lastCommit) {
|
|
||||||
$suggested = "feat: 自动提交更新 - $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
|
|
||||||
} else {
|
|
||||||
$suggested = "feat: 初始提交 - $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-ColorOutput "`n💡 建议的提交信息: $suggested" "Cyan"
|
|
||||||
$custom = Read-Host "请输入自定义提交信息 (直接回车使用建议)"
|
|
||||||
|
|
||||||
if ($custom) {
|
|
||||||
return $custom
|
|
||||||
} else {
|
|
||||||
return $suggested
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Test-GitRepository {
|
|
||||||
try {
|
|
||||||
git status --porcelain >$null 2>&1
|
|
||||||
return $LASTEXITCODE -eq 0
|
|
||||||
} catch {
|
|
||||||
return $false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Show-CommitStats {
|
|
||||||
Write-ColorOutput "`n📊 提交统计:" "Yellow"
|
|
||||||
$commitHash = git log --oneline -1 | Select-String -Pattern "^\w+" | ForEach-Object { $_.Matches[0].Value }
|
|
||||||
$filesChanged = git diff --cached --numstat | Measure-Object | Select-Object -ExpandProperty Count
|
|
||||||
$insertions = git diff --cached --numstat | ForEach-Object {
|
|
||||||
$parts = $_ -split '\s+'
|
|
||||||
if ($parts[0] -match '^\d+$') { [int]$parts[0] } else { 0 }
|
|
||||||
} | Measure-Object -Sum | Select-Object -ExpandProperty Sum
|
|
||||||
$deletions = git diff --cached --numstat | ForEach-Object {
|
|
||||||
$parts = $_ -split '\s+'
|
|
||||||
if ($parts[1] -match '^\d+$') { [int]$parts[1] } else { 0 }
|
|
||||||
} | Measure-Object -Sum | Select-Object -ExpandProperty Sum
|
|
||||||
|
|
||||||
Write-ColorOutput " 提交哈希: $commitHash" "White"
|
|
||||||
Write-ColorOutput " 文件变更: $filesChanged 个文件" "White"
|
|
||||||
Write-ColorOutput " 新增行数: $insertions 行" "Green"
|
|
||||||
Write-ColorOutput " 删除行数: $deletions 行" "Red"
|
|
||||||
Write-ColorOutput " 净增加: $($insertions - $deletions) 行" "Cyan"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 主程序开始
|
|
||||||
Write-ColorOutput "========================================" "Magenta"
|
|
||||||
Write-ColorOutput "TSP智能助手 - 自动推送脚本 v2.0" "Magenta"
|
|
||||||
Write-ColorOutput "========================================" "Magenta"
|
|
||||||
|
|
||||||
# 检查Git仓库
|
|
||||||
Write-Step 1 4 "检查Git仓库状态"
|
|
||||||
if (-not (Test-GitRepository)) {
|
|
||||||
Write-ColorOutput "❌ Git未初始化或不在Git仓库中" "Red"
|
|
||||||
Write-ColorOutput "请确保在正确的Git仓库目录中运行此脚本" "Yellow"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-ColorOutput "✅ Git仓库状态正常" "Green"
|
|
||||||
|
|
||||||
# 显示状态
|
|
||||||
Show-GitStatus
|
|
||||||
|
|
||||||
# 检查是否有更改
|
|
||||||
$hasChanges = git status --porcelain | Measure-Object | Select-Object -ExpandProperty Count
|
|
||||||
if ($hasChanges -eq 0) {
|
|
||||||
Write-ColorOutput "`n✅ 工作区干净,无需提交" "Green"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# 显示详细状态
|
|
||||||
Write-ColorOutput "`n📊 详细状态信息:" "Yellow"
|
|
||||||
|
|
||||||
# 检查未暂存的更改
|
|
||||||
$unstaged = git diff --name-only 2>$null
|
|
||||||
if ($unstaged) {
|
|
||||||
Write-ColorOutput " 未暂存更改: $($unstaged.Count) 个文件" "Yellow"
|
|
||||||
$unstaged | ForEach-Object { Write-ColorOutput " ~ $_" "Yellow" }
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput " 未暂存更改: 无" "Green"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 检查已暂存的更改
|
|
||||||
$staged = git diff --cached --name-only 2>$null
|
|
||||||
if ($staged) {
|
|
||||||
Write-ColorOutput " 已暂存更改: $($staged.Count) 个文件" "Green"
|
|
||||||
$staged | ForEach-Object { Write-ColorOutput " + $_" "Green" }
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput " 已暂存更改: 无" "Green"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 检查未跟踪的文件
|
|
||||||
$untracked = git ls-files --others --exclude-standard 2>$null
|
|
||||||
if ($untracked) {
|
|
||||||
Write-ColorOutput " 未跟踪文件: $($untracked.Count) 个文件" "Cyan"
|
|
||||||
$untracked | ForEach-Object { Write-ColorOutput " + $_" "Cyan" }
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput " 未跟踪文件: 无" "Green"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 确认操作
|
|
||||||
if (-not $NoConfirm) {
|
|
||||||
Write-ColorOutput "`n❓ 是否继续推送?" "Yellow"
|
|
||||||
$confirm = Read-Host "输入 'y' 继续,其他键取消"
|
|
||||||
if ($confirm -ne 'y') {
|
|
||||||
Write-ColorOutput "操作已取消" "Yellow"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 添加文件
|
|
||||||
Write-Step 2 4 "添加所有更改到暂存区"
|
|
||||||
try {
|
|
||||||
git add .
|
|
||||||
Write-ColorOutput "✅ 文件已添加到暂存区" "Green"
|
|
||||||
} catch {
|
|
||||||
Write-ColorOutput "❌ 添加文件失败: $($_.Exception.Message)" "Red"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 生成提交信息
|
|
||||||
Write-Step 3 4 "生成提交信息"
|
|
||||||
$finalCommitMessage = Get-CommitMessage
|
|
||||||
Write-ColorOutput "提交信息: $finalCommitMessage" "Cyan"
|
|
||||||
|
|
||||||
# 提交更改
|
|
||||||
try {
|
|
||||||
git commit -m $finalCommitMessage
|
|
||||||
Write-ColorOutput "✅ 提交成功" "Green"
|
|
||||||
} catch {
|
|
||||||
Write-ColorOutput "❌ 提交失败: $($_.Exception.Message)" "Red"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 推送到远程
|
|
||||||
Write-Step 4 4 "推送到远程仓库"
|
|
||||||
|
|
||||||
# 先尝试拉取最新更改
|
|
||||||
Write-ColorOutput "🔄 检查远程更新..." "Cyan"
|
|
||||||
try {
|
|
||||||
git fetch origin main
|
|
||||||
Write-ColorOutput "✅ 远程更新检查完成" "Green"
|
|
||||||
} catch {
|
|
||||||
Write-ColorOutput "⚠️ 无法获取远程更新,继续推送..." "Yellow"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 推送到远程
|
|
||||||
try {
|
|
||||||
git push origin main
|
|
||||||
Write-ColorOutput "✅ 推送成功" "Green"
|
|
||||||
} catch {
|
|
||||||
Write-ColorOutput "❌ 推送失败: $($_.Exception.Message)" "Red"
|
|
||||||
Write-ColorOutput "`n💡 可能的原因:" "Yellow"
|
|
||||||
Write-ColorOutput " - 网络连接问题" "White"
|
|
||||||
Write-ColorOutput " - 远程仓库权限不足" "White"
|
|
||||||
Write-ColorOutput " - 分支冲突" "White"
|
|
||||||
Write-ColorOutput " - 需要先拉取远程更改" "White"
|
|
||||||
|
|
||||||
Write-ColorOutput "`n🔧 尝试自动解决冲突..." "Cyan"
|
|
||||||
try {
|
|
||||||
git pull origin main --rebase
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-ColorOutput "✅ 冲突已解决,重新推送..." "Green"
|
|
||||||
git push origin main
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-ColorOutput "✅ 推送成功!" "Green"
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput "❌ 重新推送失败" "Red"
|
|
||||||
Write-ColorOutput "`n🔧 建议手动解决:" "Yellow"
|
|
||||||
Write-ColorOutput " 1. 运行: git pull origin main" "White"
|
|
||||||
Write-ColorOutput " 2. 解决冲突后运行: git push origin main" "White"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Write-ColorOutput "❌ 无法自动解决冲突" "Red"
|
|
||||||
Write-ColorOutput "`n🔧 建议手动解决:" "Yellow"
|
|
||||||
Write-ColorOutput " 1. 运行: git pull origin main" "White"
|
|
||||||
Write-ColorOutput " 2. 解决冲突后运行: git push origin main" "White"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-ColorOutput "❌ 自动解决冲突失败: $($_.Exception.Message)" "Red"
|
|
||||||
Write-ColorOutput "`n🔧 建议手动解决:" "Yellow"
|
|
||||||
Write-ColorOutput " 1. 运行: git pull origin main" "White"
|
|
||||||
Write-ColorOutput " 2. 解决冲突后运行: git push origin main" "White"
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 显示结果
|
|
||||||
Write-ColorOutput "`n========================================" "Magenta"
|
|
||||||
Write-ColorOutput "✅ 推送完成!" "Green"
|
|
||||||
Write-ColorOutput "========================================" "Magenta"
|
|
||||||
|
|
||||||
Show-CommitStats
|
|
||||||
|
|
||||||
Write-ColorOutput "`n🌐 远程仓库状态:" "Yellow"
|
|
||||||
git status --short
|
|
||||||
|
|
||||||
Write-ColorOutput "`n🎉 所有操作完成!" "Green"
|
|
||||||
if (-not $NoConfirm) {
|
|
||||||
Read-Host "按任意键退出"
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
from src.core.database import db_manager
|
|
||||||
from sqlalchemy import text, inspect
|
|
||||||
|
|
||||||
print("Checking users table structure...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
with db_manager.get_session() as session:
|
|
||||||
inspector = inspect(db_manager.engine)
|
|
||||||
cols = inspector.get_columns('users')
|
|
||||||
|
|
||||||
print("\nUsers table columns:")
|
|
||||||
required_fields = {}
|
|
||||||
optional_fields = {}
|
|
||||||
|
|
||||||
for col in cols:
|
|
||||||
name = col['name']
|
|
||||||
nullable = col.get('nullable', True)
|
|
||||||
default = col.get('default', None)
|
|
||||||
|
|
||||||
if nullable or default is not None:
|
|
||||||
optional_fields[name] = col
|
|
||||||
print(f" {name}: {col['type']} (nullable: {nullable}, default: {default})")
|
|
||||||
else:
|
|
||||||
required_fields[name] = col
|
|
||||||
print(f" {name}: {col['type']} (REQUIRED, nullable: {nullable})")
|
|
||||||
|
|
||||||
print(f"\nRequired fields: {list(required_fields.keys())}")
|
|
||||||
print(f"Optional fields: {list(optional_fields.keys())}")
|
|
||||||
|
|
||||||
# Check for existing admin user
|
|
||||||
result = session.execute(text("SELECT * FROM users WHERE username = 'admin' LIMIT 1"))
|
|
||||||
admin_row = result.fetchone()
|
|
||||||
|
|
||||||
if admin_row:
|
|
||||||
print("\nAdmin user found in database")
|
|
||||||
# Update password
|
|
||||||
from werkzeug.security import generate_password_hash
|
|
||||||
password_hash = generate_password_hash('admin123')
|
|
||||||
session.execute(text("""
|
|
||||||
UPDATE users
|
|
||||||
SET password_hash = :password_hash,
|
|
||||||
is_active = 1,
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE username = 'admin'
|
|
||||||
"""), {'password_hash': password_hash})
|
|
||||||
session.commit()
|
|
||||||
print("Admin password updated successfully")
|
|
||||||
else:
|
|
||||||
print("\nAdmin user not found, creating...")
|
|
||||||
from werkzeug.security import generate_password_hash
|
|
||||||
password_hash = generate_password_hash('admin123')
|
|
||||||
|
|
||||||
# Build INSERT with all required fields
|
|
||||||
insert_fields = ['username', 'email', 'password_hash', 'role']
|
|
||||||
insert_values = {
|
|
||||||
'username': 'admin',
|
|
||||||
'email': 'admin@tsp.com',
|
|
||||||
'password_hash': password_hash,
|
|
||||||
'role': 'admin'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add optional fields that exist in table
|
|
||||||
if 'is_active' in optional_fields or 'is_active' not in required_fields:
|
|
||||||
insert_fields.append('is_active')
|
|
||||||
insert_values['is_active'] = True
|
|
||||||
|
|
||||||
if 'region' in optional_fields or 'region' not in required_fields:
|
|
||||||
insert_fields.append('region')
|
|
||||||
insert_values['region'] = None
|
|
||||||
|
|
||||||
# Handle full_name if it exists
|
|
||||||
if 'full_name' in required_fields:
|
|
||||||
insert_fields.append('full_name')
|
|
||||||
insert_values['full_name'] = 'Administrator'
|
|
||||||
elif 'full_name' in optional_fields:
|
|
||||||
insert_fields.append('full_name')
|
|
||||||
insert_values['full_name'] = 'Administrator'
|
|
||||||
|
|
||||||
# Handle other required fields with defaults
|
|
||||||
for field_name in required_fields:
|
|
||||||
if field_name not in insert_fields:
|
|
||||||
if field_name in ['created_at', 'updated_at']:
|
|
||||||
insert_fields.append(field_name)
|
|
||||||
insert_values[field_name] = 'NOW()'
|
|
||||||
else:
|
|
||||||
# Use empty string or default value
|
|
||||||
insert_fields.append(field_name)
|
|
||||||
insert_values[field_name] = ''
|
|
||||||
|
|
||||||
fields_str = ', '.join(insert_fields)
|
|
||||||
values_str = ', '.join([f':{f}' for f in insert_fields])
|
|
||||||
|
|
||||||
sql = f"""
|
|
||||||
INSERT INTO users ({fields_str})
|
|
||||||
VALUES ({values_str})
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Fix NOW() placeholders
|
|
||||||
final_values = {}
|
|
||||||
for k, v in insert_values.items():
|
|
||||||
if v == 'NOW()':
|
|
||||||
# Will use SQL NOW()
|
|
||||||
continue
|
|
||||||
final_values[k] = v
|
|
||||||
|
|
||||||
# Use raw SQL with NOW()
|
|
||||||
sql_final = f"""
|
|
||||||
INSERT INTO users ({fields_str.replace(':created_at', 'created_at').replace(':updated_at', 'updated_at')})
|
|
||||||
VALUES ({values_str.replace(':created_at', 'NOW()').replace(':updated_at', 'NOW()')})
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Clean up the SQL
|
|
||||||
for k in ['created_at', 'updated_at']:
|
|
||||||
if f':{k}' in values_str:
|
|
||||||
values_str = values_str.replace(f':{k}', 'NOW()')
|
|
||||||
if k in final_values:
|
|
||||||
del final_values[k]
|
|
||||||
|
|
||||||
# Final SQL construction
|
|
||||||
final_sql = f"INSERT INTO users ({', '.join([f if f not in ['created_at', 'updated_at'] else f for f in insert_fields])}) VALUES ({', '.join([f':{f}' if f not in ['created_at', 'updated_at'] else 'NOW()' for f in insert_fields])})"
|
|
||||||
|
|
||||||
print(f"Executing SQL with fields: {insert_fields}")
|
|
||||||
session.execute(text(final_sql), final_values)
|
|
||||||
session.commit()
|
|
||||||
print("Admin user created successfully")
|
|
||||||
|
|
||||||
# Verify
|
|
||||||
result = session.execute(text("SELECT username, email, role, is_active FROM users WHERE username = 'admin'"))
|
|
||||||
admin_data = result.fetchone()
|
|
||||||
if admin_data:
|
|
||||||
print(f"\nVerification:")
|
|
||||||
print(f" Username: {admin_data[0]}")
|
|
||||||
print(f" Email: {admin_data[1]}")
|
|
||||||
print(f" Role: {admin_data[2]}")
|
|
||||||
print(f" Is Active: {admin_data[3]}")
|
|
||||||
print("\nAdmin user ready for login!")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
文件编码检查工具
|
|
||||||
检查项目中所有文件是否使用UTF-8编码
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import chardet
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def check_file_encoding(file_path: Path) -> dict:
|
|
||||||
"""检查文件编码"""
|
|
||||||
try:
|
|
||||||
with open(file_path, 'rb') as f:
|
|
||||||
raw_data = f.read()
|
|
||||||
result = chardet.detect(raw_data)
|
|
||||||
encoding = result.get('encoding', 'unknown')
|
|
||||||
confidence = result.get('confidence', 0)
|
|
||||||
|
|
||||||
# 检查文件是否有BOM
|
|
||||||
has_bom = False
|
|
||||||
if raw_data.startswith(b'\xef\xbb\xbf'):
|
|
||||||
has_bom = True
|
|
||||||
encoding = 'utf-8-sig'
|
|
||||||
|
|
||||||
return {
|
|
||||||
'file': str(file_path),
|
|
||||||
'encoding': encoding,
|
|
||||||
'confidence': confidence,
|
|
||||||
'has_bom': has_bom,
|
|
||||||
'is_utf8': encoding.lower() in ['utf-8', 'utf-8-sig', 'ascii'],
|
|
||||||
'size': len(raw_data)
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
return {
|
|
||||||
'file': str(file_path),
|
|
||||||
'error': str(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
def check_python_file_header(file_path: Path) -> bool:
|
|
||||||
"""检查Python文件是否有编码声明"""
|
|
||||||
try:
|
|
||||||
with open(file_path, 'r', encoding='utf-8') as f:
|
|
||||||
first_lines = [f.readline() for _ in range(3)]
|
|
||||||
for line in first_lines:
|
|
||||||
if 'coding' in line.lower() or 'encoding' in line.lower():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
project_root = Path(__file__).parent
|
|
||||||
|
|
||||||
# 需要检查的文件扩展名
|
|
||||||
check_extensions = {'.py', '.json', '.md', '.txt', '.html', '.css', '.js', '.sql', '.bat', '.sh'}
|
|
||||||
|
|
||||||
# 排除的目录
|
|
||||||
exclude_dirs = {'.git', '.venv', '__pycache__', 'node_modules', '.idea', 'logs', 'data', 'dist', 'build'}
|
|
||||||
|
|
||||||
results = []
|
|
||||||
python_files_without_encoding = []
|
|
||||||
|
|
||||||
print("=" * 80)
|
|
||||||
print("文件编码检查工具")
|
|
||||||
print("=" * 80)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 遍历所有文件
|
|
||||||
for root, dirs, files in os.walk(project_root):
|
|
||||||
# 排除指定目录
|
|
||||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
file_path = Path(root) / file
|
|
||||||
|
|
||||||
# 只检查指定扩展名的文件
|
|
||||||
if file_path.suffix.lower() not in check_extensions:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 检查编码
|
|
||||||
result = check_file_encoding(file_path)
|
|
||||||
results.append(result)
|
|
||||||
|
|
||||||
# 检查Python文件的编码声明
|
|
||||||
if file_path.suffix == '.py':
|
|
||||||
if not check_python_file_header(file_path):
|
|
||||||
python_files_without_encoding.append(file_path)
|
|
||||||
|
|
||||||
# 统计结果
|
|
||||||
total_files = len(results)
|
|
||||||
utf8_files = sum(1 for r in results if r.get('is_utf8', False))
|
|
||||||
non_utf8_files = total_files - utf8_files
|
|
||||||
|
|
||||||
print(f"总计检查文件: {total_files}")
|
|
||||||
print(f"UTF-8 编码文件: {utf8_files}")
|
|
||||||
print(f"非 UTF-8 编码文件: {non_utf8_files}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 显示非UTF-8文件
|
|
||||||
if non_utf8_files > 0:
|
|
||||||
print("=" * 80)
|
|
||||||
print("⚠️ 非 UTF-8 编码文件:")
|
|
||||||
print("=" * 80)
|
|
||||||
for result in results:
|
|
||||||
if not result.get('is_utf8', False) and 'error' not in result:
|
|
||||||
print(f" {result['file']}")
|
|
||||||
print(f" 编码: {result['encoding']} (置信度: {result['confidence']:.2%})")
|
|
||||||
if result.get('has_bom'):
|
|
||||||
print(f" ⚠️ 包含 BOM")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 显示缺少编码声明的Python文件
|
|
||||||
if python_files_without_encoding:
|
|
||||||
print("=" * 80)
|
|
||||||
print("⚠️ Python 文件缺少编码声明:")
|
|
||||||
print("=" * 80)
|
|
||||||
for file_path in python_files_without_encoding:
|
|
||||||
print(f" {file_path}")
|
|
||||||
print()
|
|
||||||
print("建议在这些文件开头添加: # -*- coding: utf-8 -*-")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 显示错误
|
|
||||||
errors = [r for r in results if 'error' in r]
|
|
||||||
if errors:
|
|
||||||
print("=" * 80)
|
|
||||||
print("❌ 检查出错的文件:")
|
|
||||||
print("=" * 80)
|
|
||||||
for result in errors:
|
|
||||||
print(f" {result['file']}: {result['error']}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 总结
|
|
||||||
print("=" * 80)
|
|
||||||
if non_utf8_files == 0 and not python_files_without_encoding:
|
|
||||||
print("✅ 所有文件编码检查通过!")
|
|
||||||
else:
|
|
||||||
print("⚠️ 发现编码问题,请根据上述信息修复")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
return non_utf8_files == 0 and not python_files_without_encoding
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
import chardet
|
|
||||||
except ImportError:
|
|
||||||
print("错误: 需要安装 chardet 库")
|
|
||||||
print("运行: pip install chardet")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
success = main()
|
|
||||||
sys.exit(0 if success else 1)
|
|
||||||
|
|
||||||
BIN
config/__pycache__/llm_config.cpython-311.pyc
Normal file
BIN
config/__pycache__/llm_config.cpython-311.pyc
Normal file
Binary file not shown.
@@ -7,10 +7,10 @@
|
|||||||
"pool_recycle": 3600
|
"pool_recycle": 3600
|
||||||
},
|
},
|
||||||
"llm": {
|
"llm": {
|
||||||
"provider": "openai",
|
"provider": "qwen",
|
||||||
"api_key": "",
|
"api_key": "sk-c0dbefa1718d46eaa897199135066f00",
|
||||||
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||||
"model": "qwen-turbo",
|
"model": "qwen-plus-latest",
|
||||||
"temperature": 0.7,
|
"temperature": 0.7,
|
||||||
"max_tokens": 2000,
|
"max_tokens": 2000,
|
||||||
"timeout": 30
|
"timeout": 30
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
chcp 65001 >nul 2>&1
|
|
||||||
echo ========================================
|
|
||||||
echo 代码文件编码批量转换工具
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 设置要扫描的文件扩展名
|
|
||||||
set "extensions=*.py *.java *.js *.ts *.html *.css *.xml *.json *.md *.txt *.bat *.cmd *.ps1 *.sh *.yml *.yaml *.ini *.cfg *.conf *.properties"
|
|
||||||
|
|
||||||
:: 设置目标目录(默认为当前目录)
|
|
||||||
set "target_dir=%cd%"
|
|
||||||
if not "%~1"=="" set "target_dir=%~1"
|
|
||||||
|
|
||||||
echo 目标目录: %target_dir%
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 检查目录是否存在
|
|
||||||
if not exist "%target_dir%" (
|
|
||||||
echo 错误:指定的目录不存在
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:: 创建临时目录
|
|
||||||
set "temp_dir=%temp%\encoding_convert_%random%"
|
|
||||||
mkdir "%temp_dir%"
|
|
||||||
|
|
||||||
:: 统计变量
|
|
||||||
set "total_files=0"
|
|
||||||
set "converted_files=0"
|
|
||||||
set "skipped_files=0"
|
|
||||||
|
|
||||||
echo 开始扫描文件...
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 遍历所有指定扩展名的文件
|
|
||||||
for %%e in (%extensions%) do (
|
|
||||||
for /r "%target_dir%" %%f in (%%e) do (
|
|
||||||
set /a total_files+=1
|
|
||||||
call :process_file "%%f"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo 转换完成
|
|
||||||
echo ========================================
|
|
||||||
echo 总文件数: %total_files%
|
|
||||||
echo 已转换: %converted_files%
|
|
||||||
echo 已跳过: %skipped_files%
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 清理临时目录
|
|
||||||
rd /s /q "%temp_dir%" 2>nul
|
|
||||||
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
|
|
||||||
:process_file
|
|
||||||
set "file=%~1"
|
|
||||||
set "is_utf8=0"
|
|
||||||
|
|
||||||
:: 检查文件是否已经是UTF-8编码
|
|
||||||
powershell -Command "try { $content = [System.IO.File]::ReadAllText('%file%', [System.Text.Encoding]::UTF8); $content | Out-Null; exit 0 } catch { exit 1 }" >nul 2>&1
|
|
||||||
if %errorlevel% equ 0 set "is_utf8=1"
|
|
||||||
|
|
||||||
if %is_utf8% equ 1 (
|
|
||||||
echo [跳过] %file% (已经是UTF-8)
|
|
||||||
set /a skipped_files+=1
|
|
||||||
) else (
|
|
||||||
echo [转换] %file%
|
|
||||||
|
|
||||||
:: 尝试检测并转换编码
|
|
||||||
powershell -Command ^
|
|
||||||
"$path = '%file%'; ^
|
|
||||||
try { ^
|
|
||||||
$bytes = [System.IO.File]::ReadAllBytes($path); ^
|
|
||||||
$encoding = [System.Text.Encoding]::GetEncoding('GB2312'); ^
|
|
||||||
$content = $encoding.GetString($bytes); ^
|
|
||||||
[System.IO.File]::WriteAllText($path, $content, [System.Text.Encoding]::UTF8); ^
|
|
||||||
exit 0 ^
|
|
||||||
} catch { ^
|
|
||||||
exit 1 ^
|
|
||||||
}"
|
|
||||||
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
set /a converted_files+=1
|
|
||||||
) else (
|
|
||||||
echo [警告] 无法转换 %file%
|
|
||||||
)
|
|
||||||
)
|
|
||||||
goto :eof
|
|
||||||
@@ -9,7 +9,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|||||||
|
|
||||||
from src.core.database import db_manager
|
from src.core.database import db_manager
|
||||||
from src.core.models import User
|
from src.core.models import User
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
# from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from sqlalchemy import text, inspect
|
from sqlalchemy import text, inspect
|
||||||
|
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
@@ -37,12 +37,12 @@ try:
|
|||||||
print(f" 激活状态: {admin_user.is_active}")
|
print(f" 激活状态: {admin_user.is_active}")
|
||||||
|
|
||||||
# 验证密码
|
# 验证密码
|
||||||
password_ok = check_password_hash(admin_user.password_hash, 'admin123')
|
password_ok = admin_user.check_password('admin123')
|
||||||
print(f" 密码验证: {'正确' if password_ok else '错误'}")
|
print(f" 密码验证: {'正确' if password_ok else '错误'}")
|
||||||
|
|
||||||
if not password_ok:
|
if not password_ok:
|
||||||
print("\n密码不匹配,正在更新密码...")
|
print("\n密码不匹配,正在更新密码...")
|
||||||
admin_user.password_hash = generate_password_hash('admin123')
|
admin_user.set_password('admin123')
|
||||||
admin_user.is_active = True
|
admin_user.is_active = True
|
||||||
if hasattr(admin_user, 'region'):
|
if hasattr(admin_user, 'region'):
|
||||||
admin_user.region = None
|
admin_user.region = None
|
||||||
@@ -56,7 +56,7 @@ try:
|
|||||||
print("用户已激活")
|
print("用户已激活")
|
||||||
|
|
||||||
# 最终验证
|
# 最终验证
|
||||||
test_password = check_password_hash(admin_user.password_hash, 'admin123')
|
test_password = admin_user.check_password('admin123')
|
||||||
if test_password and admin_user.is_active:
|
if test_password and admin_user.is_active:
|
||||||
print("\n管理员用户已就绪!")
|
print("\n管理员用户已就绪!")
|
||||||
print(" 用户名: admin")
|
print(" 用户名: admin")
|
||||||
@@ -69,8 +69,10 @@ try:
|
|||||||
else:
|
else:
|
||||||
print("\n未找到admin用户,正在创建...")
|
print("\n未找到admin用户,正在创建...")
|
||||||
|
|
||||||
# 准备密码哈希
|
# 创建用户对象来生成密码哈希
|
||||||
password_hash = generate_password_hash('admin123')
|
temp_user = User()
|
||||||
|
temp_user.set_password('admin123')
|
||||||
|
password_hash = temp_user.password_hash
|
||||||
|
|
||||||
# 检查表结构,使用SQL直接插入避免字段不匹配
|
# 检查表结构,使用SQL直接插入避免字段不匹配
|
||||||
try:
|
try:
|
||||||
@@ -78,6 +80,7 @@ try:
|
|||||||
new_admin = User(
|
new_admin = User(
|
||||||
username='admin',
|
username='admin',
|
||||||
email='admin@tsp.com',
|
email='admin@tsp.com',
|
||||||
|
name='系统管理员',
|
||||||
password_hash=password_hash,
|
password_hash=password_hash,
|
||||||
role='admin',
|
role='admin',
|
||||||
is_active=True
|
is_active=True
|
||||||
@@ -93,10 +96,11 @@ try:
|
|||||||
session.rollback()
|
session.rollback()
|
||||||
|
|
||||||
# 使用SQL直接插入
|
# 使用SQL直接插入
|
||||||
insert_fields = ['username', 'email', 'password_hash', 'role']
|
insert_fields = ['username', 'email', 'name', 'password_hash', 'role']
|
||||||
insert_values = {
|
insert_values = {
|
||||||
'username': 'admin',
|
'username': 'admin',
|
||||||
'email': 'admin@tsp.com',
|
'email': 'admin@tsp.com',
|
||||||
|
'name': '系统管理员',
|
||||||
'password_hash': password_hash,
|
'password_hash': password_hash,
|
||||||
'role': 'admin'
|
'role': 'admin'
|
||||||
}
|
}
|
||||||
@@ -124,7 +128,7 @@ try:
|
|||||||
# 验证创建结果
|
# 验证创建结果
|
||||||
verify_user = session.query(User).filter(User.username == 'admin').first()
|
verify_user = session.query(User).filter(User.username == 'admin').first()
|
||||||
if verify_user:
|
if verify_user:
|
||||||
test_password = check_password_hash(verify_user.password_hash, 'admin123')
|
test_password = verify_user.check_password('admin123')
|
||||||
print(f"\n验证结果:")
|
print(f"\n验证结果:")
|
||||||
print(f" 用户ID: {verify_user.id}")
|
print(f" 用户ID: {verify_user.id}")
|
||||||
print(f" 密码正确: {test_password}")
|
print(f" 密码正确: {test_password}")
|
||||||
@@ -138,9 +142,9 @@ try:
|
|||||||
print("\n警告: 用户创建成功但状态异常")
|
print("\n警告: 用户创建成功但状态异常")
|
||||||
|
|
||||||
# 创建其他示例用户
|
# 创建其他示例用户
|
||||||
for username, email, password, role, region in [
|
for username, name, email, password, role, region in [
|
||||||
('overseas_ops', 'overseas@tsp.com', 'ops123', 'overseas_ops', 'overseas'),
|
('overseas_ops', '海外运维', 'overseas@tsp.com', 'ops123', 'overseas_ops', 'overseas'),
|
||||||
('domestic_ops', 'domestic@tsp.com', 'ops123', 'domestic_ops', 'domestic')
|
('domestic_ops', '国内运维', 'domestic@tsp.com', 'ops123', 'domestic_ops', 'domestic')
|
||||||
]:
|
]:
|
||||||
ops_user = session.query(User).filter(User.username == username).first()
|
ops_user = session.query(User).filter(User.username == username).first()
|
||||||
if not ops_user:
|
if not ops_user:
|
||||||
@@ -148,11 +152,12 @@ try:
|
|||||||
try:
|
try:
|
||||||
new_user = User(
|
new_user = User(
|
||||||
username=username,
|
username=username,
|
||||||
|
name=name,
|
||||||
email=email,
|
email=email,
|
||||||
password_hash=generate_password_hash(password),
|
|
||||||
role=role,
|
role=role,
|
||||||
is_active=True
|
is_active=True
|
||||||
)
|
)
|
||||||
|
new_user.set_password(password)
|
||||||
if 'region' in existing_columns:
|
if 'region' in existing_columns:
|
||||||
new_user.region = region
|
new_user.region = region
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"init_time": "2025-10-31T12:46:01.637890",
|
|
||||||
"database_version": "MySQL 8.4.6",
|
|
||||||
"database_url": "mysql+pymysql://tsp_assistant:***@43.134.68.207/tsp_assistant?charset=utf8mb4",
|
|
||||||
"migrations_applied": 0,
|
|
||||||
"tables_created": 16,
|
|
||||||
"initial_data_inserted": true,
|
|
||||||
"verification_passed": true
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
# 数据库迁移说明
|
|
||||||
|
|
||||||
## 工单处理过程记录系统迁移
|
|
||||||
|
|
||||||
### 新增表:`work_order_process_history`
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE IF NOT EXISTS work_order_process_history (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
work_order_id INTEGER NOT NULL,
|
|
||||||
processor_name VARCHAR(100) NOT NULL,
|
|
||||||
processor_role VARCHAR(50),
|
|
||||||
processor_region VARCHAR(50),
|
|
||||||
process_content TEXT NOT NULL,
|
|
||||||
action_type VARCHAR(50) NOT NULL,
|
|
||||||
previous_status VARCHAR(50),
|
|
||||||
new_status VARCHAR(50),
|
|
||||||
assigned_module VARCHAR(50),
|
|
||||||
process_time DATETIME NOT NULL,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (work_order_id) REFERENCES work_orders(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_process_history_workorder ON work_order_process_history(work_order_id);
|
|
||||||
CREATE INDEX idx_process_history_time ON work_order_process_history(process_time);
|
|
||||||
```
|
|
||||||
|
|
||||||
### WorkOrder表新增字段
|
|
||||||
|
|
||||||
如果使用SQLite,可以使用以下SQL添加字段:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 注意:SQLite不支持直接ALTER TABLE添加多个列,需要逐个添加
|
|
||||||
|
|
||||||
ALTER TABLE work_orders ADD COLUMN assigned_module VARCHAR(50);
|
|
||||||
ALTER TABLE work_orders ADD COLUMN module_owner VARCHAR(100);
|
|
||||||
ALTER TABLE work_orders ADD COLUMN dispatcher VARCHAR(100);
|
|
||||||
ALTER TABLE work_orders ADD COLUMN dispatch_time DATETIME;
|
|
||||||
ALTER TABLE work_orders ADD COLUMN region VARCHAR(50);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用Python脚本迁移(推荐)
|
|
||||||
|
|
||||||
创建迁移脚本 `migrate_process_history.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
数据库迁移脚本:添加工单处理过程记录表和相关字段
|
|
||||||
"""
|
|
||||||
from src.core.database import db_manager
|
|
||||||
from src.core.models import Base, WorkOrderProcessHistory
|
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
def migrate_database():
|
|
||||||
"""执行数据库迁移"""
|
|
||||||
try:
|
|
||||||
with db_manager.get_session() as session:
|
|
||||||
# 创建新表
|
|
||||||
WorkOrderProcessHistory.__table__.create(db_manager.engine, checkfirst=True)
|
|
||||||
|
|
||||||
# 检查并添加新字段(SQLite需要特殊处理)
|
|
||||||
try:
|
|
||||||
# 尝试添加字段(如果已存在会报错,可以忽略)
|
|
||||||
session.execute(text("ALTER TABLE work_orders ADD COLUMN assigned_module VARCHAR(50)"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"字段 assigned_module 可能已存在: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.execute(text("ALTER TABLE work_orders ADD COLUMN module_owner VARCHAR(100)"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"字段 module_owner 可能已存在: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.execute(text("ALTER TABLE work_orders ADD COLUMN dispatcher VARCHAR(100)"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"字段 dispatcher 可能已存在: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.execute(text("ALTER TABLE work_orders ADD COLUMN dispatch_time DATETIME"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"字段 dispatch_time 可能已存在: {e}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
session.execute(text("ALTER TABLE work_orders ADD COLUMN region VARCHAR(50)"))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"字段 region 可能已存在: {e}")
|
|
||||||
|
|
||||||
session.commit()
|
|
||||||
print("数据库迁移完成!")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"数据库迁移失败: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
migrate_database()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 执行迁移
|
|
||||||
|
|
||||||
运行迁移脚本:
|
|
||||||
```bash
|
|
||||||
python migrate_process_history.py
|
|
||||||
```
|
|
||||||
|
|
||||||
或者直接在Python交互式环境中执行:
|
|
||||||
```python
|
|
||||||
from migrate_process_history import migrate_database
|
|
||||||
migrate_database()
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **备份数据库**:在执行迁移前,请务必备份现有数据库
|
|
||||||
2. **SQLite限制**:如果使用SQLite,ALTER TABLE添加列的操作在某些情况下可能失败,如果字段已存在会报错
|
|
||||||
3. **数据迁移**:现有工单的处理过程历史记录(存储在`resolution`字段中的)不会自动迁移到新表,需要手动处理
|
|
||||||
4. **索引优化**:新表已包含必要的索引,如果数据量大可以考虑添加更多索引
|
|
||||||
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
修复管理员用户
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
from src.core.database import db_manager
|
|
||||||
from sqlalchemy import text
|
|
||||||
from werkzeug.security import generate_password_hash
|
|
||||||
|
|
||||||
print("Fixing admin user...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
with db_manager.get_session() as session:
|
|
||||||
# Check if admin user exists
|
|
||||||
result = session.execute(text("SELECT id FROM users WHERE username = 'admin'"))
|
|
||||||
admin_row = result.fetchone()
|
|
||||||
|
|
||||||
password_hash = generate_password_hash('admin123')
|
|
||||||
|
|
||||||
if admin_row:
|
|
||||||
print("Admin user exists, updating password...")
|
|
||||||
session.execute(text("""
|
|
||||||
UPDATE users
|
|
||||||
SET password_hash = :password_hash,
|
|
||||||
is_active = 1,
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE username = 'admin'
|
|
||||||
"""), {'password_hash': password_hash})
|
|
||||||
session.commit()
|
|
||||||
print("Admin password updated successfully")
|
|
||||||
else:
|
|
||||||
print("Admin user not found, creating...")
|
|
||||||
session.execute(text("""
|
|
||||||
INSERT INTO users (
|
|
||||||
username, email, password_hash, role, full_name,
|
|
||||||
is_active, region, created_at, updated_at
|
|
||||||
) VALUES (
|
|
||||||
'admin', 'admin@tsp.com', :password_hash, 'admin', 'Administrator',
|
|
||||||
1, NULL, NOW(), NOW()
|
|
||||||
)
|
|
||||||
"""), {'password_hash': password_hash})
|
|
||||||
session.commit()
|
|
||||||
print("Admin user created successfully")
|
|
||||||
|
|
||||||
# Verify
|
|
||||||
result = session.execute(text("""
|
|
||||||
SELECT username, email, role, is_active, full_name
|
|
||||||
FROM users
|
|
||||||
WHERE username = 'admin'
|
|
||||||
"""))
|
|
||||||
admin_data = result.fetchone()
|
|
||||||
|
|
||||||
if admin_data:
|
|
||||||
print("\nVerification:")
|
|
||||||
print(f" Username: {admin_data[0]}")
|
|
||||||
print(f" Email: {admin_data[1]}")
|
|
||||||
print(f" Role: {admin_data[2]}")
|
|
||||||
print(f" Is Active: {admin_data[3]}")
|
|
||||||
print(f" Full Name: {admin_data[4]}")
|
|
||||||
|
|
||||||
# Test password verification
|
|
||||||
result = session.execute(text("SELECT password_hash FROM users WHERE username = 'admin'"))
|
|
||||||
stored_hash = result.fetchone()[0]
|
|
||||||
from werkzeug.security import check_password_hash
|
|
||||||
password_ok = check_password_hash(stored_hash, 'admin123')
|
|
||||||
print(f" Password Check: {'PASS' if password_ok else 'FAIL'}")
|
|
||||||
|
|
||||||
if password_ok and admin_data[3]:
|
|
||||||
print("\n[SUCCESS] Admin user is ready for login!")
|
|
||||||
print(" Username: admin")
|
|
||||||
print(" Password: admin123")
|
|
||||||
else:
|
|
||||||
print("\n[WARNING] User exists but password or status issue")
|
|
||||||
else:
|
|
||||||
print("\n[ERROR] User not found after creation")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
161
fix_git_push.bat
161
fix_git_push.bat
@@ -1,161 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
chcp 65001 >nul 2>&1
|
|
||||||
echo ========================================
|
|
||||||
echo Git推送问题诊断和修复工具
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 1. 检查Git状态
|
|
||||||
echo [1] 检查Git状态...
|
|
||||||
git status >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ? Git未初始化或不可用
|
|
||||||
echo 请确保:
|
|
||||||
echo 1. 已安装Git
|
|
||||||
echo 2. 当前目录是Git仓库
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo ? Git状态正常
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 2. 检查远程仓库配置
|
|
||||||
echo [2] 检查远程仓库配置...
|
|
||||||
git remote -v >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ? 未配置远程仓库
|
|
||||||
echo 请先运行: git remote add origin ^<仓库地址^>
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo ? 远程仓库配置正常
|
|
||||||
git remote -v
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 3. 检查当前分支
|
|
||||||
echo [3] 检查当前分支...
|
|
||||||
for /f "tokens=*" %%b in ('git branch --show-current 2^>nul') do set current_branch=%%b
|
|
||||||
if "!current_branch!"=="" (
|
|
||||||
echo ? 无法获取当前分支信息
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo 当前分支: !current_branch!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 4. 检查是否有未提交的更改
|
|
||||||
echo [4] 检查未提交的更改...
|
|
||||||
git status --porcelain >nul 2>&1
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
git status --porcelain | findstr /r "." >nul
|
|
||||||
if !errorlevel! equ 0 (
|
|
||||||
echo ?? 有未提交的更改
|
|
||||||
set /p commit="是否先提交更改? (y/n): "
|
|
||||||
if /i "!commit!"=="y" (
|
|
||||||
git add .
|
|
||||||
set /p msg="请输入提交信息: "
|
|
||||||
if "!msg!"=="" set msg=自动提交
|
|
||||||
git commit -m "!msg!"
|
|
||||||
if !errorlevel! neq 0 (
|
|
||||||
echo ? 提交失败
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
echo ? 暂存区状态正常
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 5. 尝试获取远程分支信息
|
|
||||||
echo [5] 获取远程分支信息...
|
|
||||||
git fetch origin >nul 2>&1
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ? 无法连接到远程仓库
|
|
||||||
echo.
|
|
||||||
echo 可能的原因:
|
|
||||||
echo 1. 网络连接问题
|
|
||||||
echo 2. 远程仓库地址错误
|
|
||||||
echo 3. 需要认证(请检查是否已配置SSH密钥或Token)
|
|
||||||
echo.
|
|
||||||
echo 远程仓库地址:
|
|
||||||
git config --get remote.origin.url
|
|
||||||
echo.
|
|
||||||
echo 解决建议:
|
|
||||||
echo 1. 检查网络连接
|
|
||||||
echo 2. 验证远程仓库地址
|
|
||||||
echo 3. 配置SSH密钥或访问令牌
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo ? 远程仓库连接成功
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 6. 检查分支跟踪关系
|
|
||||||
echo [6] 检查分支跟踪关系...
|
|
||||||
git branch -vv
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 7. 尝试推送到远程
|
|
||||||
echo [7] 尝试推送...
|
|
||||||
echo 当前分支: !current_branch!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 检查远程是否存在该分支
|
|
||||||
git ls-remote --heads origin !current_branch! >nul 2>&1
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
echo 远程分支 !current_branch! 已存在
|
|
||||||
echo.
|
|
||||||
echo 尝试使用当前分支名称推送...
|
|
||||||
git push origin !current_branch!
|
|
||||||
if !errorlevel! neq 0 (
|
|
||||||
echo.
|
|
||||||
echo ? 推送失败,尝试拉取最新更改...
|
|
||||||
git pull origin !current_branch! --rebase
|
|
||||||
if !errorlevel! equ 0 (
|
|
||||||
echo ? 重新尝试推送...
|
|
||||||
git push origin !current_branch!
|
|
||||||
) else (
|
|
||||||
echo ? 拉取失败,请检查冲突
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
echo 远程分支 !current_branch! 不存在
|
|
||||||
echo.
|
|
||||||
echo 尝试设置上游并推送...
|
|
||||||
git push -u origin !current_branch!
|
|
||||||
)
|
|
||||||
|
|
||||||
if %errorlevel% equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ? 推送成功!
|
|
||||||
) else (
|
|
||||||
echo.
|
|
||||||
echo ? 推送失败
|
|
||||||
echo.
|
|
||||||
echo ? 常见问题和解决方案:
|
|
||||||
echo.
|
|
||||||
echo 1. 如果是认证问题:
|
|
||||||
echo - 检查SSH密钥: ssh -T git@github.com (GitHub) 或 ssh -T git@gitee.com (Gitee)
|
|
||||||
echo - 或使用HTTPS + Token方式
|
|
||||||
echo.
|
|
||||||
echo 2. 如果是分支冲突:
|
|
||||||
echo - 运行: git pull origin !current_branch! --rebase
|
|
||||||
echo - 解决冲突后: git push origin !current_branch!
|
|
||||||
echo.
|
|
||||||
echo 3. 如果远程分支名称不同:
|
|
||||||
echo - 检查远程分支: git branch -r
|
|
||||||
echo - 可能需要推送主分支: git push origin main 或 git push origin master
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo ? 诊断完成!
|
|
||||||
echo ========================================
|
|
||||||
pause
|
|
||||||
26
git_push.bat
Normal file
26
git_push.bat
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
rem 获取当前日期和时间
|
||||||
|
for /f "tokens=1-6 delims=/ " %%a in ('date /t') do set CDATE=%%a-%%b-%%c
|
||||||
|
for /f "tokens=1-2 delims=:\ " %%a in ('time /t') do set CTIME=%%a-%%b
|
||||||
|
|
||||||
|
set COMMIT_MESSAGE="feat: %CDATE% %CTIME% - 全面架构重构、功能增强及问题修复"
|
||||||
|
|
||||||
|
rem 添加所有变更到暂存区
|
||||||
|
git add .
|
||||||
|
|
||||||
|
rem 检查是否有文件被添加、修改或删除
|
||||||
|
git diff --cached --quiet
|
||||||
|
if %errorlevel% equ 0 (
|
||||||
|
echo 没有检测到需要提交的变更。
|
||||||
|
) else (
|
||||||
|
rem 提交变更
|
||||||
|
git commit -m %COMMIT_MESSAGE%
|
||||||
|
|
||||||
|
rem 推送变更到远程仓库
|
||||||
|
git push origin master
|
||||||
|
|
||||||
|
echo Git 推送完成!
|
||||||
|
)
|
||||||
|
|
||||||
|
pause
|
||||||
23
git_push.sh
Normal file
23
git_push.sh
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 获取当前日期和时间
|
||||||
|
COMMIT_DATE=$(date +"%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# 添加所有变更到暂存区
|
||||||
|
git add .
|
||||||
|
|
||||||
|
# 检查是否有文件被添加、修改或删除
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "没有检测到需要提交的变更。"
|
||||||
|
else
|
||||||
|
# 创建提交消息
|
||||||
|
COMMIT_MESSAGE="feat: ${COMMIT_DATE} - 全面架构重构、功能增强及问题修复"
|
||||||
|
|
||||||
|
# 提交变更
|
||||||
|
git commit -m "$COMMIT_MESSAGE"
|
||||||
|
|
||||||
|
# 推送变更到远程仓库
|
||||||
|
git push origin master
|
||||||
|
|
||||||
|
echo "Git 推送完成!"
|
||||||
|
fi
|
||||||
211
init.sql
211
init.sql
@@ -1,211 +0,0 @@
|
|||||||
-- TSP智能助手数据库初始化脚本
|
|
||||||
|
|
||||||
-- 创建数据库(如果不存在)
|
|
||||||
CREATE DATABASE IF NOT EXISTS tsp_assistant CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- 使用数据库
|
|
||||||
USE tsp_assistant;
|
|
||||||
|
|
||||||
-- 创建用户表
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
username VARCHAR(50) UNIQUE NOT NULL,
|
|
||||||
email VARCHAR(100) UNIQUE NOT NULL,
|
|
||||||
password_hash VARCHAR(255) NOT NULL,
|
|
||||||
role ENUM('admin', 'user', 'operator') DEFAULT 'user',
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建工单表
|
|
||||||
CREATE TABLE IF NOT EXISTS work_orders (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
order_id VARCHAR(50) UNIQUE NOT NULL,
|
|
||||||
title VARCHAR(200) NOT NULL,
|
|
||||||
description TEXT NOT NULL,
|
|
||||||
category VARCHAR(100) NOT NULL,
|
|
||||||
priority VARCHAR(20) NOT NULL DEFAULT 'medium',
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
resolution TEXT,
|
|
||||||
satisfaction_score FLOAT,
|
|
||||||
|
|
||||||
-- 飞书集成字段
|
|
||||||
feishu_record_id VARCHAR(100) UNIQUE,
|
|
||||||
assignee VARCHAR(100),
|
|
||||||
solution TEXT,
|
|
||||||
ai_suggestion TEXT,
|
|
||||||
|
|
||||||
-- 扩展飞书字段
|
|
||||||
source VARCHAR(50),
|
|
||||||
module VARCHAR(100),
|
|
||||||
created_by VARCHAR(100),
|
|
||||||
wilfulness VARCHAR(100),
|
|
||||||
date_of_close TIMESTAMP NULL,
|
|
||||||
vehicle_type VARCHAR(100),
|
|
||||||
vin_sim VARCHAR(50),
|
|
||||||
app_remote_control_version VARCHAR(100),
|
|
||||||
hmi_sw VARCHAR(100),
|
|
||||||
parent_record VARCHAR(100),
|
|
||||||
has_updated_same_day VARCHAR(50),
|
|
||||||
operating_time VARCHAR(100),
|
|
||||||
|
|
||||||
-- 工单分发和权限管理字段
|
|
||||||
assigned_module VARCHAR(50),
|
|
||||||
module_owner VARCHAR(100),
|
|
||||||
dispatcher VARCHAR(100),
|
|
||||||
dispatch_time TIMESTAMP NULL,
|
|
||||||
region VARCHAR(50),
|
|
||||||
|
|
||||||
INDEX idx_order_id (order_id),
|
|
||||||
INDEX idx_status (status),
|
|
||||||
INDEX idx_priority (priority),
|
|
||||||
INDEX idx_created_at (created_at),
|
|
||||||
INDEX idx_assigned_module (assigned_module),
|
|
||||||
INDEX idx_region (region),
|
|
||||||
INDEX idx_feishu_record_id (feishu_record_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建预警表
|
|
||||||
CREATE TABLE IF NOT EXISTS alerts (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
rule_name VARCHAR(100) NOT NULL,
|
|
||||||
alert_type VARCHAR(50) NOT NULL,
|
|
||||||
level VARCHAR(20) NOT NULL DEFAULT 'info',
|
|
||||||
severity VARCHAR(20) NOT NULL DEFAULT 'medium',
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
data TEXT,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
resolved_at TIMESTAMP NULL,
|
|
||||||
INDEX idx_level (level),
|
|
||||||
INDEX idx_alert_type (alert_type),
|
|
||||||
INDEX idx_severity (severity),
|
|
||||||
INDEX idx_is_active (is_active),
|
|
||||||
INDEX idx_created_at (created_at)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建对话表
|
|
||||||
CREATE TABLE IF NOT EXISTS conversations (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
work_order_id INT,
|
|
||||||
user_message TEXT NOT NULL,
|
|
||||||
assistant_response TEXT NOT NULL,
|
|
||||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
confidence_score FLOAT,
|
|
||||||
knowledge_used TEXT,
|
|
||||||
response_time FLOAT,
|
|
||||||
FOREIGN KEY (work_order_id) REFERENCES work_orders(id) ON DELETE CASCADE,
|
|
||||||
INDEX idx_work_order_id (work_order_id),
|
|
||||||
INDEX idx_timestamp (timestamp)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建知识库表
|
|
||||||
CREATE TABLE IF NOT EXISTS knowledge_entries (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
question TEXT NOT NULL,
|
|
||||||
answer TEXT NOT NULL,
|
|
||||||
category VARCHAR(100) NOT NULL,
|
|
||||||
confidence_score FLOAT DEFAULT 0.0,
|
|
||||||
usage_count INT DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
is_verified BOOLEAN DEFAULT FALSE,
|
|
||||||
verified_by VARCHAR(100),
|
|
||||||
verified_at TIMESTAMP NULL,
|
|
||||||
vector_embedding TEXT,
|
|
||||||
INDEX idx_category (category),
|
|
||||||
INDEX idx_is_active (is_active),
|
|
||||||
INDEX idx_is_verified (is_verified)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建工单建议表
|
|
||||||
CREATE TABLE IF NOT EXISTS work_order_suggestions (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
work_order_id INT NOT NULL,
|
|
||||||
ai_suggestion TEXT,
|
|
||||||
human_resolution TEXT,
|
|
||||||
ai_similarity FLOAT,
|
|
||||||
approved BOOLEAN DEFAULT FALSE,
|
|
||||||
use_human_resolution BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (work_order_id) REFERENCES work_orders(id) ON DELETE CASCADE,
|
|
||||||
INDEX idx_work_order_id (work_order_id),
|
|
||||||
INDEX idx_approved (approved)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建工单处理过程记录表
|
|
||||||
CREATE TABLE IF NOT EXISTS work_order_process_history (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
work_order_id INT NOT NULL,
|
|
||||||
processor_name VARCHAR(100) NOT NULL,
|
|
||||||
processor_role VARCHAR(50),
|
|
||||||
processor_region VARCHAR(50),
|
|
||||||
process_content TEXT NOT NULL,
|
|
||||||
action_type VARCHAR(50) NOT NULL,
|
|
||||||
previous_status VARCHAR(50),
|
|
||||||
new_status VARCHAR(50),
|
|
||||||
assigned_module VARCHAR(50),
|
|
||||||
process_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (work_order_id) REFERENCES work_orders(id) ON DELETE CASCADE,
|
|
||||||
INDEX idx_work_order_id (work_order_id),
|
|
||||||
INDEX idx_process_time (process_time),
|
|
||||||
INDEX idx_action_type (action_type),
|
|
||||||
INDEX idx_processor_name (processor_name)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建系统配置表
|
|
||||||
CREATE TABLE IF NOT EXISTS system_settings (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
key_name VARCHAR(100) UNIQUE NOT NULL,
|
|
||||||
value TEXT,
|
|
||||||
description TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 插入默认管理员用户
|
|
||||||
INSERT IGNORE INTO users (username, email, password_hash, role) VALUES
|
|
||||||
('admin', 'admin@tsp.com', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewdBPj4J/8K8K8K8', 'admin');
|
|
||||||
|
|
||||||
-- 插入默认系统配置
|
|
||||||
INSERT IGNORE INTO system_settings (key_name, value, description) VALUES
|
|
||||||
('system_name', 'TSP智能助手', '系统名称'),
|
|
||||||
('version', '2.0.0', '系统版本'),
|
|
||||||
('maintenance_mode', 'false', '维护模式'),
|
|
||||||
('max_concurrent_users', '100', '最大并发用户数'),
|
|
||||||
('session_timeout', '3600', '会话超时时间(秒)');
|
|
||||||
|
|
||||||
-- 创建分析统计表
|
|
||||||
CREATE TABLE IF NOT EXISTS analytics (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
date TIMESTAMP NOT NULL,
|
|
||||||
total_orders INT DEFAULT 0,
|
|
||||||
resolved_orders INT DEFAULT 0,
|
|
||||||
avg_resolution_time FLOAT DEFAULT 0.0,
|
|
||||||
satisfaction_avg FLOAT DEFAULT 0.0,
|
|
||||||
knowledge_hit_rate FLOAT DEFAULT 0.0,
|
|
||||||
category_distribution TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_date (date)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建车辆实时数据表(如果不存在)
|
|
||||||
CREATE TABLE IF NOT EXISTS vehicle_data (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
vehicle_id VARCHAR(50) NOT NULL,
|
|
||||||
vehicle_vin VARCHAR(17),
|
|
||||||
data_type VARCHAR(50) NOT NULL,
|
|
||||||
data_value TEXT NOT NULL,
|
|
||||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
INDEX idx_vehicle_id (vehicle_id),
|
|
||||||
INDEX idx_vehicle_vin (vehicle_vin),
|
|
||||||
INDEX idx_data_type (data_type),
|
|
||||||
INDEX idx_timestamp (timestamp)
|
|
||||||
);
|
|
||||||
@@ -20,7 +20,7 @@ from src.utils.helpers import setup_logging
|
|||||||
from src.core.database import db_manager
|
from src.core.database import db_manager
|
||||||
from src.core.models import (
|
from src.core.models import (
|
||||||
Base, WorkOrder, KnowledgeEntry, Conversation, Analytics, Alert, VehicleData,
|
Base, WorkOrder, KnowledgeEntry, Conversation, Analytics, Alert, VehicleData,
|
||||||
WorkOrderSuggestion, WorkOrderProcessHistory
|
WorkOrderSuggestion, WorkOrderProcessHistory, User
|
||||||
)
|
)
|
||||||
|
|
||||||
class DatabaseInitializer:
|
class DatabaseInitializer:
|
||||||
@@ -168,6 +168,7 @@ class DatabaseInitializer:
|
|||||||
print("\n执行数据库迁移...")
|
print("\n执行数据库迁移...")
|
||||||
|
|
||||||
migrations = [
|
migrations = [
|
||||||
|
self._migrate_user_table_structure,
|
||||||
self._migrate_knowledge_verification_fields,
|
self._migrate_knowledge_verification_fields,
|
||||||
self._migrate_alert_severity_field,
|
self._migrate_alert_severity_field,
|
||||||
self._migrate_vehicle_data_table,
|
self._migrate_vehicle_data_table,
|
||||||
@@ -192,6 +193,51 @@ class DatabaseInitializer:
|
|||||||
print(f"完成 {success_count}/{len(migrations)} 个迁移")
|
print(f"完成 {success_count}/{len(migrations)} 个迁移")
|
||||||
return success_count > 0
|
return success_count > 0
|
||||||
|
|
||||||
|
def _migrate_user_table_structure(self) -> bool:
|
||||||
|
"""迁移用户表结构"""
|
||||||
|
print(" 检查用户表结构...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with db_manager.get_session() as session:
|
||||||
|
# 检查users表是否存在
|
||||||
|
inspector = inspect(db_manager.engine)
|
||||||
|
if 'users' not in inspector.get_table_names():
|
||||||
|
print(" 创建users表...")
|
||||||
|
# 创建User模型的表
|
||||||
|
User.__table__.create(session.bind, checkfirst=True)
|
||||||
|
print(" users表创建成功")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 检查users表的列
|
||||||
|
existing_columns = [col['name'] for col in inspector.get_columns('users')]
|
||||||
|
print(f" users表现有字段: {existing_columns}")
|
||||||
|
|
||||||
|
# 检查必需的字段
|
||||||
|
required_fields = {
|
||||||
|
'name': 'VARCHAR(100)',
|
||||||
|
'email': 'VARCHAR(120)',
|
||||||
|
'role': 'VARCHAR(20) DEFAULT \'user\'',
|
||||||
|
'is_active': 'BOOLEAN DEFAULT TRUE',
|
||||||
|
'created_at': 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
|
||||||
|
'last_login': 'TIMESTAMP NULL'
|
||||||
|
}
|
||||||
|
|
||||||
|
fields_to_add = []
|
||||||
|
for field_name, field_type in required_fields.items():
|
||||||
|
if field_name not in existing_columns:
|
||||||
|
fields_to_add.append((field_name, field_type))
|
||||||
|
|
||||||
|
if fields_to_add:
|
||||||
|
print(f" 需要添加字段: {[f[0] for f in fields_to_add]}")
|
||||||
|
return self._add_table_columns('users', fields_to_add)
|
||||||
|
else:
|
||||||
|
print(" users表结构完整")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 用户表结构迁移失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def _migrate_knowledge_verification_fields(self) -> bool:
|
def _migrate_knowledge_verification_fields(self) -> bool:
|
||||||
"""迁移知识库验证字段"""
|
"""迁移知识库验证字段"""
|
||||||
print(" 检查知识库验证字段...")
|
print(" 检查知识库验证字段...")
|
||||||
@@ -454,7 +500,18 @@ class DatabaseInitializer:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with db_manager.get_session() as session:
|
with db_manager.get_session() as session:
|
||||||
# 检查是否已有数据
|
# 检查是否已有用户数据
|
||||||
|
existing_users = session.query(User).count()
|
||||||
|
if existing_users == 0:
|
||||||
|
# 创建默认管理员用户
|
||||||
|
from src.core.auth_manager import auth_manager
|
||||||
|
admin_user = auth_manager.create_default_admin()
|
||||||
|
if admin_user:
|
||||||
|
print(" 默认管理员用户已创建: admin/admin123")
|
||||||
|
else:
|
||||||
|
print(" 创建默认管理员用户失败")
|
||||||
|
|
||||||
|
# 检查是否已有知识库数据
|
||||||
existing_count = session.query(KnowledgeEntry).count()
|
existing_count = session.query(KnowledgeEntry).count()
|
||||||
if existing_count > 0:
|
if existing_count > 0:
|
||||||
print(f" 数据库中已有 {existing_count} 条知识库条目,跳过初始数据插入")
|
print(f" 数据库中已有 {existing_count} 条知识库条目,跳过初始数据插入")
|
||||||
@@ -653,6 +710,7 @@ class DatabaseInitializer:
|
|||||||
with db_manager.get_session() as session:
|
with db_manager.get_session() as session:
|
||||||
# 检查各表的记录数
|
# 检查各表的记录数
|
||||||
tables_info = {
|
tables_info = {
|
||||||
|
'users': User,
|
||||||
'work_orders': WorkOrder,
|
'work_orders': WorkOrder,
|
||||||
'conversations': Conversation,
|
'conversations': Conversation,
|
||||||
'knowledge_entries': KnowledgeEntry,
|
'knowledge_entries': KnowledgeEntry,
|
||||||
|
|||||||
BIN
local_test.db
Normal file
BIN
local_test.db
Normal file
Binary file not shown.
16204
logs/dashboard.log
16204
logs/dashboard.log
File diff suppressed because one or more lines are too long
@@ -1,70 +0,0 @@
|
|||||||
2025-09-19 18:26:27,748 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V001 - location
|
|
||||||
2025-09-19 18:26:27,752 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V001 - status
|
|
||||||
2025-09-19 18:26:27,756 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V001 - battery
|
|
||||||
2025-09-19 18:26:27,759 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V001 - engine
|
|
||||||
2025-09-19 18:26:27,764 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V002 - location
|
|
||||||
2025-09-19 18:26:27,768 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V002 - status
|
|
||||||
2025-09-19 18:26:27,772 - src.vehicle.vehicle_data_manager - INFO - 添加车辆数据成功: V002 - fault
|
|
||||||
2025-09-19 18:26:27,773 - src.vehicle.vehicle_data_manager - INFO - 示例车辆数据添加成功
|
|
||||||
2025-09-19 18:53:30,187 - sqlalchemy.pool.impl.QueuePool - ERROR - Exception during reset or similar
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\pool\base.py", line 985, in _finalize_fairy
|
|
||||||
fairy._reset(
|
|
||||||
~~~~~~~~~~~~^
|
|
||||||
pool,
|
|
||||||
^^^^^
|
|
||||||
...<2 lines>...
|
|
||||||
asyncio_safe=can_manipulate_connection,
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
)
|
|
||||||
^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\pool\base.py", line 1433, in _reset
|
|
||||||
pool._dialect.do_rollback(self)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\engine\default.py", line 711, in do_rollback
|
|
||||||
dbapi_connection.rollback()
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 505, in rollback
|
|
||||||
self._read_ok_packet()
|
|
||||||
~~~~~~~~~~~~~~~~~~~~^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 465, in _read_ok_packet
|
|
||||||
pkt = self._read_packet()
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 751, in _read_packet
|
|
||||||
packet_header = self._read_bytes(4)
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 789, in _read_bytes
|
|
||||||
data = self._rfile.read(num_bytes)
|
|
||||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.2032.0_x64__qbz5n2kfra8p0\Lib\socket.py", line 719, in readinto
|
|
||||||
return self._sock.recv_into(b)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~^^^
|
|
||||||
KeyboardInterrupt
|
|
||||||
2025-09-19 18:54:31,332 - sqlalchemy.pool.impl.QueuePool - ERROR - Exception during reset or similar
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\pool\base.py", line 985, in _finalize_fairy
|
|
||||||
fairy._reset(
|
|
||||||
~~~~~~~~~~~~^
|
|
||||||
pool,
|
|
||||||
^^^^^
|
|
||||||
...<2 lines>...
|
|
||||||
asyncio_safe=can_manipulate_connection,
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
)
|
|
||||||
^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\pool\base.py", line 1433, in _reset
|
|
||||||
pool._dialect.do_rollback(self)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\sqlalchemy\engine\default.py", line 711, in do_rollback
|
|
||||||
dbapi_connection.rollback()
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 505, in rollback
|
|
||||||
self._read_ok_packet()
|
|
||||||
~~~~~~~~~~~~~~~~~~~~^^
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 465, in _read_ok_packet
|
|
||||||
pkt = self._read_packet()
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 751, in _read_packet
|
|
||||||
packet_header = self._read_bytes(4)
|
|
||||||
File "C:\Users\Administrator.CHERY-NOT-8217.000\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pymysql\connections.py", line 789, in _read_bytes
|
|
||||||
data = self._rfile.read(num_bytes)
|
|
||||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.2032.0_x64__qbz5n2kfra8p0\Lib\socket.py", line 719, in readinto
|
|
||||||
return self._sock.recv_into(b)
|
|
||||||
~~~~~~~~~~~~~~~~~~~~^^^
|
|
||||||
KeyboardInterrupt
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
setlocal enabledelayedexpansion
|
|
||||||
|
|
||||||
echo ========================================
|
|
||||||
echo 简单Git推送工具
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 1. 显示Git状态
|
|
||||||
echo [1] Git状态:
|
|
||||||
git status --short
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 2. 显示远程仓库
|
|
||||||
echo [2] 远程仓库:
|
|
||||||
git remote -v
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo 错误: 未配置远程仓库
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 3. 显示当前分支
|
|
||||||
echo [3] 当前分支:
|
|
||||||
for /f "tokens=*" %%b in ('git branch --show-current 2^>nul') do set branch=%%b
|
|
||||||
if "!branch!"=="" (
|
|
||||||
echo 警告: 无法获取分支名称,尝试使用main
|
|
||||||
set branch=main
|
|
||||||
)
|
|
||||||
echo 分支: !branch!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 4. 检查是否有未提交的更改
|
|
||||||
echo [4] 检查未提交的更改...
|
|
||||||
git diff --quiet
|
|
||||||
set has_uncommitted=%errorlevel%
|
|
||||||
git diff --cached --quiet
|
|
||||||
set has_staged=%errorlevel%
|
|
||||||
|
|
||||||
if %has_uncommitted% neq 0 (
|
|
||||||
echo 有未暂存的更改
|
|
||||||
)
|
|
||||||
if %has_staged% neq 0 (
|
|
||||||
echo 有已暂存的更改
|
|
||||||
)
|
|
||||||
if %has_uncommitted% equ 0 if %has_staged% equ 0 (
|
|
||||||
echo 所有更改已提交
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 5. 尝试推送
|
|
||||||
echo [5] 开始推送...
|
|
||||||
echo 命令: git push origin !branch!
|
|
||||||
echo.
|
|
||||||
|
|
||||||
git push origin !branch! 2>&1 | findstr /v "^$"
|
|
||||||
set push_error=!errorlevel!
|
|
||||||
|
|
||||||
if !push_error! equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo 推送成功!
|
|
||||||
echo ========================================
|
|
||||||
) else (
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo 推送失败!错误码: !push_error!
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo 尝试设置上游并推送...
|
|
||||||
git push -u origin !branch! 2>&1 | findstr /v "^$"
|
|
||||||
set push_u_error=!errorlevel!
|
|
||||||
|
|
||||||
if !push_u_error! equ 0 (
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo 推送成功(已设置上游)!
|
|
||||||
echo ========================================
|
|
||||||
) else (
|
|
||||||
echo.
|
|
||||||
echo ========================================
|
|
||||||
echo 推送仍然失败
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
echo 常见问题和解决方案:
|
|
||||||
echo.
|
|
||||||
echo 1. 认证问题:
|
|
||||||
echo - 检查SSH密钥: ssh -T git@github.com (GitHub)
|
|
||||||
echo - 检查SSH密钥: ssh -T git@gitee.com (Gitee)
|
|
||||||
echo - 或使用HTTPS + Personal Access Token
|
|
||||||
echo.
|
|
||||||
echo 2. 远程仓库地址:
|
|
||||||
git config --get remote.origin.url
|
|
||||||
echo.
|
|
||||||
echo 3. 分支冲突:
|
|
||||||
echo - 先拉取: git pull origin !branch! --rebase
|
|
||||||
echo - 解决冲突后推送: git push origin !branch!
|
|
||||||
echo.
|
|
||||||
echo 4. 检查网络连接和远程仓库权限
|
|
||||||
echo.
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -167,6 +167,17 @@ class AlertSystem:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"检查规则 {rule_name} 失败: {e}")
|
logger.error(f"检查规则 {rule_name} 失败: {e}")
|
||||||
|
# 如果规则检查失败,也可以考虑生成一个系统级别的预警
|
||||||
|
system_alert = {
|
||||||
|
"rule_name": f"系统预警 - 规则检查失败: {rule_name}",
|
||||||
|
"alert_type": AlertType.SYSTEM.value,
|
||||||
|
"level": AlertLevel.ERROR.value,
|
||||||
|
"message": f"规则 \'{rule_name}\' 检查失败: {e}",
|
||||||
|
"data": {"error": str(e)},
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"rule_id": "system_rule_check_failure"
|
||||||
|
}
|
||||||
|
self._save_alert(system_alert) # 保存系统预警
|
||||||
|
|
||||||
return triggered_alerts
|
return triggered_alerts
|
||||||
|
|
||||||
@@ -238,6 +249,8 @@ class AlertSystem:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取规则数据失败: {e}")
|
logger.error(f"获取规则数据失败: {e}")
|
||||||
|
# 重新抛出异常,让上层调用者决定如何处理
|
||||||
|
raise
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,7 @@ class Config:
|
|||||||
|
|
||||||
# 数据库配置
|
# 数据库配置
|
||||||
DATABASE_URL = "mysql+pymysql://tsp_assistant:123456@jeason.online/tsp_assistant?charset=utf8mb4"
|
DATABASE_URL = "mysql+pymysql://tsp_assistant:123456@jeason.online/tsp_assistant?charset=utf8mb4"
|
||||||
|
# DATABASE_URL = "sqlite:///local_test.db" # 本地测试数据库
|
||||||
|
|
||||||
# 知识库配置
|
# 知识库配置
|
||||||
KNOWLEDGE_BASE_PATH = "data/knowledge_base"
|
KNOWLEDGE_BASE_PATH = "data/knowledge_base"
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ class DatabaseConfig:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class LLMConfig:
|
class LLMConfig:
|
||||||
"""LLM配置"""
|
"""LLM配置"""
|
||||||
provider: str = "openai"
|
provider: str = "qwen"
|
||||||
api_key: str = ""
|
api_key: str = "sk-c0dbefa1718d46eaa897199135066f00"
|
||||||
base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
model: str = "qwen-turbo"
|
model: str = "qwen-plus-latest"
|
||||||
temperature: float = 0.7
|
temperature: float = 0.7
|
||||||
max_tokens: int = 2000
|
max_tokens: int = 2000
|
||||||
timeout: int = 30
|
timeout: int = 30
|
||||||
|
|||||||
Binary file not shown.
BIN
src/core/__pycache__/auth_manager.cpython-311.pyc
Normal file
BIN
src/core/__pycache__/auth_manager.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
144
src/core/auth_manager.py
Normal file
144
src/core/auth_manager.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
认证管理器
|
||||||
|
处理用户登录、注册、会话管理等功能
|
||||||
|
"""
|
||||||
|
|
||||||
|
import jwt
|
||||||
|
import hashlib
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from src.core.database import db_manager
|
||||||
|
from src.core.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class AuthManager:
|
||||||
|
"""认证管理器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.secret_key = "your-secret-key-change-this-in-production" # 应该从配置中读取
|
||||||
|
self.token_expiry = timedelta(hours=24)
|
||||||
|
|
||||||
|
def hash_password(self, password: str) -> str:
|
||||||
|
"""密码哈希"""
|
||||||
|
return hashlib.sha256(password.encode()).hexdigest()
|
||||||
|
|
||||||
|
def verify_password(self, password: str, password_hash: str) -> bool:
|
||||||
|
"""验证密码"""
|
||||||
|
return self.hash_password(password) == password_hash
|
||||||
|
|
||||||
|
def generate_token(self, user_data: dict) -> str:
|
||||||
|
"""生成JWT token"""
|
||||||
|
payload = {
|
||||||
|
'user_id': user_data['id'],
|
||||||
|
'username': user_data['username'],
|
||||||
|
'exp': datetime.utcnow() + self.token_expiry,
|
||||||
|
'iat': datetime.utcnow()
|
||||||
|
}
|
||||||
|
return jwt.encode(payload, self.secret_key, algorithm='HS256')
|
||||||
|
|
||||||
|
def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
|
||||||
|
"""验证JWT token"""
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, self.secret_key, algorithms=['HS256'])
|
||||||
|
return payload
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
return None
|
||||||
|
except jwt.InvalidTokenError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def authenticate_user(self, username: str, password: str) -> Optional[dict]:
|
||||||
|
"""用户认证"""
|
||||||
|
print(f"[DEBUG] 开始认证用户: {username}")
|
||||||
|
with db_manager.get_session() as session:
|
||||||
|
user = session.query(User).filter_by(username=username).first()
|
||||||
|
print(f"[DEBUG] 找到用户: {user is not None}")
|
||||||
|
|
||||||
|
if user and user.is_active and self.verify_password(password, user.password_hash):
|
||||||
|
print(f"[DEBUG] 用户认证成功: {user.username}")
|
||||||
|
|
||||||
|
# 立即访问所有需要的属性,确保在会话内
|
||||||
|
user_id = user.id
|
||||||
|
username_val = user.username
|
||||||
|
password_hash_val = user.password_hash
|
||||||
|
email_val = user.email
|
||||||
|
name_val = user.name
|
||||||
|
role_val = user.role
|
||||||
|
is_active_val = user.is_active
|
||||||
|
created_at_val = user.created_at
|
||||||
|
last_login_val = datetime.now()
|
||||||
|
|
||||||
|
print(f"[DEBUG] 访问用户属性成功: name={name_val}, role={role_val}")
|
||||||
|
|
||||||
|
# 更新最后登录时间
|
||||||
|
user.last_login = last_login_val
|
||||||
|
session.commit()
|
||||||
|
print("[DEBUG] 数据库更新成功")
|
||||||
|
|
||||||
|
# 返回用户数据字典,避免SQLAlchemy会话绑定问题
|
||||||
|
user_dict = {
|
||||||
|
'id': user_id,
|
||||||
|
'username': username_val,
|
||||||
|
'password_hash': password_hash_val,
|
||||||
|
'email': email_val,
|
||||||
|
'name': name_val,
|
||||||
|
'role': role_val,
|
||||||
|
'is_active': is_active_val,
|
||||||
|
'created_at': created_at_val,
|
||||||
|
'last_login': last_login_val
|
||||||
|
}
|
||||||
|
print(f"[DEBUG] 返回用户字典: {user_dict['username']}")
|
||||||
|
return user_dict
|
||||||
|
else:
|
||||||
|
print("[DEBUG] 用户认证失败")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user_by_id(self, user_id: int) -> Optional[User]:
|
||||||
|
"""根据ID获取用户"""
|
||||||
|
with db_manager.get_session() as session:
|
||||||
|
return session.query(User).filter_by(id=user_id).first()
|
||||||
|
|
||||||
|
def get_user_by_token(self, token: str) -> Optional[User]:
|
||||||
|
"""根据token获取用户"""
|
||||||
|
payload = self.verify_token(token)
|
||||||
|
if payload:
|
||||||
|
return self.get_user_by_id(payload['user_id'])
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_user(self, username: str, password: str, name: str, email: str = None, role: str = 'user') -> Optional[User]:
|
||||||
|
"""创建新用户"""
|
||||||
|
try:
|
||||||
|
with db_manager.get_session() as session:
|
||||||
|
# 检查用户名是否已存在
|
||||||
|
existing_user = session.query(User).filter_by(username=username).first()
|
||||||
|
if existing_user:
|
||||||
|
return None
|
||||||
|
|
||||||
|
user = User(
|
||||||
|
username=username,
|
||||||
|
name=name,
|
||||||
|
email=email,
|
||||||
|
role=role,
|
||||||
|
is_active=True
|
||||||
|
)
|
||||||
|
user.set_password(password)
|
||||||
|
|
||||||
|
session.add(user)
|
||||||
|
session.commit()
|
||||||
|
return user
|
||||||
|
except Exception as e:
|
||||||
|
print(f"创建用户失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def create_default_admin(self):
|
||||||
|
"""创建默认管理员用户"""
|
||||||
|
admin = self.create_user('admin', 'admin123', '系统管理员', 'admin@example.com', 'admin')
|
||||||
|
if admin:
|
||||||
|
print("默认管理员用户已创建: admin/admin123")
|
||||||
|
return admin
|
||||||
|
|
||||||
|
|
||||||
|
# 全局认证管理器实例
|
||||||
|
auth_manager = AuthManager()
|
||||||
@@ -26,18 +26,22 @@ class DatabaseManager:
|
|||||||
|
|
||||||
# 根据数据库类型选择不同的连接参数
|
# 根据数据库类型选择不同的连接参数
|
||||||
if "mysql" in db_config["url"]:
|
if "mysql" in db_config["url"]:
|
||||||
# MySQL配置 - 优化连接池
|
# MySQL配置 - 优化连接池和重连机制
|
||||||
self.engine = create_engine(
|
self.engine = create_engine(
|
||||||
db_config["url"],
|
db_config["url"],
|
||||||
echo=db_config["echo"],
|
echo=db_config["echo"],
|
||||||
pool_size=20, # 增加连接池大小
|
pool_size=10, # 连接池大小
|
||||||
max_overflow=30, # 增加溢出连接数
|
max_overflow=20, # 溢出连接数
|
||||||
pool_pre_ping=True,
|
pool_pre_ping=True, # 连接前检查连接是否有效
|
||||||
pool_recycle=1800, # 减少回收时间
|
pool_recycle=3600, # 1小时后回收连接
|
||||||
pool_timeout=30, # 连接池超时(秒)
|
pool_timeout=60, # 连接池超时(秒)
|
||||||
connect_args={
|
connect_args={
|
||||||
"charset": "utf8mb4",
|
"charset": "utf8mb4",
|
||||||
"autocommit": False,
|
"autocommit": False,
|
||||||
|
"connect_timeout": 30, # 连接超时
|
||||||
|
"read_timeout": 60, # 读取超时
|
||||||
|
"write_timeout": 60, # 写入超时
|
||||||
|
"max_allowed_packet": 64*1024*1024, # 64MB
|
||||||
"connect_timeout": 30, # 连接超时(秒)- 适用于网络延迟较大的情况
|
"connect_timeout": 30, # 连接超时(秒)- 适用于网络延迟较大的情况
|
||||||
"read_timeout": 30, # 读取超时(秒)
|
"read_timeout": 30, # 读取超时(秒)
|
||||||
"write_timeout": 30, # 写入超时(秒)
|
"write_timeout": 30, # 写入超时(秒)
|
||||||
@@ -82,7 +86,32 @@ class DatabaseManager:
|
|||||||
logger.error(f"数据库操作失败: {e}")
|
logger.error(f"数据库操作失败: {e}")
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
|
try:
|
||||||
session.close()
|
session.close()
|
||||||
|
except Exception as close_error:
|
||||||
|
logger.warning(f"关闭数据库会话时出错: {close_error}")
|
||||||
|
|
||||||
|
def check_connection(self) -> bool:
|
||||||
|
"""检查数据库连接是否正常"""
|
||||||
|
try:
|
||||||
|
with self.get_session() as session:
|
||||||
|
session.execute(text("SELECT 1"))
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"数据库连接检查失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reconnect(self) -> bool:
|
||||||
|
"""重新连接数据库"""
|
||||||
|
try:
|
||||||
|
if self.engine:
|
||||||
|
self.engine.dispose()
|
||||||
|
self._initialize_database()
|
||||||
|
logger.info("数据库重新连接成功")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"数据库重新连接失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def get_session_direct(self) -> Session:
|
def get_session_direct(self) -> Session:
|
||||||
"""直接获取数据库会话"""
|
"""直接获取数据库会话"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean,
|
|||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import hashlib
|
||||||
|
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
@@ -173,3 +174,39 @@ class WorkOrderProcessHistory(Base):
|
|||||||
|
|
||||||
# 关联工单
|
# 关联工单
|
||||||
work_order = relationship("WorkOrder", back_populates="process_history")
|
work_order = relationship("WorkOrder", back_populates="process_history")
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
"""用户模型"""
|
||||||
|
__tablename__ = "users"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
username = Column(String(50), unique=True, nullable=False)
|
||||||
|
password_hash = Column(String(128), nullable=False)
|
||||||
|
email = Column(String(120), unique=True, nullable=True)
|
||||||
|
name = Column(String(100), nullable=True)
|
||||||
|
role = Column(String(20), default='user') # admin, user, operator
|
||||||
|
is_active = Column(Boolean, default=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
|
last_login = Column(DateTime)
|
||||||
|
|
||||||
|
def set_password(self, password):
|
||||||
|
"""设置密码哈希"""
|
||||||
|
self.password_hash = hashlib.sha256(password.encode()).hexdigest()
|
||||||
|
|
||||||
|
def check_password(self, password):
|
||||||
|
"""验证密码"""
|
||||||
|
return self.password_hash == hashlib.sha256(password.encode()).hexdigest()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
"""转换为字典格式(用于API响应)"""
|
||||||
|
return {
|
||||||
|
'id': self.id,
|
||||||
|
'username': self.username,
|
||||||
|
'email': self.email,
|
||||||
|
'name': self.name,
|
||||||
|
'role': self.role,
|
||||||
|
'is_active': self.is_active,
|
||||||
|
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||||
|
'last_login': self.last_login.isoformat() if self.last_login else None
|
||||||
|
}
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ class QueryOptimizer:
|
|||||||
# 处理状态映射(支持中英文状态)
|
# 处理状态映射(支持中英文状态)
|
||||||
status_mapping = {
|
status_mapping = {
|
||||||
'open': ['open', '待处理', '新建', 'new'],
|
'open': ['open', '待处理', '新建', 'new'],
|
||||||
'in_progress': ['in_progress', '处理中', '进行中', 'progress', 'processing'],
|
'in_progress': ['in_progress', '处理中', '进行中', 'progress', 'processing', 'analysising', 'analyzing'],
|
||||||
'resolved': ['resolved', '已解决', '已完成'],
|
'resolved': ['resolved', '已解决', '已完成'],
|
||||||
'closed': ['closed', '已关闭', '关闭']
|
'closed': ['closed', '已关闭', '关闭']
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -142,6 +142,12 @@ class RealtimeChatManager:
|
|||||||
# 保存到数据库(每轮一条,带会话标记)
|
# 保存到数据库(每轮一条,带会话标记)
|
||||||
self._save_conversation(session_id, user_msg, assistant_msg)
|
self._save_conversation(session_id, user_msg, assistant_msg)
|
||||||
|
|
||||||
|
# 更新知识库使用次数
|
||||||
|
if knowledge_results:
|
||||||
|
used_entry_ids = [result["id"] for result in knowledge_results if result.get("id")]
|
||||||
|
if used_entry_ids:
|
||||||
|
self.knowledge_manager.update_usage_count(used_entry_ids)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"response": assistant_response["content"], # 修改为response字段
|
"response": assistant_response["content"], # 修改为response字段
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -23,15 +23,16 @@ class AISuggestionService:
|
|||||||
self.llm_config = get_config().llm
|
self.llm_config = get_config().llm
|
||||||
logger.info(f"使用LLM配置: {self.llm_config.provider} - {self.llm_config.model}")
|
logger.info(f"使用LLM配置: {self.llm_config.provider} - {self.llm_config.model}")
|
||||||
|
|
||||||
def generate_suggestion(self, tr_description: str, process_history: Optional[str] = None, vin: Optional[str] = None, existing_ai_suggestion: Optional[str] = None) -> str:
|
def generate_suggestion(self, tr_description: str, process_history: Optional[str] = None, vin: Optional[str] = None, existing_ai_suggestion: Optional[str] = None, context: str = "realtime_chat") -> str:
|
||||||
"""
|
"""
|
||||||
生成AI建议 - 参考处理过程记录生成建议
|
生成AI建议 - 根据不同上下文使用不同的提示词
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tr_description: TR描述
|
tr_description: TR描述
|
||||||
process_history: 处理过程记录(可选,用于了解当前问题状态)
|
process_history: 处理过程记录(可选,用于了解当前问题状态)
|
||||||
vin: 车架号(可选)
|
vin: 车架号(可选)
|
||||||
existing_ai_suggestion: 现有的AI建议(可选,用于判断是否是首次建议)
|
existing_ai_suggestion: 现有的AI建议(可选,用于判断是否是首次建议)
|
||||||
|
context: 调用上下文,"realtime_chat" 或 "feishu_sync"
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AI建议文本
|
AI建议文本
|
||||||
@@ -70,16 +71,11 @@ class AISuggestionService:
|
|||||||
2. 如果远程操作都无法解决,可以考虑更深入的诊断方案
|
2. 如果远程操作都无法解决,可以考虑更深入的诊断方案
|
||||||
3. 语言简洁精炼,用逗号连接,不要用序号或分行"""
|
3. 语言简洁精炼,用逗号连接,不要用序号或分行"""
|
||||||
|
|
||||||
# 构建用户消息 - 要求生成简洁的简短建议
|
# 根据上下文选择不同的提示词构建方法
|
||||||
user_message = f"""请为以下问题提供精炼的技术支持操作建议:
|
if context == "feishu_sync":
|
||||||
|
user_message = self._build_feishu_sync_prompt(tr_description, process_history, vin, existing_ai_suggestion, is_first_suggestion)
|
||||||
格式要求:
|
else:
|
||||||
1. 现状+步骤,语言精炼
|
user_message = self._build_realtime_chat_prompt(tr_description, process_history, is_first_suggestion)
|
||||||
2. 总长度控制在150字以内
|
|
||||||
|
|
||||||
{suggestion_instruction}
|
|
||||||
|
|
||||||
问题描述:{tr_description}{context_info}"""
|
|
||||||
|
|
||||||
# 创建会话
|
# 创建会话
|
||||||
session_id = chat_manager.create_session("ai_suggestion_service")
|
session_id = chat_manager.create_session("ai_suggestion_service")
|
||||||
@@ -249,6 +245,126 @@ class AISuggestionService:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _build_feishu_sync_prompt(self, tr_description: str, process_history: str = None, vin: str = None, existing_ai_suggestion: str = None, is_first_suggestion: bool = True) -> str:
|
||||||
|
"""
|
||||||
|
构建飞书同步专用的AI建议提示词
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tr_description: TR描述
|
||||||
|
process_history: 处理过程记录
|
||||||
|
vin: 车架号
|
||||||
|
existing_ai_suggestion: 现有的AI建议
|
||||||
|
is_first_suggestion: 是否是首次建议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
构建的提示词
|
||||||
|
"""
|
||||||
|
prompt = f"""请作为专业的汽车技术支持工程师,为以下工单问题提供详细的技术分析和处理建议。
|
||||||
|
|
||||||
|
问题描述:
|
||||||
|
{tr_description}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 添加处理过程记录
|
||||||
|
if process_history and process_history.strip():
|
||||||
|
prompt += f"""
|
||||||
|
当前处理进度:
|
||||||
|
{process_history}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 添加VIN信息
|
||||||
|
if vin:
|
||||||
|
prompt += f"""
|
||||||
|
车辆VIN:{vin}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 添加现有AI建议历史
|
||||||
|
if existing_ai_suggestion and existing_ai_suggestion.strip():
|
||||||
|
prompt += f"""
|
||||||
|
历史AI建议记录:
|
||||||
|
{existing_ai_suggestion}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 根据是否首次建议设置不同的要求
|
||||||
|
if is_first_suggestion:
|
||||||
|
prompt += """
|
||||||
|
要求:
|
||||||
|
1. 详细分析问题描述,识别可能的根本原因
|
||||||
|
2. 基于当前处理进度,判断问题处于哪个阶段
|
||||||
|
3. 提供具体的排查步骤和技术指导
|
||||||
|
4. 建议需要收集哪些技术信息(如日志、配置、版本等)
|
||||||
|
5. 如果需要,可以建议进站处理的具体项目
|
||||||
|
6. 语言专业,包含技术细节,方便技术人员理解
|
||||||
|
7. 建议格式要清晰,便于执行和跟踪
|
||||||
|
|
||||||
|
请提供完整的分析和建议:"""
|
||||||
|
else:
|
||||||
|
prompt += """
|
||||||
|
要求:
|
||||||
|
1. 基于已有处理记录和当前进度,分析问题进展情况
|
||||||
|
2. 判断之前的处理步骤是否有效,找出可能的遗漏点
|
||||||
|
3. 根据问题发展阶段提供更深入的技术解决方案
|
||||||
|
4. 如果远程处理无效,明确说明需要哪些线下技术支持
|
||||||
|
5. 详细说明进站后需要执行的具体诊断和修复步骤
|
||||||
|
6. 包含技术参数、工具要求和注意事项
|
||||||
|
7. 便于技术人员快速理解问题状态和下一步行动
|
||||||
|
|
||||||
|
请提供针对性的深入分析和处理建议:"""
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
def _build_realtime_chat_prompt(self, tr_description: str, process_history: str = None, is_first_suggestion: bool = True) -> str:
|
||||||
|
"""
|
||||||
|
构建实时对话专用的AI建议提示词(保持原有风格)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tr_description: TR描述
|
||||||
|
process_history: 处理过程记录
|
||||||
|
is_first_suggestion: 是否是首次建议
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
构建的提示词
|
||||||
|
"""
|
||||||
|
# 构建上下文信息
|
||||||
|
context_info = ""
|
||||||
|
if process_history and process_history.strip():
|
||||||
|
context_info = f"""
|
||||||
|
|
||||||
|
已处理的步骤:
|
||||||
|
{process_history}"""
|
||||||
|
|
||||||
|
# 根据是否为首次建议,设置不同的提示词
|
||||||
|
if is_first_suggestion:
|
||||||
|
# 首次建议:只给出一般性的排查步骤,不要提进站抓取日志
|
||||||
|
suggestion_instruction = """要求:
|
||||||
|
1. 首次给客户建议,只提供远程可操作的一般性排查步骤
|
||||||
|
2. 如检查网络、重启系统、确认配置等常见操作
|
||||||
|
3. 绝对不要提到"进站"、"抓取日志"等需要线下操作的内容
|
||||||
|
4. 语言简洁精炼,用逗号连接,不要用序号或分行"""
|
||||||
|
else:
|
||||||
|
# 后续建议:如果已有处理记录但未解决,可以考虑更深入的方案
|
||||||
|
suggestion_instruction = """要求:
|
||||||
|
1. 基于已有处理步骤,给出下一步的排查建议
|
||||||
|
2. 如果远程操作都无法解决,可以考虑更深入的诊断方案
|
||||||
|
3. 语言简洁精炼,用逗号连接,不要用序号或分行"""
|
||||||
|
|
||||||
|
# 构建用户消息 - 要求生成简洁的简短建议
|
||||||
|
user_message = f"""请为以下问题提供精炼的技术支持操作建议:
|
||||||
|
|
||||||
|
格式要求:
|
||||||
|
1. 现状+步骤,语言精炼
|
||||||
|
2. 总长度控制在150字以内
|
||||||
|
|
||||||
|
{suggestion_instruction}
|
||||||
|
|
||||||
|
问题描述:{tr_description}{context_info}"""
|
||||||
|
|
||||||
|
return user_message
|
||||||
|
|
||||||
def _clean_and_validate_response(self, content: str) -> str:
|
def _clean_and_validate_response(self, content: str) -> str:
|
||||||
"""
|
"""
|
||||||
清理和校验响应内容
|
清理和校验响应内容
|
||||||
@@ -346,13 +462,14 @@ class AISuggestionService:
|
|||||||
logger.error(f"清理响应内容失败: {e}")
|
logger.error(f"清理响应内容失败: {e}")
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def batch_generate_suggestions(self, records: List[Dict[str, Any]], limit: int = 10) -> List[Dict[str, Any]]:
|
def batch_generate_suggestions(self, records: List[Dict[str, Any]], limit: int = 10, context: str = "feishu_sync") -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
批量生成AI建议
|
批量生成AI建议
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
records: 记录列表
|
records: 记录列表
|
||||||
limit: 处理数量限制
|
limit: 处理数量限制
|
||||||
|
context: 调用上下文,"realtime_chat" 或 "feishu_sync"
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
处理后的记录列表
|
处理后的记录列表
|
||||||
@@ -377,7 +494,7 @@ class AISuggestionService:
|
|||||||
logger.info(f"记录 {record.get('record_id', i)} - 现有AI建议前100字符: {existing_ai_suggestion[:100]}")
|
logger.info(f"记录 {record.get('record_id', i)} - 现有AI建议前100字符: {existing_ai_suggestion[:100]}")
|
||||||
|
|
||||||
if tr_description:
|
if tr_description:
|
||||||
ai_suggestion = self.generate_suggestion(tr_description, process_history, vin, existing_ai_suggestion)
|
ai_suggestion = self.generate_suggestion(tr_description, process_history, vin, existing_ai_suggestion, context)
|
||||||
# 处理同一天多次更新的情况
|
# 处理同一天多次更新的情况
|
||||||
new_suggestion = self._format_ai_suggestion_with_numbering(
|
new_suggestion = self._format_ai_suggestion_with_numbering(
|
||||||
time_str, ai_suggestion, existing_ai_suggestion
|
time_str, ai_suggestion, existing_ai_suggestion
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -417,3 +417,23 @@ class KnowledgeManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取知识库统计失败: {e}")
|
logger.error(f"获取知识库统计失败: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def update_usage_count(self, entry_ids: List[int]) -> bool:
|
||||||
|
"""更新知识库条目的使用次数"""
|
||||||
|
try:
|
||||||
|
with db_manager.get_session() as session:
|
||||||
|
# 批量更新使用次数
|
||||||
|
session.query(KnowledgeEntry).filter(
|
||||||
|
KnowledgeEntry.id.in_(entry_ids)
|
||||||
|
).update({
|
||||||
|
"usage_count": KnowledgeEntry.usage_count + 1,
|
||||||
|
"updated_at": datetime.now()
|
||||||
|
})
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
logger.info(f"成功更新 {len(entry_ids)} 个知识库条目的使用次数")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新知识库使用次数失败: {e}")
|
||||||
|
return False
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
src/web/__pycache__/app.cpython-310.pyc
Normal file
BIN
src/web/__pycache__/app.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/web/__pycache__/app.cpython-312.pyc
Normal file
BIN
src/web/__pycache__/app.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/web/__pycache__/error_handlers.cpython-310.pyc
Normal file
BIN
src/web/__pycache__/error_handlers.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/web/__pycache__/service_manager.cpython-310.pyc
Normal file
BIN
src/web/__pycache__/service_manager.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/web/__pycache__/websocket_server.cpython-310.pyc
Normal file
BIN
src/web/__pycache__/websocket_server.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
401
src/web/app.py
401
src/web/app.py
@@ -32,6 +32,11 @@ from src.web.blueprints.monitoring import monitoring_bp
|
|||||||
from src.web.blueprints.system import system_bp
|
from src.web.blueprints.system import system_bp
|
||||||
from src.web.blueprints.feishu_sync import feishu_sync_bp
|
from src.web.blueprints.feishu_sync import feishu_sync_bp
|
||||||
from src.web.blueprints.core import core_bp
|
from src.web.blueprints.core import core_bp
|
||||||
|
from src.web.blueprints.auth import auth_bp
|
||||||
|
from src.web.blueprints.agent import agent_bp
|
||||||
|
from src.web.blueprints.vehicle import vehicle_bp
|
||||||
|
from src.web.blueprints.analytics import analytics_bp
|
||||||
|
from src.web.blueprints.test import test_bp
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -69,6 +74,11 @@ app.register_blueprint(monitoring_bp)
|
|||||||
app.register_blueprint(system_bp)
|
app.register_blueprint(system_bp)
|
||||||
app.register_blueprint(feishu_sync_bp)
|
app.register_blueprint(feishu_sync_bp)
|
||||||
app.register_blueprint(core_bp)
|
app.register_blueprint(core_bp)
|
||||||
|
app.register_blueprint(auth_bp)
|
||||||
|
app.register_blueprint(agent_bp)
|
||||||
|
app.register_blueprint(vehicle_bp)
|
||||||
|
app.register_blueprint(analytics_bp)
|
||||||
|
app.register_blueprint(test_bp)
|
||||||
|
|
||||||
# 页面路由
|
# 页面路由
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@@ -100,10 +110,12 @@ def uploaded_file(filename):
|
|||||||
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
|
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 核心API路由 - 已迁移到蓝图
|
# 核心API路由
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 健康检查、预警规则、监控状态等核心功能已迁移到 core 蓝图
|
# 以下路由因功能特殊性保留在主应用中:
|
||||||
# 分析数据相关功能也已迁移到 core 蓝图
|
# - Chat相关路由:使用RealtimeChatManager进行实时对话
|
||||||
|
# - 健康检查、预警规则、监控状态等核心功能已迁移到 core 蓝图
|
||||||
|
# - 分析数据相关功能已迁移到 analytics 蓝图
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 实时对话相关路由
|
# 实时对话相关路由
|
||||||
@@ -209,388 +221,13 @@ def get_active_sessions():
|
|||||||
logger.error(f"获取活跃会话失败: {e}")
|
logger.error(f"获取活跃会话失败: {e}")
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
# ============================================================================
|
# Agent相关路由已移动到 agent_bp 蓝图
|
||||||
# Agent相关API
|
|
||||||
# ============================================================================
|
|
||||||
@app.route('/api/agent/status')
|
|
||||||
def get_agent_status():
|
|
||||||
"""获取Agent状态"""
|
|
||||||
try:
|
|
||||||
status = service_manager.get_agent_assistant().get_agent_status()
|
|
||||||
return jsonify({"success": True, **status})
|
|
||||||
except Exception as e:
|
|
||||||
# 返回默认状态,避免500错误
|
|
||||||
return jsonify({
|
|
||||||
"success": False,
|
|
||||||
"status": "inactive",
|
|
||||||
"active_goals": 0,
|
|
||||||
"available_tools": 0,
|
|
||||||
"error": "Agent服务暂时不可用"
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/agent/action-history')
|
# 分析相关路由已移动到 analytics_bp 蓝图
|
||||||
def get_agent_action_history():
|
|
||||||
"""获取Agent动作执行历史"""
|
|
||||||
try:
|
|
||||||
limit = request.args.get('limit', 50, type=int)
|
|
||||||
history = service_manager.get_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'])
|
# 车辆数据相关路由已移动到 vehicle_bp 蓝图
|
||||||
def trigger_sample_action():
|
|
||||||
"""触发示例动作"""
|
|
||||||
try:
|
|
||||||
import asyncio
|
|
||||||
result = asyncio.run(service_manager.get_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'])
|
# API测试相关路由已移动到 test_bp 蓝图
|
||||||
def clear_agent_history():
|
|
||||||
"""清空Agent执行历史"""
|
|
||||||
try:
|
|
||||||
result = service_manager.get_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 = service_manager.get_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 = service_manager.get_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 = service_manager.get_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 = service_manager.get_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 = service_manager.get_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 = service_manager.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 = service_manager.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:
|
|
||||||
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,
|
|
||||||
"performance": performance
|
|
||||||
})
|
|
||||||
except Exception as e:
|
|
||||||
# 返回默认工具列表,避免500错误
|
|
||||||
return jsonify({
|
|
||||||
"success": False,
|
|
||||||
"tools": [],
|
|
||||||
"performance": {},
|
|
||||||
"error": "工具统计暂时不可用"
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/agent/tools/execute', methods=['POST'])
|
|
||||||
def execute_agent_tool():
|
|
||||||
"""执行指定的Agent工具"""
|
|
||||||
try:
|
|
||||||
data = request.get_json() or {}
|
|
||||||
tool_name = data.get('tool') or data.get('name')
|
|
||||||
parameters = data.get('parameters') or {}
|
|
||||||
if not tool_name:
|
|
||||||
return jsonify({"error": "缺少工具名称tool"}), 400
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
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
|
|
||||||
|
|
||||||
@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}
|
|
||||||
|
|
||||||
service_manager.get_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/<name>', methods=['DELETE'])
|
|
||||||
def unregister_custom_tool(name):
|
|
||||||
try:
|
|
||||||
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 - 已迁移到 core 蓝图
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
@app.route('/api/analytics/export')
|
|
||||||
def export_analytics():
|
|
||||||
"""导出分析报告"""
|
|
||||||
try:
|
|
||||||
# 生成Excel报告(使用数据库真实数据)
|
|
||||||
analytics = query_optimizer.get_analytics_optimized(30)
|
|
||||||
|
|
||||||
# 创建工作簿
|
|
||||||
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)
|
|
||||||
|
|
||||||
vehicle_mgr = service_manager.get_vehicle_manager()
|
|
||||||
if vehicle_vin:
|
|
||||||
data = vehicle_mgr.get_vehicle_data_by_vin(vehicle_vin, data_type, limit)
|
|
||||||
elif vehicle_id:
|
|
||||||
data = vehicle_mgr.get_vehicle_data(vehicle_id, data_type, limit)
|
|
||||||
else:
|
|
||||||
data = vehicle_mgr.search_vehicle_data(limit=limit)
|
|
||||||
|
|
||||||
return jsonify(data)
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({"error": str(e)}), 500
|
|
||||||
|
|
||||||
@app.route('/api/vehicle/data/vin/<vehicle_vin>/latest')
|
|
||||||
def get_latest_vehicle_data_by_vin(vehicle_vin):
|
|
||||||
"""按VIN获取车辆最新数据"""
|
|
||||||
try:
|
|
||||||
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
|
|
||||||
|
|
||||||
@app.route('/api/vehicle/data/<vehicle_id>/latest')
|
|
||||||
def get_latest_vehicle_data(vehicle_id):
|
|
||||||
"""获取车辆最新数据"""
|
|
||||||
try:
|
|
||||||
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
|
|
||||||
|
|
||||||
@app.route('/api/vehicle/data/<vehicle_id>/summary')
|
|
||||||
def get_vehicle_summary(vehicle_id):
|
|
||||||
"""获取车辆数据摘要"""
|
|
||||||
try:
|
|
||||||
summary = service_manager.get_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 = service_manager.get_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 = 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连接"""
|
|
||||||
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
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 应用启动配置
|
# 应用启动配置
|
||||||
|
|||||||
BIN
src/web/blueprints/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
src/web/blueprints/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/web/blueprints/__pycache__/agent.cpython-311.pyc
Normal file
BIN
src/web/blueprints/__pycache__/agent.cpython-311.pyc
Normal file
Binary file not shown.
BIN
src/web/blueprints/__pycache__/alerts.cpython-310.pyc
Normal file
BIN
src/web/blueprints/__pycache__/alerts.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/web/blueprints/__pycache__/analytics.cpython-311.pyc
Normal file
BIN
src/web/blueprints/__pycache__/analytics.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user