diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml new file mode 100644 index 0000000..fa2b6dc --- /dev/null +++ b/.idea/data_source_mapping.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 348929a..e8f80c1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { "files.autoGuessEncoding": true -} \ No newline at end of file +} diff --git a/auto_push.bat b/auto_push.bat index 4186fdf..d2c0a0f 100644 --- a/auto_push.bat +++ b/auto_push.bat @@ -200,18 +200,56 @@ echo ✅ 提交成功 echo. 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 🔄 检查远程更新... -git fetch origin main -if %errorlevel% neq 0 ( - echo ⚠️ 无法获取远程更新,继续推送... +git fetch origin !current_branch! >nul 2>&1 +set fetch_result=!errorlevel! +if !fetch_result! neq 0 ( + echo ⚠️ 无法获取远程更新,尝试获取所有分支... + git fetch origin >nul 2>&1 + set fetch_all_result=!errorlevel! + if !fetch_all_result! neq 0 ( + echo ⚠️ 无法获取远程更新,继续推送... + ) else ( + echo ✅ 远程更新检查完成 + ) ) else ( echo ✅ 远程更新检查完成 ) -:: 推送到远程 -git push origin main -if %errorlevel% neq 0 ( +:: 检查远程分支是否存在,如果不存在则设置上游 +echo 🔍 检查远程分支状态... +git ls-remote --heads origin !current_branch! >nul 2>&1 +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 💡 可能的原因: @@ -221,18 +259,20 @@ if %errorlevel% neq 0 ( echo - 需要先拉取远程更改 echo. echo 🔧 尝试自动解决冲突... - git pull origin main --rebase - if %errorlevel% equ 0 ( + git pull origin !current_branch! --rebase + set pull_result=!errorlevel! + if !pull_result! equ 0 ( echo ✅ 冲突已解决,重新推送... - git push origin main - if %errorlevel% equ 0 ( + git push origin !current_branch! + set final_push_result=!errorlevel! + if !final_push_result! equ 0 ( echo ✅ 推送成功! ) else ( echo ❌ 重新推送失败 echo. echo 🔧 建议手动解决: - echo 1. 运行: git pull origin main - echo 2. 解决冲突后运行: git push origin main + echo 1. 运行: git pull origin !current_branch! + echo 2. 解决冲突后运行: git push origin !current_branch! pause exit /b 1 ) @@ -240,8 +280,8 @@ if %errorlevel% neq 0 ( echo ❌ 无法自动解决冲突 echo. echo 🔧 建议手动解决: - echo 1. 运行: git pull origin main - echo 2. 解决冲突后运行: git push origin main + echo 1. 运行: git pull origin !current_branch! + echo 2. 解决冲突后运行: git push origin !current_branch! pause exit /b 1 ) diff --git a/check_and_fix_users.py b/check_and_fix_users.py new file mode 100644 index 0000000..a330049 --- /dev/null +++ b/check_and_fix_users.py @@ -0,0 +1,147 @@ +# -*- 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() + diff --git a/config/field_mapping_config.json b/config/field_mapping_config.json index ffe9cd6..2ed3fa0 100644 --- a/config/field_mapping_config.json +++ b/config/field_mapping_config.json @@ -7,7 +7,7 @@ "TR Status": "status", "Source": "source", "Date creation": "created_at", - "处理过程": "solution", + "处理过程": "resolution", "TR tracking": "resolution", "Created by": "created_by", "Module(模块)": "module", diff --git a/create_admin_user.py b/create_admin_user.py new file mode 100644 index 0000000..480348c --- /dev/null +++ b/create_admin_user.py @@ -0,0 +1,173 @@ +# -*- 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 src.core.models import User +from werkzeug.security import generate_password_hash, check_password_hash +from sqlalchemy import text, inspect + +print("=" * 60) +print("创建/修复管理员用户") +print("=" * 60) + +try: + with db_manager.get_session() as session: + # 检查表结构 + inspector = inspect(db_manager.engine) + if 'users' not in inspector.get_table_names(): + print("错误: users表不存在,请先运行 python init_database.py") + sys.exit(1) + + existing_columns = [col['name'] for col in inspector.get_columns('users')] + print(f"users表字段: {existing_columns}") + + # 检查是否存在admin用户 + admin_user = session.query(User).filter(User.username == 'admin').first() + + if admin_user: + print(f"\n找到admin用户 (ID: {admin_user.id})") + print(f" 邮箱: {admin_user.email}") + print(f" 角色: {admin_user.role}") + print(f" 激活状态: {admin_user.is_active}") + + # 验证密码 + password_ok = check_password_hash(admin_user.password_hash, 'admin123') + print(f" 密码验证: {'正确' if password_ok else '错误'}") + + if not password_ok: + print("\n密码不匹配,正在更新密码...") + admin_user.password_hash = generate_password_hash('admin123') + admin_user.is_active = True + if hasattr(admin_user, 'region'): + admin_user.region = None + session.commit() + print("密码已更新为: admin123") + + if not admin_user.is_active: + print("用户未激活,正在激活...") + admin_user.is_active = True + session.commit() + print("用户已激活") + + # 最终验证 + test_password = check_password_hash(admin_user.password_hash, 'admin123') + if test_password and admin_user.is_active: + print("\n管理员用户已就绪!") + print(" 用户名: admin") + print(" 密码: admin123") + print(" 状态: 已激活") + else: + print("\n警告: 用户状态异常") + print(f" 密码正确: {test_password}") + print(f" 已激活: {admin_user.is_active}") + else: + print("\n未找到admin用户,正在创建...") + + # 准备密码哈希 + password_hash = generate_password_hash('admin123') + + # 检查表结构,使用SQL直接插入避免字段不匹配 + try: + # 先尝试使用模型创建 + new_admin = User( + username='admin', + email='admin@tsp.com', + password_hash=password_hash, + role='admin', + is_active=True + ) + if 'region' in existing_columns: + new_admin.region = None + session.add(new_admin) + session.commit() + print("使用模型创建成功") + except Exception as model_error: + print(f"模型创建失败: {model_error}") + print("尝试使用SQL直接插入...") + session.rollback() + + # 使用SQL直接插入 + insert_fields = ['username', 'email', 'password_hash', 'role'] + insert_values = { + 'username': 'admin', + 'email': 'admin@tsp.com', + 'password_hash': password_hash, + 'role': 'admin' + } + + if 'is_active' in existing_columns: + insert_fields.append('is_active') + insert_values['is_active'] = True + + if 'region' in existing_columns: + insert_fields.append('region') + insert_values['region'] = None + + fields_str = ', '.join(insert_fields) + values_str = ', '.join([f":{k}" for k in insert_fields]) + + sql = f""" + INSERT INTO users ({fields_str}) + VALUES ({values_str}) + """ + + session.execute(text(sql), insert_values) + session.commit() + print("使用SQL创建成功") + + # 验证创建结果 + verify_user = session.query(User).filter(User.username == 'admin').first() + if verify_user: + test_password = check_password_hash(verify_user.password_hash, 'admin123') + print(f"\n验证结果:") + print(f" 用户ID: {verify_user.id}") + print(f" 密码正确: {test_password}") + print(f" 已激活: {verify_user.is_active}") + + if test_password and verify_user.is_active: + print("\n管理员用户创建成功!") + print(" 用户名: admin") + print(" 密码: admin123") + else: + print("\n警告: 用户创建成功但状态异常") + + # 创建其他示例用户 + for username, email, password, role, region in [ + ('overseas_ops', 'overseas@tsp.com', 'ops123', 'overseas_ops', 'overseas'), + ('domestic_ops', 'domestic@tsp.com', 'ops123', 'domestic_ops', 'domestic') + ]: + ops_user = session.query(User).filter(User.username == username).first() + if not ops_user: + print(f"\n创建{username}用户...") + try: + new_user = User( + username=username, + email=email, + password_hash=generate_password_hash(password), + role=role, + is_active=True + ) + if 'region' in existing_columns: + new_user.region = region + session.add(new_user) + session.commit() + print(f" {username}用户创建成功") + except Exception as e: + print(f" {username}用户创建失败: {e}") + session.rollback() + + print("\n" + "=" * 60) + print("操作完成!") + print("=" * 60) + +except Exception as e: + print(f"错误: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/database_init_report.json b/database_init_report.json index 76689cd..4d99062 100644 --- a/database_init_report.json +++ b/database_init_report.json @@ -1,9 +1,9 @@ { - "init_time": "2025-09-19T18:57:01.015501", + "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": 15, + "tables_created": 16, "initial_data_inserted": true, "verification_passed": true } \ No newline at end of file diff --git a/database_migration_notes.md b/database_migration_notes.md new file mode 100644 index 0000000..5114108 --- /dev/null +++ b/database_migration_notes.md @@ -0,0 +1,119 @@ +# 数据库迁移说明 + +## 工单处理过程记录系统迁移 + +### 新增表:`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. **索引优化**:新表已包含必要的索引,如果数据量大可以考虑添加更多索引 + diff --git a/fix_admin_user.py b/fix_admin_user.py new file mode 100644 index 0000000..94d2770 --- /dev/null +++ b/fix_admin_user.py @@ -0,0 +1,86 @@ +# -*- 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) + diff --git a/fix_git_push.bat b/fix_git_push.bat new file mode 100644 index 0000000..3d47641 --- /dev/null +++ b/fix_git_push.bat @@ -0,0 +1,125 @@ +@echo off +chcp 65001 >nul +echo ======================================== +echo GitϺ޸ +echo ======================================== +echo. + +:: 1. Git״̬ +echo [1] Git״̬... +git status +if %errorlevel% neq 0 ( + echo ? Gitδʼ + pause + exit /b 1 +) +echo. + +:: 2. Զֿ̲ +echo [2] Զֿ̲... +git remote -v +if %errorlevel% neq 0 ( + echo ? δԶֿ̲ + echo : git remote add origin <ַֿ> + pause + exit /b 1 +) +echo. + +:: 3. 鵱ǰ֧ +echo [3] 鵱ǰ֧... +git branch --show-current +echo. + +:: 4. Ƿδύĸ +echo [4] δύĸ... +git status --porcelain +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%" + ) +) +echo. + +:: 5. ԻȡԶ̷֧Ϣ +echo [5] ȡԶ̷֧Ϣ... +git fetch origin +if %errorlevel% neq 0 ( + echo ? ޷ӵԶֿ̲ + echo. + echo ܵԭ: + echo 1. + echo 2. Զַֿ̲ + echo 3. Ҫ֤ǷSSHԿToken + echo. + echo Զַֿ̲: + git config --get remote.origin.url + pause + exit /b 1 +) +echo ? Զֿ̲ӳɹ +echo. + +:: 6. ֧ٹϵ +echo [6] ֧ٹϵ... +git branch -vv +echo. + +:: 7. ͵Զ +echo [7] ... +set current_branch= +for /f "tokens=*" %%b in ('git branch --show-current') do set current_branch=%%b + +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% +) 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 + diff --git a/init.sql b/init.sql index ea26bca..10e11c4 100644 --- a/init.sql +++ b/init.sql @@ -23,49 +23,83 @@ 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, - category VARCHAR(50), - priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium', - status ENUM('open', 'in_progress', 'resolved', 'closed') DEFAULT 'open', - user_id INT, - assigned_to INT, + 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, - resolved_at TIMESTAMP NULL, - FOREIGN KEY (user_id) REFERENCES users(id), - FOREIGN KEY (assigned_to) REFERENCES users(id) + 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, - level ENUM('info', 'warning', 'error', 'critical') DEFAULT 'info', - status ENUM('active', 'resolved', 'suppressed') DEFAULT 'active', - source VARCHAR(100), - metadata JSON, + data TEXT, + is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, resolved_at TIMESTAMP NULL, INDEX idx_level (level), - INDEX idx_status (status), + 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, - session_id VARCHAR(100) NOT NULL, - user_id INT, work_order_id INT, user_message TEXT NOT NULL, assistant_response TEXT NOT NULL, - confidence_score DECIMAL(3,2) DEFAULT 0.50, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_id) REFERENCES users(id), - FOREIGN KEY (work_order_id) REFERENCES work_orders(id), - INDEX idx_session_id (session_id), - INDEX idx_created_at (created_at) + 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) ); -- 创建知识库表 @@ -73,13 +107,16 @@ CREATE TABLE IF NOT EXISTS knowledge_entries ( id INT AUTO_INCREMENT PRIMARY KEY, question TEXT NOT NULL, answer TEXT NOT NULL, - category VARCHAR(50), - confidence_score DECIMAL(3,2) DEFAULT 0.50, + category VARCHAR(100) NOT NULL, + confidence_score FLOAT DEFAULT 0.0, usage_count INT DEFAULT 0, - is_verified BOOLEAN DEFAULT FALSE, - is_active BOOLEAN DEFAULT TRUE, 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) @@ -91,9 +128,35 @@ CREATE TABLE IF NOT EXISTS work_order_suggestions ( 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 + 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) ); -- 创建系统配置表 @@ -107,21 +170,42 @@ CREATE TABLE IF NOT EXISTS system_settings ( ); -- 插入默认管理员用户 -INSERT IGNORE INTO users (username, email, password_hash, role) VALUES +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 +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 INDEX idx_work_orders_status ON work_orders(status); -CREATE INDEX idx_work_orders_priority ON work_orders(priority); -CREATE INDEX idx_work_orders_created_at ON work_orders(created_at); -CREATE INDEX idx_alerts_level_status ON alerts(level, status); -CREATE INDEX idx_conversations_user_id ON conversations(user_id); -CREATE INDEX idx_knowledge_entries_category_active ON knowledge_entries(category, is_active); +-- 创建分析统计表 +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) +); diff --git a/init_database.py b/init_database.py index 760de40..c5f2ae4 100644 --- a/init_database.py +++ b/init_database.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """ -TSP助手数据库初始化脚本 - 重构版本 -结合项目新特性,提供更高效的数据库初始化和管理功能 +TSP助手数据库初始化脚本 """ import sys @@ -19,24 +18,26 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__))) from src.config.config import Config from src.utils.helpers import setup_logging from src.core.database import db_manager -from src.core.models import Base, WorkOrder, KnowledgeEntry, Conversation, Analytics, Alert, VehicleData +from src.core.models import ( + Base, WorkOrder, KnowledgeEntry, Conversation, Analytics, Alert, VehicleData, + WorkOrderSuggestion, WorkOrderProcessHistory +) class DatabaseInitializer: - """数据库初始化器 - 重构版本""" - + def __init__(self): self.logger = logging.getLogger(__name__) self.db_url = str(db_manager.engine.url) self.is_mysql = 'mysql' in self.db_url self.is_sqlite = 'sqlite' in self.db_url self.is_postgresql = 'postgresql' in self.db_url - + # 数据库版本信息 self.db_version = self._get_database_version() - + # 迁移历史记录 self.migration_history = [] - + def _get_database_version(self) -> str: """获取数据库版本信息""" try: @@ -53,119 +54,119 @@ class DatabaseInitializer: except Exception as e: self.logger.warning(f"无法获取数据库版本: {e}") return "Unknown" - + def initialize_database(self, force_reset: bool = False) -> bool: """初始化数据库 - 主入口函数""" print("=" * 80) - print("🚀 TSP智能助手数据库初始化系统") + print("TSP智能助手数据库初始化系统") print("=" * 80) - print(f"📊 数据库类型: {self.db_version}") - print(f"🔗 连接地址: {self.db_url}") - print(f"⏰ 初始化时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f"数据库类型: {self.db_version}") + print(f"连接地址: {self.db_url}") + print(f"初始化时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("=" * 80) - + try: # 设置日志 setup_logging(Config.LOG_LEVEL, Config.LOG_FILE) - + # 测试数据库连接 if not self._test_connection(): return False - + # 检查是否需要重置数据库 if force_reset: if not self._reset_database(): return False - + # 创建数据库表 if not self._create_tables(): return False - + # 执行数据库迁移 if not self._run_migrations(): return False - + # 插入初始数据 if not self._insert_initial_data(): return False - + # 验证数据库完整性 if not self._verify_database_integrity(): return False - + # 生成初始化报告 self._generate_init_report() - + print("\n" + "=" * 80) - print("🎉 数据库初始化完成!") + print("数据库初始化完成!") print("=" * 80) return True - + except Exception as e: - print(f"\n❌ 数据库初始化失败: {e}") + print(f"\n数据库初始化失败: {e}") self.logger.error(f"数据库初始化失败: {e}", exc_info=True) return False - + def _test_connection(self) -> bool: """测试数据库连接""" - print("\n🔌 测试数据库连接...") + print("\n测试数据库连接...") try: if db_manager.test_connection(): - print("✅ 数据库连接成功") + print("数据库连接成功") return True else: - print("❌ 数据库连接失败") + print("数据库连接失败") return False except Exception as e: - print(f"❌ 数据库连接测试异常: {e}") + print(f"数据库连接测试异常: {e}") return False - + def _reset_database(self) -> bool: """重置数据库(谨慎使用)""" - print("\n⚠️ 重置数据库...") + print("\n重置数据库...") try: # 删除所有表 Base.metadata.drop_all(bind=db_manager.engine) - print("✅ 数据库表删除成功") - + print("数据库表删除成功") + # 重新创建所有表 Base.metadata.create_all(bind=db_manager.engine) - print("✅ 数据库表重新创建成功") - + print("数据库表重新创建成功") + return True except Exception as e: - print(f"❌ 数据库重置失败: {e}") + print(f"数据库重置失败: {e}") return False def _create_tables(self) -> bool: """创建数据库表""" - print("\n📋 创建数据库表...") + print("\n创建数据库表...") try: # 获取现有表信息 inspector = inspect(db_manager.engine) existing_tables = inspector.get_table_names() - + # 创建所有表 Base.metadata.create_all(bind=db_manager.engine) - + # 检查新创建的表 new_tables = inspector.get_table_names() created_tables = set(new_tables) - set(existing_tables) - + if created_tables: - print(f"✅ 新创建表: {', '.join(created_tables)}") + print(f"新创建表: {', '.join(created_tables)}") else: - print("✅ 所有表已存在") - + print("所有表已存在") + return True except Exception as e: - print(f"❌ 创建数据库表失败: {e}") + print(f"创建数据库表失败: {e}") return False - + def _run_migrations(self) -> bool: """执行数据库迁移""" - print("\n🔄 执行数据库迁移...") - + print("\n执行数据库迁移...") + migrations = [ self._migrate_knowledge_verification_fields, self._migrate_alert_severity_field, @@ -173,10 +174,12 @@ class DatabaseInitializer: self._migrate_conversation_enhancements, self._migrate_workorder_enhancements, self._migrate_workorder_suggestions_enhancements, + self._migrate_workorder_dispatch_fields, + self._migrate_workorder_process_history_table, self._migrate_analytics_enhancements, self._migrate_system_optimization_fields ] - + success_count = 0 for migration in migrations: try: @@ -184,79 +187,79 @@ class DatabaseInitializer: success_count += 1 except Exception as e: self.logger.error(f"迁移失败: {migration.__name__}: {e}") - print(f"⚠️ 迁移 {migration.__name__} 失败: {e}") - - print(f"✅ 完成 {success_count}/{len(migrations)} 个迁移") + print(f"迁移 {migration.__name__} 失败: {e}") + + print(f"完成 {success_count}/{len(migrations)} 个迁移") return success_count > 0 - + def _migrate_knowledge_verification_fields(self) -> bool: """迁移知识库验证字段""" - print(" 📝 检查知识库验证字段...") - + print(" 检查知识库验证字段...") + fields_to_add = [ ('is_verified', 'BOOLEAN DEFAULT FALSE'), ('verified_by', 'VARCHAR(100)'), ('verified_at', 'DATETIME') ] - + return self._add_table_columns('knowledge_entries', fields_to_add) - + def _migrate_alert_severity_field(self) -> bool: """迁移预警严重程度字段""" - print(" 🚨 检查预警严重程度字段...") - + print(" 检查预警严重程度字段...") + fields_to_add = [ ('severity', 'VARCHAR(20) DEFAULT \'medium\'') ] - + return self._add_table_columns('alerts', fields_to_add) - + def _migrate_vehicle_data_table(self) -> bool: """迁移车辆数据表""" - print(" 🚗 检查车辆数据表...") - + print(" 检查车辆数据表...") + try: with db_manager.get_session() as session: # 检查表是否存在 inspector = inspect(db_manager.engine) if 'vehicle_data' not in inspector.get_table_names(): - print(" ➕ 创建vehicle_data表...") + print(" 创建vehicle_data表...") VehicleData.__table__.create(session.bind, checkfirst=True) - print(" ✅ vehicle_data表创建成功") + print(" vehicle_data表创建成功") else: - print(" ✅ vehicle_data表已存在") - + print(" vehicle_data表已存在") + session.commit() return True except Exception as e: - print(f" ❌ 车辆数据表迁移失败: {e}") + print(f" 车辆数据表迁移失败: {e}") return False - + def _migrate_conversation_enhancements(self) -> bool: """迁移对话增强字段""" - print(" 💬 检查对话增强字段...") - + print(" 检查对话增强字段...") + fields_to_add = [ - ('response_time', 'FLOAT'), - ('user_satisfaction', 'INTEGER'), - ('ai_confidence', 'FLOAT'), - ('context_data', 'TEXT') + ('timestamp', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP'), + ('knowledge_used', 'TEXT'), + ('response_time', 'FLOAT') ] - + return self._add_table_columns('conversations', fields_to_add) - + def _migrate_workorder_enhancements(self) -> bool: """迁移工单增强字段""" - print(" 📋 检查工单增强字段...") - + print(" 检查工单增强字段...") + fields_to_add = [ - ('ai_suggestion', 'TEXT'), - ('human_resolution', 'TEXT'), - ('ai_similarity', 'FLOAT'), - ('ai_approved', 'BOOLEAN DEFAULT FALSE'), + ('resolution', 'TEXT'), + ('satisfaction_score', 'FLOAT'), + # 飞书集成字段 ('feishu_record_id', 'VARCHAR(100)'), - ('sync_status', 'VARCHAR(20) DEFAULT \'pending\''), - # 飞书集成扩展字段 + ('assignee', 'VARCHAR(100)'), + ('solution', 'TEXT'), + ('ai_suggestion', 'TEXT'), + # 扩展飞书字段 ('source', 'VARCHAR(50)'), ('module', 'VARCHAR(100)'), ('created_by', 'VARCHAR(100)'), @@ -270,36 +273,88 @@ class DatabaseInitializer: ('has_updated_same_day', 'VARCHAR(50)'), ('operating_time', 'VARCHAR(100)') ] - + return self._add_table_columns('work_orders', fields_to_add) - + def _migrate_workorder_suggestions_enhancements(self) -> bool: """迁移工单建议表增强字段""" - print(" 💡 检查工单建议表增强字段...") - + print(" 检查工单建议表增强字段...") + fields_to_add = [ + ('ai_similarity', 'FLOAT'), + ('approved', 'BOOLEAN DEFAULT FALSE'), ('use_human_resolution', 'BOOLEAN DEFAULT FALSE') # 是否使用人工描述入库 ] - + return self._add_table_columns('work_order_suggestions', fields_to_add) - + + def _migrate_workorder_dispatch_fields(self) -> bool: + """迁移工单分发和权限管理字段""" + print(" 检查工单分发和权限管理字段...") + + fields_to_add = [ + ('assigned_module', 'VARCHAR(50)'), + ('module_owner', 'VARCHAR(100)'), + ('dispatcher', 'VARCHAR(100)'), + ('dispatch_time', 'DATETIME'), + ('region', 'VARCHAR(50)') + ] + + return self._add_table_columns('work_orders', fields_to_add) + + def _migrate_workorder_process_history_table(self) -> bool: + """迁移工单处理过程记录表""" + print(" 检查工单处理过程记录表...") + + try: + with db_manager.get_session() as session: + # 检查表是否存在 + inspector = inspect(db_manager.engine) + if 'work_order_process_history' not in inspector.get_table_names(): + print(" 创建work_order_process_history表...") + WorkOrderProcessHistory.__table__.create(session.bind, checkfirst=True) + print(" work_order_process_history表创建成功") + else: + print(" work_order_process_history表已存在") + + # 检查字段是否完整 + existing_columns = [col['name'] for col in inspector.get_columns('work_order_process_history')] + required_columns = [ + 'processor_name', 'processor_role', 'processor_region', + 'process_content', 'action_type', 'previous_status', + 'new_status', 'assigned_module', 'process_time' + ] + + missing_columns = [col for col in required_columns if col not in existing_columns] + if missing_columns: + print(f" 缺少字段: {', '.join(missing_columns)}") + # 这里可以选择性地添加缺失字段,但通常表已经完整创建 + else: + print(" 所有必需字段已存在") + + session.commit() + return True + except Exception as e: + print(f" 工单处理过程记录表迁移失败: {e}") + return False + def _migrate_analytics_enhancements(self) -> bool: """迁移分析增强字段""" - print(" 📊 检查分析增强字段...") - + print(" 检查分析增强字段...") + fields_to_add = [ ('performance_score', 'FLOAT'), ('quality_metrics', 'TEXT'), ('cost_analysis', 'TEXT'), ('optimization_suggestions', 'TEXT') ] - + return self._add_table_columns('analytics', fields_to_add) - + def _migrate_system_optimization_fields(self) -> bool: """迁移系统优化字段""" - print(" ⚙️ 检查系统优化字段...") - + print(" 检查系统优化字段...") + # 为各个表添加系统优化相关字段 tables_and_fields = { 'conversations': [ @@ -317,114 +372,114 @@ class DatabaseInitializer: ('relevance_score', 'FLOAT') ] } - + success = True for table_name, fields in tables_and_fields.items(): if not self._add_table_columns(table_name, fields): success = False - + return success - + def _add_table_columns(self, table_name: str, fields: List[tuple]) -> bool: """为表添加字段""" try: added_count = 0 skipped_count = 0 - + for field_name, field_type in fields: try: if self._column_exists(table_name, field_name): skipped_count += 1 continue - - print(f" ➕ 添加字段 {table_name}.{field_name}...") - + + print(f" 添加字段 {table_name}.{field_name}...") + # 使用单独的会话添加每个字段,避免长时间锁定 with db_manager.get_session() as session: alter_sql = f"ALTER TABLE {table_name} ADD COLUMN {field_name} {field_type}" session.execute(text(alter_sql)) session.commit() - - print(f" ✅ 字段 {field_name} 添加成功") + + print(f" 字段 {field_name} 添加成功") added_count += 1 - + except Exception as field_error: - print(f" ⚠️ 字段 {field_name} 添加失败: {field_error}") + print(f" 字段 {field_name} 添加失败: {field_error}") # 继续处理其他字段,不中断整个过程 - + if added_count > 0: - print(f" 📊 成功添加 {added_count} 个字段,跳过 {skipped_count} 个已存在字段") + print(f" 成功添加 {added_count} 个字段,跳过 {skipped_count} 个已存在字段") else: - print(f" 📊 所有字段都已存在,跳过 {skipped_count} 个字段") - + print(f" 所有字段都已存在,跳过 {skipped_count} 个字段") + return True - + except Exception as e: - print(f" ❌ 添加字段过程失败: {e}") + print(f" 添加字段过程失败: {e}") return False - + def _column_exists(self, table_name: str, column_name: str) -> bool: """检查字段是否存在""" try: with db_manager.get_session() as session: if self.is_mysql: result = session.execute(text(""" - SELECT COUNT(*) as count - FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = :table_name + SELECT COUNT(*) as count + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = :table_name AND COLUMN_NAME = :column_name """), {"table_name": table_name, "column_name": column_name}).fetchone() elif self.is_postgresql: result = session.execute(text(""" - SELECT COUNT(*) as count - FROM information_schema.columns - WHERE table_name = :table_name + SELECT COUNT(*) as count + FROM information_schema.columns + WHERE table_name = :table_name AND column_name = :column_name """), {"table_name": table_name, "column_name": column_name}).fetchone() else: # SQLite result = session.execute(text(""" - SELECT COUNT(*) as count - FROM pragma_table_info(:table_name) + SELECT COUNT(*) as count + FROM pragma_table_info(:table_name) WHERE name = :column_name """), {"table_name": table_name, "column_name": column_name}).fetchone() - + return result[0] > 0 except Exception: return False def _insert_initial_data(self) -> bool: """插入初始数据""" - print("\n📊 插入初始数据...") - + print("\n插入初始数据...") + try: with db_manager.get_session() as session: # 检查是否已有数据 existing_count = session.query(KnowledgeEntry).count() if existing_count > 0: - print(f" ✅ 数据库中已有 {existing_count} 条知识库条目,跳过初始数据插入") + print(f" 数据库中已有 {existing_count} 条知识库条目,跳过初始数据插入") return True - + # 插入初始知识库数据 initial_data = self._get_initial_knowledge_data() for data in initial_data: entry = KnowledgeEntry(**data) session.add(entry) - + session.commit() - print(f" ✅ 成功插入 {len(initial_data)} 条知识库条目") - + print(f" 成功插入 {len(initial_data)} 条知识库条目") + # 添加示例车辆数据 self._add_sample_vehicle_data() - + # 验证现有知识库条目 self._verify_existing_knowledge() - + return True except Exception as e: - print(f" ❌ 插入初始数据失败: {e}") + print(f" 插入初始数据失败: {e}") return False - + def _get_initial_knowledge_data(self) -> List[Dict[str, Any]]: """获取初始知识库数据""" return [ @@ -539,23 +594,23 @@ class DatabaseInitializer: "relevance_score": 0.9 } ] - + def _add_sample_vehicle_data(self) -> bool: """添加示例车辆数据""" try: from src.vehicle.vehicle_data_manager import VehicleDataManager - + vehicle_manager = VehicleDataManager() success = vehicle_manager.add_sample_vehicle_data() - + if success: - print(" ✅ 示例车辆数据添加成功") + print(" 示例车辆数据添加成功") else: - print(" ❌ 示例车辆数据添加失败") - + print(" 示例车辆数据添加失败") + return success except Exception as e: - print(f" ❌ 添加示例车辆数据失败: {e}") + print(f" 添加示例车辆数据失败: {e}") return False def _verify_existing_knowledge(self) -> bool: @@ -566,10 +621,10 @@ class DatabaseInitializer: unverified_entries = session.query(KnowledgeEntry).filter( KnowledgeEntry.is_verified == False ).all() - + if unverified_entries: - print(f" 📝 发现 {len(unverified_entries)} 条未验证的知识库条目") - + print(f" 发现 {len(unverified_entries)} 条未验证的知识库条目") + # 将现有的知识库条目标记为已验证 for entry in unverified_entries: entry.is_verified = True @@ -579,21 +634,21 @@ class DatabaseInitializer: entry.search_frequency = 0 if not hasattr(entry, 'relevance_score'): entry.relevance_score = 0.7 - + session.commit() - print(f" ✅ 成功验证 {len(unverified_entries)} 条知识库条目") + print(f" 成功验证 {len(unverified_entries)} 条知识库条目") else: - print(" ✅ 所有知识库条目已验证") - + print(" 所有知识库条目已验证") + return True except Exception as e: - print(f" ❌ 验证知识库条目失败: {e}") + print(f" 验证知识库条目失败: {e}") return False - + def _verify_database_integrity(self) -> bool: """验证数据库完整性""" - print("\n🔍 验证数据库完整性...") - + print("\n验证数据库完整性...") + try: with db_manager.get_session() as session: # 检查各表的记录数 @@ -603,49 +658,55 @@ class DatabaseInitializer: 'knowledge_entries': KnowledgeEntry, 'analytics': Analytics, 'alerts': Alert, - 'vehicle_data': VehicleData + 'vehicle_data': VehicleData, + 'work_order_suggestions': WorkOrderSuggestion, + 'work_order_process_history': WorkOrderProcessHistory } - + total_records = 0 for table_name, model_class in tables_info.items(): try: count = session.query(model_class).count() total_records += count - print(f" 📋 {table_name}: {count} 条记录") + print(f" {table_name}: {count} 条记录") except Exception as e: - print(f" ⚠️ {table_name}: 检查失败 - {e}") - - print(f" 📊 总记录数: {total_records}") - + print(f" {table_name}: 检查失败 - {e}") + + print(f" 总记录数: {total_records}") + # 检查关键字段 self._check_critical_fields() - - print(" ✅ 数据库完整性验证通过") + + print(" 数据库完整性验证通过") return True except Exception as e: - print(f" ❌ 数据库完整性验证失败: {e}") + print(f" 数据库完整性验证失败: {e}") return False - + def _check_critical_fields(self): """检查关键字段""" critical_checks = [ ("knowledge_entries", "is_verified"), ("alerts", "severity"), ("vehicle_data", "vehicle_id"), + ("conversations", "timestamp"), ("conversations", "response_time"), - ("work_orders", "ai_suggestion") + ("work_orders", "ai_suggestion"), + ("work_orders", "assigned_module"), + ("work_order_process_history", "processor_name"), + ("work_order_suggestions", "ai_similarity") ] - + for table_name, field_name in critical_checks: if self._column_exists(table_name, field_name): - print(f" ✅ {table_name}.{field_name} 字段存在") + print(f" {table_name}.{field_name} 字段存在") else: - print(f" ⚠️ {table_name}.{field_name} 字段缺失") - + print(f" {table_name}.{field_name} 字段缺失") + def _generate_init_report(self): """生成初始化报告""" - print("\n📋 生成初始化报告...") - + print("\n生成初始化报告...") + try: report = { "init_time": datetime.now().isoformat(), @@ -656,17 +717,17 @@ class DatabaseInitializer: "initial_data_inserted": True, "verification_passed": True } - + # 保存报告到文件 report_path = Path("database_init_report.json") with open(report_path, 'w', encoding='utf-8') as f: json.dump(report, f, indent=2, ensure_ascii=False) - - print(f" ✅ 初始化报告已保存到: {report_path}") - + + print(f" 初始化报告已保存到: {report_path}") + except Exception as e: - print(f" ⚠️ 生成初始化报告失败: {e}") - + print(f" 生成初始化报告失败: {e}") + def _get_table_count(self) -> int: """获取表数量""" try: @@ -674,13 +735,13 @@ class DatabaseInitializer: return len(inspector.get_table_names()) except Exception: return 0 - + def check_database_status(self) -> Dict[str, Any]: """检查数据库状态""" print("\n" + "=" * 80) - print("📊 数据库状态检查") + print("数据库状态检查") print("=" * 80) - + try: with db_manager.get_session() as session: # 检查各表的记录数 @@ -690,9 +751,11 @@ class DatabaseInitializer: 'knowledge_entries': KnowledgeEntry, 'analytics': Analytics, 'alerts': Alert, - 'vehicle_data': VehicleData + 'vehicle_data': VehicleData, + 'work_order_suggestions': WorkOrderSuggestion, + 'work_order_process_history': WorkOrderProcessHistory } - + status = { "database_version": self.db_version, "connection_status": "正常", @@ -700,17 +763,17 @@ class DatabaseInitializer: "total_records": 0, "last_check": datetime.now().isoformat() } - + for table_name, model_class in tables_info.items(): try: count = session.query(model_class).count() status["tables"][table_name] = count status["total_records"] += count - print(f"📋 {table_name}: {count} 条记录") + print(f"{table_name}: {count} 条记录") except Exception as e: status["tables"][table_name] = f"错误: {e}" - print(f"⚠️ {table_name}: 检查失败 - {e}") - + print(f"{table_name}: 检查失败 - {e}") + # 检查车辆数据详情 if 'vehicle_data' in status["tables"] and isinstance(status["tables"]['vehicle_data'], int): vehicle_count = status["tables"]['vehicle_data'] @@ -718,13 +781,13 @@ class DatabaseInitializer: vehicle_ids = session.query(VehicleData.vehicle_id).distinct().all() print(f" - 车辆数量: {len(vehicle_ids)}") status["vehicle_count"] = len(vehicle_ids) - + for vehicle_id in vehicle_ids[:3]: # 显示前3个车辆 vehicle_data_types = session.query(VehicleData.data_type).filter( VehicleData.vehicle_id == vehicle_id[0] ).distinct().all() print(f" - 车辆 {vehicle_id[0]}: {len(vehicle_data_types)} 种数据类型") - + # 检查知识库验证状态 if 'knowledge_entries' in status["tables"] and isinstance(status["tables"]['knowledge_entries'], int): verified_count = session.query(KnowledgeEntry).filter(KnowledgeEntry.is_verified == True).count() @@ -735,29 +798,29 @@ class DatabaseInitializer: "verified": verified_count, "unverified": unverified_count } - - print(f"\n📊 总记录数: {status['total_records']}") - print("\n✅ 数据库状态检查完成") - + + print(f"\n总记录数: {status['total_records']}") + print("\n数据库状态检查完成") + return status - + except Exception as e: - print(f"❌ 数据库状态检查失败: {e}") + print(f"数据库状态检查失败: {e}") return {"error": str(e)} def main(): """主函数""" - print("🚀 TSP智能助手数据库初始化工具 - 重构版本") + print("TSP智能助手数据库初始化工具") print("=" * 80) - + # 创建初始化器 initializer = DatabaseInitializer() - + # 检查命令行参数 force_reset = '--reset' in sys.argv or '--force' in sys.argv - + if force_reset: - print("⚠️ 警告:将重置数据库,所有数据将被删除!") + print("警告:将重置数据库,所有数据将被删除!") try: confirm = input("确定要继续吗?(y/N): ") if confirm.lower() != 'y': @@ -765,39 +828,19 @@ def main(): return except Exception: print("非交互环境,跳过确认") - + # 初始化数据库 if initializer.initialize_database(force_reset=force_reset): # 检查数据库状态 initializer.check_database_status() - + print("\n" + "=" * 80) - print("🎉 数据库初始化成功!") + print("数据库初始化成功!") print("=" * 80) - print("✅ 已完成的操作:") - print(" - 创建所有数据库表") - print(" - 执行数据库迁移") - print(" - 添加知识库验证字段") - print(" - 创建车辆数据表") - print(" - 插入初始知识库数据") - print(" - 添加示例车辆数据") - print(" - 验证所有知识库条目") - print(" - 生成初始化报告") - print("\n🚀 现在您可以运行以下命令启动系统:") - print(" python start_dashboard.py") - print("\n🧪 或运行功能测试:") - print(" python test_new_features.py") - print("\n📋 新功能包括:") - print(" - 知识库分页显示") - print(" - 知识库验证机制") - print(" - 车辆实时数据管理") - print(" - 文件上传生成知识库") - print(" - 智能对话结合车辆数据") - print(" - 飞书同步功能") - print(" - 系统性能优化") else: print("\n" + "=" * 80) - print("❌ 数据库初始化失败!") + print("数据库初始化失败!") + print("=" * 80) if __name__ == "__main__": diff --git a/logs/dashboard.log b/logs/dashboard.log index 22c21f1..c003862 100644 --- a/logs/dashboard.log +++ b/logs/dashboard.log @@ -13515,3 +13515,1918 @@ TypeError: WebSocketServer.start_server..handle_client_with_cors() missi 2025-10-27 10:29:08,599 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 10:29:08] "POST /api/feishu-sync/sync-from-feishu HTTP/1.1" 200 - 2025-10-27 10:29:09,049 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 10:29:09] "GET /api/feishu-sync/status HTTP/1.1" 200 - 2025-10-27 10:29:23,523 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 10:29:23] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-27 13:25:35,074 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-27 13:25:35,075 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-27 13:25:38,188 - src.core.database - INFO - 数据库初始化成功 +2025-10-27 13:25:41,666 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-27 13:25:41,682 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-27 13:25:41,793 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-27 13:25:41,794 - werkzeug - INFO - Press CTRL+C to quit +2025-10-27 13:25:42,089 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:25:42,294 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-27 13:25:42,306 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-27 13:25:42,306 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-27 13:26:43,325 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:26:43] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-27 13:26:43,814 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:44,021 - src.config.unified_config - WARNING - 无法加载默认LLM配置,使用内置默认值: No module named 'config.llm_config' +2025-10-27 13:26:44,023 - src.config.unified_config - INFO - 配置文件加载成功 +2025-10-27 13:26:44,023 - src.integrations.ai_suggestion_service - INFO - 使用LLM配置: openai - qwen-turbo +2025-10-27 13:26:44,025 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Number -> order_id +2025-10-27 13:26:44,026 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Description -> description +2025-10-27 13:26:44,028 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Type of problem -> category +2025-10-27 13:26:44,030 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Level -> priority +2025-10-27 13:26:44,031 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Status -> status +2025-10-27 13:26:44,034 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:26:44] "GET /api/analytics HTTP/1.1" 200 - +2025-10-27 13:26:44,035 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Source -> source +2025-10-27 13:26:44,037 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date creation -> created_at +2025-10-27 13:26:44,042 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 处理过程 -> solution +2025-10-27 13:26:44,045 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR tracking -> resolution +2025-10-27 13:26:44,049 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Created by -> created_by +2025-10-27 13:26:44,052 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Module(模块) -> module +2025-10-27 13:26:44,057 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Wilfulness(责任人) -> wilfulness +2025-10-27 13:26:44,060 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date of close TR -> date_of_close +2025-10-27 13:26:44,062 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Vehicle Type01 -> vehicle_type +2025-10-27 13:26:44,063 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: VIN|sim -> vin_sim +2025-10-27 13:26:44,064 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: App remote control version -> app_remote_control_version +2025-10-27 13:26:44,065 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: HMI SW -> hmi_sw +2025-10-27 13:26:44,067 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 父记录 -> parent_record +2025-10-27 13:26:44,070 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Has it been updated on the same day -> has_updated_same_day +2025-10-27 13:26:44,072 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Operating time -> operating_time +2025-10-27 13:26:44,076 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: AI建议 -> ai_suggestion +2025-10-27 13:26:44,079 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Issue Start Time -> updated_at +2025-10-27 13:26:44,080 - src.integrations.workorder_sync - INFO - 开始从飞书同步工单数据... +2025-10-27 13:26:44,080 - src.integrations.feishu_client - INFO - 正在获取飞书tenant_access_token,应用ID: cli_a8b50ec0eed1500d +2025-10-27 13:26:44,330 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:44,620 - src.integrations.feishu_client - INFO - 飞书API响应: {'code': 0, 'expire': 2000, 'msg': 'ok', 'tenant_access_token': 't-g104arc0EZPYERRZG3ZZHO52A3VD3CLFNEWGAY7R'} +2025-10-27 13:26:44,621 - src.integrations.feishu_client - INFO - tenant_access_token获取成功: t-g104arc0EZPYERRZG3... +2025-10-27 13:26:44,621 - src.integrations.feishu_client - INFO - 令牌有效期: 2000秒,过期时间: 2025-10-27 14:00:04 +2025-10-27 13:26:44,624 - src.integrations.feishu_client - INFO - 发送飞书API请求: GET https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records +2025-10-27 13:26:44,624 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104arc0EZPYERRZG3... +2025-10-27 13:26:44,934 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:26:44] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-27 13:26:45,057 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:46,937 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:26:46,938 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'has_more': False, 'items': [{'fields': {'AI建议': '1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}, {'fields': {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'Vehicle Type01': 'LNNBDDEZ8SD345645 ', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}, {'fields': {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'LNNBDDEZXSD449358.zip', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'Vehicle Type01': 'LNNBDDEZXSD449358', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}, {'fields': {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'Traffic_consumption_issue.pdf', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'SIM': 'Ксения Митрофанова ', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'Vehicle Type01': 'XEYDD14B3SA012164', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}], 'total': 4}, 'msg': 'success'} +2025-10-27 13:26:46,943 - src.integrations.workorder_sync - INFO - 从飞书获取 4 条记录 +2025-10-27 13:26:46,944 - src.integrations.workorder_sync - INFO - 开始生成AI建议... +2025-10-27 13:26:46,944 - src.integrations.workorder_sync - INFO - 第一条记录结构示例: record_id=rec253kqBfXae4, 有fields字段=True +2025-10-27 13:26:46,944 - src.integrations.workorder_sync - INFO - 第一条记录的fields示例: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source'] +2025-10-27 13:26:46,945 - src.integrations.workorder_sync - INFO - 第一条记录的AI建议字段内容: 1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网 +2025-10-27 13:26:46,945 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议长度: 202 +2025-10-27 13:26:46,946 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议前100字符: 1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析 +1026:检查车机网络 +2025-10-27 13:26:47,114 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:47,283 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761542807 +2025-10-27 13:26:47,754 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 用逗号连接,一句话表达,不要用序号或分行 +2. 现状+步骤,语言精炼 +3. 总长度控制在150字以内 + +根据问题复杂程度选择结尾: +- 简单问题:给出具体操作步骤即可,不需要提日志分析 +- 复杂问题:如远程操作无法解决,结尾才使用"建议邀请用户进站抓取日志分析" + +问题描述:The widget weather doesn't work + +已处理的步骤: +0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志' 返回 3 个结果 +2025-10-27 13:26:50,347 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:26:50,726 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析... +2025-10-27 13:26:50,727 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析 +2025-10-27 13:26:50,727 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析 +2025-10-27 13:26:50,728 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析... +2025-10-27 13:26:50,728 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=202 +2025-10-27 13:26:50,728 - src.integrations.ai_suggestion_service - INFO - 为记录 rec253kqBfXae4 生成AI建议,新建议长度: 292 +2025-10-27 13:26:50,729 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLSh2a2S7 - 现有AI建议长度: 0 +2025-10-27 13:26:50,936 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:51,149 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761542811 +2025-10-27 13:26:51,324 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 用逗号连接,一句话表达,不要用序号或分行 +2. 现状+步骤,语言精炼 +3. 总长度控制在150字以内 + +根据问题复杂程度选择结尾: +- 简单问题:给出具体操作步骤即可,不需要提日志分析 +- 复杂问题:如远程操作无法解决,结尾才使用"建议邀请用户进站抓取日志分析" + +问题描述:Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + + +已处理的步骤: +1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志' 返回 3 个结果 +2025-10-27 13:26:53,923 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:26:54,282 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析... +2025-10-27 13:26:54,283 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析 +2025-10-27 13:26:54,284 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析 +2025-10-27 13:26:54,284 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析... +2025-10-27 13:26:54,285 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=0 +2025-10-27 13:26:54,285 - src.integrations.ai_suggestion_service - INFO - 没有现有建议,返回: 1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和D... +2025-10-27 13:26:54,285 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLSh2a2S7 生成AI建议,新建议长度: 61 +2025-10-27 13:26:54,286 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKLlRJ - 现有AI建议长度: 0 +2025-10-27 13:26:54,762 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:55,236 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761542815 +2025-10-27 13:26:55,441 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 用逗号连接,一句话表达,不要用序号或分行 +2. 现状+步骤,语言精炼 +3. 总长度控制在150字以内 + +根据问题复杂程度选择结尾: +- 简单问题:给出具体操作步骤即可,不需要提日志分析 +- 复杂问题:如远程操作无法解决,结尾才使用"建议邀请用户进站抓取日志分析" + +问题描述:User not able to enter into member center neither mobile phone nor QR code. + +已处理的步骤: +1024: DMC logs attached as well as vieos of the issue.' 返回 3 个结果 +2025-10-27 13:26:57,312 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:26:57,662 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。... +2025-10-27 13:26:57,662 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。 +2025-10-27 13:26:57,663 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。 +2025-10-27 13:26:57,663 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。... +2025-10-27 13:26:57,663 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=0 +2025-10-27 13:26:57,663 - src.integrations.ai_suggestion_service - INFO - 没有现有建议,返回: 1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认... +2025-10-27 13:26:57,664 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKLlRJ 生成AI建议,新建议长度: 86 +2025-10-27 13:26:57,664 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKjKvx - 现有AI建议长度: 0 +2025-10-27 13:26:58,177 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:26:58,645 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761542818 +2025-10-27 13:26:58,853 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 用逗号连接,一句话表达,不要用序号或分行 +2. 现状+步骤,语言精炼 +3. 总长度控制在150字以内 + +根据问题复杂程度选择结尾: +- 简单问题:给出具体操作步骤即可,不需要提日志分析 +- 复杂问题:如远程操作无法解决,结尾才使用"建议邀请用户进站抓取日志分析" + +问题描述:Abnormal traffic consumption + +已处理的步骤: +1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf' 返回 3 个结果 +2025-10-27 13:27:01,251 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:27:01,599 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。... +2025-10-27 13:27:01,599 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。 +2025-10-27 13:27:01,600 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。 +2025-10-27 13:27:01,600 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。... +2025-10-27 13:27:01,600 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=0 +2025-10-27 13:27:01,600 - src.integrations.ai_suggestion_service - INFO - 没有现有建议,返回: 1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设... +2025-10-27 13:27:01,601 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKjKvx 生成AI建议,新建议长度: 97 +2025-10-27 13:27:01,601 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/rec253kqBfXae4 +2025-10-27 13:27:01,601 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104arc0EZPYERRZG3... +2025-10-27 13:27:03,582 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:27:03,583 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析\n1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}}, 'msg': 'success'} +2025-10-27 13:27:03,585 - src.integrations.workorder_sync - INFO - 更新飞书记录 rec253kqBfXae4 的AI建议 +2025-10-27 13:27:03,586 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLSh2a2S7 +2025-10-27 13:27:03,586 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104arc0EZPYERRZG3... +2025-10-27 13:27:05,450 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:27:05,450 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}}, 'msg': 'success'} +2025-10-27 13:27:05,452 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLSh2a2S7 的AI建议 +2025-10-27 13:27:05,452 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKLlRJ +2025-10-27 13:27:05,452 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104arc0EZPYERRZG3... +2025-10-27 13:27:07,471 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:27:07,472 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}}, 'msg': 'success'} +2025-10-27 13:27:07,473 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKLlRJ 的AI建议 +2025-10-27 13:27:07,474 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKjKvx +2025-10-27 13:27:07,474 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104arc0EZPYERRZG3... +2025-10-27 13:27:09,385 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:27:09,386 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}}, 'msg': 'success'} +2025-10-27 13:27:09,388 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKjKvx 的AI建议 +2025-10-27 13:27:09,880 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'} +2025-10-27 13:27:09,881 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:27:09,882 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析 +1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析 +1025: +2025-10-27 13:27:09,882 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Evgeniy +2025-10-27 13:27:09,882 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1745769600000 +2025-10-27 13:27:09,882 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: local O&M +2025-10-27 13:27:09,883 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:27:09,883 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: The widget weather doesn't work +2025-10-27 13:27:09,883 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:27:09,884 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR559 +2025-10-27 13:27:09,884 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:27:09,884 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs. +2025-10-27 13:27:09,884 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: HU troubles +2025-10-27 13:27:09,884 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: LVTDD24B8RG019153 +2025-10-27 13:27:09,885 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: EXEED RX(T22) +2025-10-27 13:27:09,885 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Evgeniy +2025-10-27 13:27:09,885 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志 +2025-10-27 13:27:09,885 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:27:09,885 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:27:10,119 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'Vehicle Type01': 'LNNBDDEZ8SD345645 ', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'} +2025-10-27 13:27:10,120 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['App remote control version', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:27:10,120 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:27:10,121 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:27:10,121 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: TBOX +2025-10-27 13:27:10,122 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: 'Астанин Артем Иванович (CSAT: 4.5 ⭐)' +2025-10-27 13:27:10,122 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:27:10,122 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + +2025-10-27 13:27:10,123 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:27:10,123 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR863 +2025-10-27 13:27:10,123 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:27:10,124 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi +2025-10-27 13:27:10,124 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Remote control +2025-10-27 13:27:10,124 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: LNNBDDEZ8SD345645 +2025-10-27 13:27:10,124 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 刘娇龙 +2025-10-27 13:27:10,125 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志 +2025-10-27 13:27:10,125 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 14, 未映射 0 +2025-10-27 13:27:10,125 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 14, 已映射 14, 未映射 0 +2025-10-27 13:27:10,362 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'LNNBDDEZXSD449358.zip', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'Vehicle Type01': 'LNNBDDEZXSD449358', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'} +2025-10-27 13:27:10,363 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['App remote control version', 'Created by', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:27:10,363 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:27:10,363 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: LNNBDDEZXSD449358.zip +2025-10-27 13:27:10,363 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:27:10,364 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: 生态/ecologically +2025-10-27 13:27:10,364 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: 'Астанин Артем Иванович (CSAT: 4.5 ⭐)' +2025-10-27 13:27:10,364 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:27:10,364 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: User not able to enter into member center neither mobile phone nor QR code. +2025-10-27 13:27:10,364 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:27:10,365 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR864 +2025-10-27 13:27:10,365 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:27:10,365 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 23/10: DMC logs attached as well as vieos of the issue. +2025-10-27 13:27:10,365 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Problem with auth in member center +2025-10-27 13:27:10,365 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: LNNBDDEZXSD449358 +2025-10-27 13:27:10,366 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 袁清 +2025-10-27 13:27:10,366 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024: DMC logs attached as well as vieos of the issue. +2025-10-27 13:27:10,366 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:27:10,366 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:27:11,003 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'Traffic_consumption_issue.pdf', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'SIM': 'Ксения Митрофанова ', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'Vehicle Type01': 'XEYDD14B3SA012164', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'} +2025-10-27 13:27:11,003 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['App remote control version', 'Created by', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:27:11,004 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:27:11,004 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Traffic_consumption_issue.pdf +2025-10-27 13:27:11,005 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761235200000 +2025-10-27 13:27:11,005 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: DMC +2025-10-27 13:27:11,005 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: Ксения Митрофанова +2025-10-27 13:27:11,005 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:27:11,005 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Abnormal traffic consumption +2025-10-27 13:27:11,006 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:27:11,006 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR866 +2025-10-27 13:27:11,006 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Analysising +2025-10-27 13:27:11,006 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi +24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached +2025-10-27 13:27:11,007 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Traffic is over +2025-10-27 13:27:11,007 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: XEYDD14B3SA012164 +2025-10-27 13:27:11,007 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Vsevolod Tsoi +2025-10-27 13:27:11,007 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf +2025-10-27 13:27:11,008 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:27:11,008 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:27:11,749 - src.core.database - ERROR - 数据库操作失败: dict can not be used as parameter +2025-10-27 13:27:11,750 - src.integrations.workorder_sync - ERROR - 飞书同步失败: dict can not be used as parameter +2025-10-27 13:27:11,751 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:27:11] "POST /api/feishu-sync/sync-from-feishu HTTP/1.1" 500 - +2025-10-27 13:30:55,735 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 88.8% +2025-10-27 13:30:55,796 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 88.8% +2025-10-27 13:31:57,179 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 88.8% +2025-10-27 13:31:57,748 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 88.8% +2025-10-27 13:32:58,618 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.2% +2025-10-27 13:32:59,409 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.2% +2025-10-27 13:34:00,567 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.6% +2025-10-27 13:34:00,849 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.6% +2025-10-27 13:34:57,794 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:34:57] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-27 13:34:58,255 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:34:58] "GET /api/analytics HTTP/1.1" 200 - +2025-10-27 13:34:59,468 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:34:59] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-27 13:35:02,035 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.9% +2025-10-27 13:35:02,367 - src.core.system_optimizer - WARNING - 性能预警: 内存使用率过高: 89.9% +2025-10-27 13:35:27,055 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-27 13:35:27,055 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-27 13:35:33,014 - src.core.database - INFO - 数据库初始化成功 +2025-10-27 13:35:36,053 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-27 13:35:36,090 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-27 13:35:36,195 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-27 13:35:36,196 - werkzeug - INFO - Press CTRL+C to quit +2025-10-27 13:35:36,707 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:35:37,177 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-27 13:35:37,188 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-27 13:35:37,188 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-27 13:35:43,025 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:35:43,503 - src.config.unified_config - WARNING - 无法加载默认LLM配置,使用内置默认值: No module named 'config.llm_config' +2025-10-27 13:35:43,504 - src.config.unified_config - INFO - 配置文件加载成功 +2025-10-27 13:35:43,505 - src.integrations.ai_suggestion_service - INFO - 使用LLM配置: openai - qwen-turbo +2025-10-27 13:35:43,507 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Number -> order_id +2025-10-27 13:35:43,509 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Description -> description +2025-10-27 13:35:43,511 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Type of problem -> category +2025-10-27 13:35:43,512 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Level -> priority +2025-10-27 13:35:43,514 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Status -> status +2025-10-27 13:35:43,515 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Source -> source +2025-10-27 13:35:43,517 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date creation -> created_at +2025-10-27 13:35:43,518 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 处理过程 -> solution +2025-10-27 13:35:43,520 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR tracking -> resolution +2025-10-27 13:35:43,522 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Created by -> created_by +2025-10-27 13:35:43,524 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Module(模块) -> module +2025-10-27 13:35:43,525 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Wilfulness(责任人) -> wilfulness +2025-10-27 13:35:43,527 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date of close TR -> date_of_close +2025-10-27 13:35:43,528 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Vehicle Type01 -> vehicle_type +2025-10-27 13:35:43,530 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: VIN|sim -> vin_sim +2025-10-27 13:35:43,531 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: App remote control version -> app_remote_control_version +2025-10-27 13:35:43,533 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: HMI SW -> hmi_sw +2025-10-27 13:35:43,535 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 父记录 -> parent_record +2025-10-27 13:35:43,537 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Has it been updated on the same day -> has_updated_same_day +2025-10-27 13:35:43,538 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Operating time -> operating_time +2025-10-27 13:35:43,540 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: AI建议 -> ai_suggestion +2025-10-27 13:35:43,541 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Issue Start Time -> updated_at +2025-10-27 13:35:43,542 - src.integrations.workorder_sync - INFO - 开始从飞书同步工单数据... +2025-10-27 13:35:43,542 - src.integrations.feishu_client - INFO - 正在获取飞书tenant_access_token,应用ID: cli_a8b50ec0eed1500d +2025-10-27 13:35:44,223 - src.integrations.feishu_client - INFO - 飞书API响应: {'code': 0, 'expire': 7200, 'msg': 'ok', 'tenant_access_token': 't-g104ardzY5FUAT4PXXIHVH2MEYWZ56MIGBB6BLWC'} +2025-10-27 13:35:44,224 - src.integrations.feishu_client - INFO - tenant_access_token获取成功: t-g104ardzY5FUAT4PXX... +2025-10-27 13:35:44,225 - src.integrations.feishu_client - INFO - 令牌有效期: 7200秒,过期时间: 2025-10-27 15:35:44 +2025-10-27 13:35:44,228 - src.integrations.feishu_client - INFO - 发送飞书API请求: GET https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records +2025-10-27 13:35:44,229 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:35:46,244 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:35:46] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-27 13:35:47,173 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:35:47,174 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'has_more': False, 'items': [{'fields': {'AI建议': '1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析\n1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}, {'fields': {'AI建议': '1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'Vehicle Type01': 'LNNBDDEZ8SD345645 ', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}, {'fields': {'AI建议': '1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'LNNBDDEZXSD449358.zip', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'Vehicle Type01': 'LNNBDDEZXSD449358', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}, {'fields': {'AI建议': '1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'Traffic_consumption_issue.pdf', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'SIM': 'Ксения Митрофанова ', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'Vehicle Type01': 'XEYDD14B3SA012164', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}], 'total': 4}, 'msg': 'success'} +2025-10-27 13:35:47,180 - src.integrations.workorder_sync - INFO - 从飞书获取 4 条记录 +2025-10-27 13:35:47,180 - src.integrations.workorder_sync - INFO - 开始生成AI建议... +2025-10-27 13:35:47,180 - src.integrations.workorder_sync - INFO - 第一条记录结构示例: record_id=rec253kqBfXae4, 有fields字段=True +2025-10-27 13:35:47,181 - src.integrations.workorder_sync - INFO - 第一条记录的fields示例: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source'] +2025-10-27 13:35:47,181 - src.integrations.workorder_sync - INFO - 第一条记录的AI建议字段内容: 1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测 +2025-10-27 13:35:47,182 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议长度: 292 +2025-10-27 13:35:47,182 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议前100字符: 1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析 +1027:天气小部件 +2025-10-27 13:35:47,657 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:35:48,128 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543348 +2025-10-27 13:35:48,598 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:The widget weather doesn't work + +已处理的步骤: +0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志' 返回 3 个结果 +2025-10-27 13:35:51,563 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:35:52,554 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议连接手机热点测试网络连通性,若仍无效则抓取IHU日志分析,如远程排查无果,需进站进行深入诊断。... +2025-10-27 13:35:52,555 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议连接手机热点测试网络连通性,若仍无效则抓取IHU日志分析,如远程排查无果,需进站进行深入诊断。 +2025-10-27 13:35:52,555 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议连接手机热点测试网络连通性,若仍无效则抓取IHU日志分析,如远程排查无果,需进站进行深入诊断。 +2025-10-27 13:35:52,555 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议连接手机热点测试网络连通性,若仍无效则抓取IHU日志分析,如远程排查无果,需进站进行深入诊断。... +2025-10-27 13:35:52,556 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=292 +2025-10-27 13:35:52,556 - src.integrations.ai_suggestion_service - INFO - 为记录 rec253kqBfXae4 生成AI建议,新建议长度: 382 +2025-10-27 13:35:52,556 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLSh2a2S7 - 现有AI建议长度: 61 +2025-10-27 13:35:52,556 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLSh2a2S7 - 现有AI建议前100字符: 1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析 +2025-10-27 13:35:53,047 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:35:53,519 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543353 +2025-10-27 13:35:53,988 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + + +已处理的步骤: +1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志' 返回 3 个结果 +2025-10-27 13:35:56,982 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:35:57,955 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 车辆自10月19日进入深度睡眠,TBOX无日志上传,远程无法唤醒,建议客户尽快前往服务站提取TBOX及DMC日志进行深入诊断,以定位通信异常原因。... +2025-10-27 13:35:57,955 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日进入深度睡眠,TBOX无日志上传,远程无法唤醒,建议客户尽快前往服务站提取TBOX及DMC日志进行深入诊断,以定位通信异常原因。 +2025-10-27 13:35:57,956 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日进入深度睡眠,TBOX无日志上传,远程无法唤醒,建议客户尽快前往服务站提取TBOX及DMC日志进行深入诊断,以定位通信异常原因。 +2025-10-27 13:35:57,956 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 车辆自10月19日进入深度睡眠,TBOX无日志上传,远程无法唤醒,建议客户尽快前往服务站提取TBOX及DMC日志进行深入诊断,以定位通信异常原因。... +2025-10-27 13:35:57,957 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=61 +2025-10-27 13:35:57,957 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLSh2a2S7 生成AI建议,新建议长度: 142 +2025-10-27 13:35:57,958 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKLlRJ - 现有AI建议长度: 86 +2025-10-27 13:35:57,958 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKLlRJ - 现有AI建议前100字符: 1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。 +2025-10-27 13:35:58,678 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:35:59,152 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543359 +2025-10-27 13:35:59,861 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:User not able to enter into member center neither mobile phone nor QR code. + +已处理的步骤: +1024: DMC logs attached as well as vieos of the issue.' 返回 3 个结果 +2025-10-27 13:36:03,863 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:36:04,816 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 确认手机网络及APP版本正常,尝试重启手机和车辆,清除APP缓存后重新登录,检查车机系统时间是否准确,确保二维码扫描功能开启;若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查账户同步问题。... +2025-10-27 13:36:04,817 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 确认手机网络及APP版本正常,尝试重启手机和车辆,清除APP缓存后重新登录,检查车机系统时间是否准确,确保二维码扫描功能开启;若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查账户同步问题。 +2025-10-27 13:36:04,818 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 确认手机网络及APP版本正常,尝试重启手机和车辆,清除APP缓存后重新登录,检查车机系统时间是否准确,确保二维码扫描功能开启;若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查账户同步问题。 +2025-10-27 13:36:04,820 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 确认手机网络及APP版本正常,尝试重启手机和车辆,清除APP缓存后重新登录,检查车机系统时间是否准确,确保二维码扫描功能开启;若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查账户同步问题。... +2025-10-27 13:36:04,820 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=86 +2025-10-27 13:36:04,821 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKLlRJ 生成AI建议,新建议长度: 192 +2025-10-27 13:36:04,825 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKjKvx - 现有AI建议长度: 97 +2025-10-27 13:36:04,826 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKjKvx - 现有AI建议前100字符: 1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。 +2025-10-27 13:36:06,000 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:36:06,477 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543366 +2025-10-27 13:36:08,118 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:Abnormal traffic consumption + +已处理的步骤: +1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf' 返回 3 个结果 +2025-10-27 13:36:11,565 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:36:12,550 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 流量异常可能与导航应用后台持续联网有关,建议关闭非必要应用自动更新,限制后台数据使用,检查是否有系统或应用异常进程;若问题依旧,请回传完整日志供技术分析,必要时进站检测通信模块及SIM卡状态。... +2025-10-27 13:36:12,551 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 流量异常可能与导航应用后台持续联网有关,建议关闭非必要应用自动更新,限制后台数据使用,检查是否有系统或应用异常进程;若问题依旧,请回传完整日志供技术分析,必要时进站检测通信模块及SIM卡状态。 +2025-10-27 13:36:12,551 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 流量异常可能与导航应用后台持续联网有关,建议关闭非必要应用自动更新,限制后台数据使用,检查是否有系统或应用异常进程;若问题依旧,请回传完整日志供技术分析,必要时进站检测通信模块及SIM卡状态。 +2025-10-27 13:36:12,551 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 流量异常可能与导航应用后台持续联网有关,建议关闭非必要应用自动更新,限制后台数据使用,检查是否有系统或应用异常进程;若问题依旧,请回传完整日志供技术分析,必要时进站检测通信模块及SIM卡状态。... +2025-10-27 13:36:12,552 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=97 +2025-10-27 13:36:12,552 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKjKvx 生成AI建议,新建议长度: 201 +2025-10-27 13:36:12,552 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/rec253kqBfXae4 +2025-10-27 13:36:12,552 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:36:14,852 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:36:14,852 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027-2:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议连接手机热点测试网络连通性,若仍无效则抓取IHU日志分析,如远程排查无果,需进站进行深入诊断。\n1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析\n1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}}, 'msg': 'success'} +2025-10-27 13:36:14,854 - src.integrations.workorder_sync - INFO - 更新飞书记录 rec253kqBfXae4 的AI建议 +2025-10-27 13:36:14,855 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLSh2a2S7 +2025-10-27 13:36:14,855 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:36:17,199 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:36:17,200 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027-1:车辆自10月19日进入深度睡眠,TBOX无日志上传,远程无法唤醒,建议客户尽快前往服务站提取TBOX及DMC日志进行深入诊断,以定位通信异常原因。\n1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}}, 'msg': 'success'} +2025-10-27 13:36:17,201 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLSh2a2S7 的AI建议 +2025-10-27 13:36:17,202 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKLlRJ +2025-10-27 13:36:17,202 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:36:19,415 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:36:19,415 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027-1:确认手机网络及APP版本正常,尝试重启手机和车辆,清除APP缓存后重新登录,检查车机系统时间是否准确,确保二维码扫描功能开启;若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查账户同步问题。\n1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}}, 'msg': 'success'} +2025-10-27 13:36:19,416 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKLlRJ 的AI建议 +2025-10-27 13:36:19,417 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKjKvx +2025-10-27 13:36:19,417 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:36:21,462 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:36:21,462 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027-1:流量异常可能与导航应用后台持续联网有关,建议关闭非必要应用自动更新,限制后台数据使用,检查是否有系统或应用异常进程;若问题依旧,请回传完整日志供技术分析,必要时进站检测通信模块及SIM卡状态。\n1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}}, 'msg': 'success'} +2025-10-27 13:36:21,464 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKjKvx 的AI建议 +2025-10-27 13:36:21,941 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析\n1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析\n1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析\n1025:', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'} +2025-10-27 13:36:21,942 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:36:21,942 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1027-1:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络,若仍不恢复则重启车机系统,如问题持续建议邀请用户进站抓取IHU日志分析 +1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议先连接手机热点测试网络响应,若仍不恢复,尝试重启车机系统,如问题持续建议邀请用户进站抓取日志分析 +1026:检查车机网络连接状态,确认车辆是否处于无信号区域,建议使用手机热点连接车机观察天气小部件是否恢复,如仍无效,尝试重启车机系统或恢复网络设置,必要时升级车机系统版本,若问题持续,建议邀请用户进站抓取日志分析 +1025: +2025-10-27 13:36:21,943 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Evgeniy +2025-10-27 13:36:21,944 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1745769600000 +2025-10-27 13:36:21,944 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: local O&M +2025-10-27 13:36:21,944 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:36:21,944 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: The widget weather doesn't work +2025-10-27 13:36:21,945 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:36:21,945 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR559 +2025-10-27 13:36:21,945 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:36:21,945 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs. +2025-10-27 13:36:21,946 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: HU troubles +2025-10-27 13:36:21,946 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: LVTDD24B8RG019153 +2025-10-27 13:36:21,946 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: EXEED RX(T22) +2025-10-27 13:36:21,946 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Evgeniy +2025-10-27 13:36:21,946 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志 +2025-10-27 13:36:21,947 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:36:21,947 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:36:22,183 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'Vehicle Type01': 'LNNBDDEZ8SD345645 ', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'} +2025-10-27 13:36:22,184 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'App remote control version', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:36:22,185 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1027:车辆自10月19日起TBOX进入深度睡眠,远程无法唤醒且数据未更新,建议邀请用户进站抓取TBOX和DMC日志分析 +2025-10-27 13:36:22,185 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:36:22,186 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:36:22,186 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: TBOX +2025-10-27 13:36:22,186 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: 'Астанин Артем Иванович (CSAT: 4.5 ⭐)' +2025-10-27 13:36:22,187 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:36:22,187 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + +2025-10-27 13:36:22,187 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:36:22,188 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR863 +2025-10-27 13:36:22,188 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:36:22,188 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi +2025-10-27 13:36:22,189 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Remote control +2025-10-27 13:36:22,189 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: LNNBDDEZ8SD345645 +2025-10-27 13:36:22,190 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 刘娇龙 +2025-10-27 13:36:22,190 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志 +2025-10-27 13:36:22,191 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:36:22,191 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:36:22,431 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'LNNBDDEZXSD449358.zip', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'SIM': "'Астанин Артем Иванович (CSAT: 4.5 ⭐)' ", 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'Vehicle Type01': 'LNNBDDEZXSD449358', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'} +2025-10-27 13:36:22,432 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'App remote control version', 'Created by', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:36:22,433 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1027:当前无法登录会员中心,建议先检查手机网络连接是否正常,尝试重启手机或更换网络环境后重新扫码登录,确认APP为最新版本,若仍无法进入,建议邀请用户进站抓取日志分析。 +2025-10-27 13:36:22,433 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:36:22,434 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: LNNBDDEZXSD449358.zip +2025-10-27 13:36:22,434 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:36:22,435 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: 生态/ecologically +2025-10-27 13:36:22,435 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: 'Астанин Артем Иванович (CSAT: 4.5 ⭐)' +2025-10-27 13:36:22,435 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:36:22,436 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: User not able to enter into member center neither mobile phone nor QR code. +2025-10-27 13:36:22,437 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:36:22,438 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR864 +2025-10-27 13:36:22,438 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:36:22,439 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 23/10: DMC logs attached as well as vieos of the issue. +2025-10-27 13:36:22,439 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Problem with auth in member center +2025-10-27 13:36:22,440 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: LNNBDDEZXSD449358 +2025-10-27 13:36:22,440 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 袁清 +2025-10-27 13:36:22,440 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024: DMC logs attached as well as vieos of the issue. +2025-10-27 13:36:22,441 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 16, 未映射 0 +2025-10-27 13:36:22,441 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 16, 已映射 16, 未映射 0 +2025-10-27 13:36:22,680 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。', 'App remote control version': [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}], 'Created by': 'Traffic_consumption_issue.pdf', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'SIM': 'Ксения Митрофанова ', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'Vehicle Type01': 'XEYDD14B3SA012164', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'} +2025-10-27 13:36:22,681 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'App remote control version', 'Created by', 'Date creation', 'Module(模块)', 'SIM', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:36:22,682 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1027:当前流量异常消耗达3.12GB/3天,用户仅使用导航,建议先重启车机并关闭非必要后台应用,检查网络设置中数据使用情况,更新车机系统至最新版本,若问题依旧,建议邀请用户进站抓取日志分析。 +2025-10-27 13:36:22,682 - src.integrations.flexible_field_mapper - INFO - 映射字段 App remote control version -> app_remote_control_version: [{'avatar_url': 'https://s16-imfile-sg.feishucdn.com/static-resource/v1/v3_00ol_fb9a943e-728d-4c86-9136-1eb9be3ff5hu~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp', 'en_name': 'Vsevolod Tsoi', 'id': 'ou_f59d164483b0d52c47c5ec5ecf6d842c', 'name': 'Vsevolod Tsoi'}] +2025-10-27 13:36:22,682 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Traffic_consumption_issue.pdf +2025-10-27 13:36:22,683 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761235200000 +2025-10-27 13:36:22,683 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: DMC +2025-10-27 13:36:22,683 - src.integrations.flexible_field_mapper - INFO - 映射字段 SIM -> vin_sim: Ксения Митрофанова +2025-10-27 13:36:22,684 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:36:22,684 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Abnormal traffic consumption +2025-10-27 13:36:22,684 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:36:22,684 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR866 +2025-10-27 13:36:22,685 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Analysising +2025-10-27 13:36:22,685 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi +24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached +2025-10-27 13:36:22,686 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Traffic is over +2025-10-27 13:36:22,686 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: XEYDD14B3SA012164 +2025-10-27 13:36:22,686 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Vsevolod Tsoi +2025-10-27 13:36:22,687 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf +2025-10-27 13:36:22,687 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 16, 未映射 0 +2025-10-27 13:36:22,688 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 16, 已映射 16, 未映射 0 +2025-10-27 13:36:23,404 - src.core.database - ERROR - 数据库操作失败: dict can not be used as parameter +2025-10-27 13:36:23,405 - src.integrations.workorder_sync - ERROR - 飞书同步失败: dict can not be used as parameter +2025-10-27 13:36:23,405 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:36:23] "POST /api/feishu-sync/sync-from-feishu HTTP/1.1" 500 - +2025-10-27 13:39:38,030 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:38,270 - src.integrations.workorder_sync - INFO - 开始从飞书同步工单数据... +2025-10-27 13:39:38,270 - src.integrations.feishu_client - INFO - 发送飞书API请求: GET https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records +2025-10-27 13:39:38,271 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:39:38,404 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:39,488 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:39:39] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-27 13:39:39,551 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:39:39] "GET /api/analytics HTTP/1.1" 200 - +2025-10-27 13:39:40,715 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:39:40,715 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'has_more': False, 'items': [{'fields': {'AI建议': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}, {'fields': {'AI建议': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'VIN|sim': 'LNNBDDEZ8SD345645 ', 'Vehicle Type01': 'JAECOO J7(T1EJ)', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}, {'fields': {'AI建议': '1024: DMC logs attached as well as vieos of the issue.', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'VIN|sim': 'LNNBDDEZXSD449358', 'Vehicle Type01': 'EXEED VX FL(M36T)', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}, {'fields': {'AI建议': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'VIN|sim': 'XEYDD14B3SA012164', 'Vehicle Type01': 'TEST', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}], 'total': 4}, 'msg': 'success'} +2025-10-27 13:39:40,719 - src.integrations.workorder_sync - INFO - 从飞书获取 4 条记录 +2025-10-27 13:39:40,719 - src.integrations.workorder_sync - INFO - 开始生成AI建议... +2025-10-27 13:39:40,719 - src.integrations.workorder_sync - INFO - 第一条记录结构示例: record_id=rec253kqBfXae4, 有fields字段=True +2025-10-27 13:39:40,719 - src.integrations.workorder_sync - INFO - 第一条记录的fields示例: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source'] +2025-10-27 13:39:40,720 - src.integrations.workorder_sync - INFO - 第一条记录的AI建议字段内容: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手 +2025-10-27 13:39:40,720 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议长度: 82 +2025-10-27 13:39:40,720 - src.integrations.ai_suggestion_service - INFO - 记录 rec253kqBfXae4 - 现有AI建议前100字符: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志 +2025-10-27 13:39:40,908 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:41,093 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543581 +2025-10-27 13:39:41,281 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:The widget weather doesn't work + +已处理的步骤: +0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志' 返回 3 个结果 +2025-10-27 13:39:43,697 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:39:44,081 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议已完成手机热点连接测试并尝试抓取IHU日志,若问题仍存在,建议进站进行系统诊断或软件版本升级排查。... +2025-10-27 13:39:44,082 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议已完成手机热点连接测试并尝试抓取IHU日志,若问题仍存在,建议进站进行系统诊断或软件版本升级排查。 +2025-10-27 13:39:44,082 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议已完成手机热点连接测试并尝试抓取IHU日志,若问题仍存在,建议进站进行系统诊断或软件版本升级排查。 +2025-10-27 13:39:44,083 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议已完成手机热点连接测试并尝试抓取IHU日志,若问题仍存在,建议进站进行系统诊断或软件版本升级排查。... +2025-10-27 13:39:44,083 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=82 +2025-10-27 13:39:44,083 - src.integrations.ai_suggestion_service - INFO - 为记录 rec253kqBfXae4 生成AI建议,新建议长度: 172 +2025-10-27 13:39:44,084 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLSh2a2S7 - 现有AI建议长度: 60 +2025-10-27 13:39:44,084 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLSh2a2S7 - 现有AI建议前100字符: 1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志 +2025-10-27 13:39:44,227 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:39:44] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-27 13:39:44,272 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:44,504 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543584 +2025-10-27 13:39:44,989 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + + +已处理的步骤: +1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志' 返回 3 个结果 +2025-10-27 13:39:48,039 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:39:48,742 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 车辆自10月19日进入深度睡眠,TBOX无远程连接,建议客户尽快进站提取TBOX及DMC日志,进行本地诊断分析,排查电源管理或通信模块异常原因,确认硬件状态及软件配置是否正常。... +2025-10-27 13:39:48,743 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日进入深度睡眠,TBOX无远程连接,建议客户尽快进站提取TBOX及DMC日志,进行本地诊断分析,排查电源管理或通信模块异常原因,确认硬件状态及软件配置是否正常。 +2025-10-27 13:39:48,743 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 车辆自10月19日进入深度睡眠,TBOX无远程连接,建议客户尽快进站提取TBOX及DMC日志,进行本地诊断分析,排查电源管理或通信模块异常原因,确认硬件状态及软件配置是否正常。 +2025-10-27 13:39:48,743 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 车辆自10月19日进入深度睡眠,TBOX无远程连接,建议客户尽快进站提取TBOX及DMC日志,进行本地诊断分析,排查电源管理或通信模块异常原因,确认硬件状态及软件配置是否正常。... +2025-10-27 13:39:48,743 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=60 +2025-10-27 13:39:48,744 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLSh2a2S7 生成AI建议,新建议长度: 154 +2025-10-27 13:39:48,744 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKLlRJ - 现有AI建议长度: 54 +2025-10-27 13:39:48,744 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKLlRJ - 现有AI建议前100字符: 1024: DMC logs attached as well as vieos of the issue. +2025-10-27 13:39:49,271 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:49,801 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543589 +2025-10-27 13:39:50,196 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:User not able to enter into member center neither mobile phone nor QR code. + +已处理的步骤: +1024: DMC logs attached as well as vieos of the issue.' 返回 3 个结果 +2025-10-27 13:39:54,163 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:39:54,559 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 确认手机网络及APP版本正常,尝试清除APP缓存并重新登录,检查QR码扫描权限是否开启,重启手机和车机,若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查系统故障。... +2025-10-27 13:39:54,559 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 确认手机网络及APP版本正常,尝试清除APP缓存并重新登录,检查QR码扫描权限是否开启,重启手机和车机,若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查系统故障。 +2025-10-27 13:39:54,560 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 确认手机网络及APP版本正常,尝试清除APP缓存并重新登录,检查QR码扫描权限是否开启,重启手机和车机,若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查系统故障。 +2025-10-27 13:39:54,560 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 确认手机网络及APP版本正常,尝试清除APP缓存并重新登录,检查QR码扫描权限是否开启,重启手机和车机,若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查系统故障。... +2025-10-27 13:39:54,561 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=54 +2025-10-27 13:39:54,561 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKLlRJ 生成AI建议,新建议长度: 145 +2025-10-27 13:39:54,561 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKjKvx - 现有AI建议长度: 98 +2025-10-27 13:39:54,562 - src.integrations.ai_suggestion_service - INFO - 记录 recv0NLZoKjKvx - 现有AI建议前100字符: 1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf +2025-10-27 13:39:55,036 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-27 13:39:55,507 - src.dialogue.realtime_chat - INFO - 创建新会话: session_ai_suggestion_service_1761543595 +2025-10-27 13:39:55,770 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 '请为以下问题提供精炼的技术支持操作建议: + +格式要求: +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 + +要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行 + +问题描述:Abnormal traffic consumption + +已处理的步骤: +1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf' 返回 3 个结果 +2025-10-27 13:39:59,147 - src.core.llm_client - INFO - API请求成功 +2025-10-27 13:39:59,530 - src.integrations.ai_suggestion_service - INFO - AI生成原始内容: 流量异常可能与导航应用后台持续联网有关,建议关闭Navi自动更新地图功能并限制后台数据使用,检查车机系统是否存在异常进程,如问题依旧,建议进站检测DMC模块日志及SIM卡通信状态,必要时升级系统或更换... +2025-10-27 13:39:59,531 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 流量异常可能与导航应用后台持续联网有关,建议关闭Navi自动更新地图功能并限制后台数据使用,检查车机系统是否存在异常进程,如问题依旧,建议进站检测DMC模块日志及SIM卡通信状态,必要时升级系统或更换 +2025-10-27 13:39:59,531 - src.integrations.ai_suggestion_service - INFO - 未找到需要替换的内容: 流量异常可能与导航应用后台持续联网有关,建议关闭Navi自动更新地图功能并限制后台数据使用,检查车机系统是否存在异常进程,如问题依旧,建议进站检测DMC模块日志及SIM卡通信状态,必要时升级系统或更换 +2025-10-27 13:39:59,531 - src.integrations.ai_suggestion_service - INFO - AI建议清理后: 流量异常可能与导航应用后台持续联网有关,建议关闭Navi自动更新地图功能并限制后台数据使用,检查车机系统是否存在异常进程,如问题依旧,建议进站检测DMC模块日志及SIM卡通信状态,必要时升级系统或更换... +2025-10-27 13:39:59,532 - src.integrations.ai_suggestion_service - INFO - _format_ai_suggestion_with_numbering 调用 - time_str=1027, existing长度=98 +2025-10-27 13:39:59,532 - src.integrations.ai_suggestion_service - INFO - 为记录 recv0NLZoKjKvx 生成AI建议,新建议长度: 207 +2025-10-27 13:39:59,532 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/rec253kqBfXae4 +2025-10-27 13:39:59,532 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:40:01,778 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:40:01,779 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:天气小部件无法使用,已确认TBOX及IHU登录正常、流量绑定正常,建议已完成手机热点连接测试并尝试抓取IHU日志,若问题仍存在,建议进站进行系统诊断或软件版本升级排查。\n0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'}, 'id': 'rec253kqBfXae4', 'record_id': 'rec253kqBfXae4'}}, 'msg': 'success'} +2025-10-27 13:40:01,780 - src.integrations.workorder_sync - INFO - 更新飞书记录 rec253kqBfXae4 的AI建议 +2025-10-27 13:40:01,780 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLSh2a2S7 +2025-10-27 13:40:01,781 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:40:03,936 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:40:03,937 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:车辆自10月19日进入深度睡眠,TBOX无远程连接,建议客户尽快进站提取TBOX及DMC日志,进行本地诊断分析,排查电源管理或通信模块异常原因,确认硬件状态及软件配置是否正常。\n1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'}, 'id': 'recv0NLSh2a2S7', 'record_id': 'recv0NLSh2a2S7'}}, 'msg': 'success'} +2025-10-27 13:40:03,938 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLSh2a2S7 的AI建议 +2025-10-27 13:40:03,939 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKLlRJ +2025-10-27 13:40:03,939 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:40:06,015 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:40:06,016 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:确认手机网络及APP版本正常,尝试清除APP缓存并重新登录,检查QR码扫描权限是否开启,重启手机和车机,若仍无法进入会员中心,建议联系售后进行远程诊断或进站排查系统故障。\n1024: DMC logs attached as well as vieos of the issue.'}, 'id': 'recv0NLZoKLlRJ', 'record_id': 'recv0NLZoKLlRJ'}}, 'msg': 'success'} +2025-10-27 13:40:06,017 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKLlRJ 的AI建议 +2025-10-27 13:40:06,018 - src.integrations.feishu_client - INFO - 发送飞书API请求: PUT https://open.feishu.cn/open-apis/bitable/v1/apps/XXnEbiCmEaMblSs6FDJcFCqsnIg/tables/tblnl3vJPpgMTSiP/records/recv0NLZoKjKvx +2025-10-27 13:40:06,018 - src.integrations.feishu_client - INFO - 请求头: Authorization: Bearer t-g104ardzY5FUAT4PXX... +2025-10-27 13:40:08,121 - src.integrations.feishu_client - INFO - 飞书API响应状态码: 200 +2025-10-27 13:40:08,121 - src.integrations.feishu_client - INFO - 飞书API响应内容: {'code': 0, 'data': {'record': {'fields': {'AI建议': '1027:流量异常可能与导航应用后台持续联网有关,建议关闭Navi自动更新地图功能并限制后台数据使用,检查车机系统是否存在异常进程,如问题依旧,建议进站检测DMC模块日志及SIM卡通信状态,必要时升级系统或更换模块。\n1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'}, 'id': 'recv0NLZoKjKvx', 'record_id': 'recv0NLZoKjKvx'}}, 'msg': 'success'} +2025-10-27 13:40:08,123 - src.integrations.workorder_sync - INFO - 更新飞书记录 recv0NLZoKjKvx 的AI建议 +2025-10-27 13:40:08,595 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志', 'Created by': 'Evgeniy', 'Date creation': 1745769600000, 'Module(模块)': 'local O&M', 'Source': 'Mail', 'TR Description': "The widget weather doesn't work ", 'TR Level': 'Low', 'TR Number': 'TR559', 'TR Status': 'Processing', 'TR tracking': '28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs.', 'Type of problem': 'HU troubles', 'VIN|sim': 'LVTDD24B8RG019153 ', 'Vehicle Type01': 'EXEED RX(T22)', 'Wilfulness(责任人)': 'Evgeniy', '处理过程': '0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志'} +2025-10-27 13:40:08,596 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:40:08,596 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志 +2025-10-27 13:40:08,597 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Evgeniy +2025-10-27 13:40:08,597 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1745769600000 +2025-10-27 13:40:08,597 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: local O&M +2025-10-27 13:40:08,597 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:40:08,598 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: The widget weather doesn't work +2025-10-27 13:40:08,598 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:40:08,598 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR559 +2025-10-27 13:40:08,598 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:40:08,599 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 28/04:Local feedback weather widget does not work, query TBOX and IHU login record is normal, traffic binding is normal, we suggest users to use mobile phone hotspot to connect to the car, observe whether the widget is restored, if not, try to capture the IHU logs. +2025-10-27 13:40:08,599 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: HU troubles +2025-10-27 13:40:08,599 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: LVTDD24B8RG019153 +2025-10-27 13:40:08,599 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: EXEED RX(T22) +2025-10-27 13:40:08,600 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Evgeniy +2025-10-27 13:40:08,600 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 0428:属地反馈天气小部件不起作用,查询TBOX及IHU登录记录正常,流量绑定正常,建议用户使用手机热点连接车机,观察小部件是否恢复,如不恢复,尝试抓取IHU日志 +2025-10-27 13:40:08,600 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:40:08,600 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:40:09,529 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761148800000, 'Module(模块)': 'TBOX', 'Source': 'Mail', 'TR Description': 'Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19.\n', 'TR Level': 'Low', 'TR Number': 'TR863', 'TR Status': 'Processing', 'TR tracking': '24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi', 'Type of problem': 'Remote control ', 'VIN|sim': 'LNNBDDEZ8SD345645 ', 'Vehicle Type01': 'JAECOO J7(T1EJ)', 'Wilfulness(责任人)': '刘娇龙', '处理过程': '1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志'} +2025-10-27 13:40:09,530 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:40:09,530 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志 +2025-10-27 13:40:09,531 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Vsevolod Tsoi +2025-10-27 13:40:09,531 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:40:09,531 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: TBOX +2025-10-27 13:40:09,531 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:40:09,531 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Vehicle data is not updated in the app => dated from October, 19 - thus, they are old one. No TBOX log in since October, 19. TBOX is in deep sleep since October, 19. + +2025-10-27 13:40:09,532 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:40:09,532 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR863 +2025-10-27 13:40:09,532 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:40:09,533 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10:The vehicle is in deep sleep, and T-box logs cannot be remotely obtained. The problem is a bit complex and difficult to locate, so the customer needs to visit the station to retrieve T-box logs and DMC logs. @Vsevolod Tsoi +2025-10-27 13:40:09,533 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Remote control +2025-10-27 13:40:09,533 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: LNNBDDEZ8SD345645 +2025-10-27 13:40:09,534 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: JAECOO J7(T1EJ) +2025-10-27 13:40:09,534 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 刘娇龙 +2025-10-27 13:40:09,534 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:车辆处于深度睡眠中,远程无法获取T-box日志,问题有点复杂不好定位,需要客户进站取T-box日志和DMC日志 +2025-10-27 13:40:09,534 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:40:09,535 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:40:10,488 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1024: DMC logs attached as well as vieos of the issue.', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761148800000, 'Module(模块)': '生态/ecologically', 'Source': 'Mail', 'TR Description': 'User not able to enter into member center neither mobile phone nor QR code.', 'TR Level': 'Low', 'TR Number': 'TR864', 'TR Status': 'Processing', 'TR tracking': '23/10: DMC logs attached as well as vieos of the issue.', 'Type of problem': 'Problem with auth in member center', 'VIN|sim': 'LNNBDDEZXSD449358', 'Vehicle Type01': 'EXEED VX FL(M36T)', 'Wilfulness(责任人)': '袁清', '处理过程': '1024: DMC logs attached as well as vieos of the issue.'} +2025-10-27 13:40:10,489 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:40:10,489 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1024: DMC logs attached as well as vieos of the issue. +2025-10-27 13:40:10,490 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Vsevolod Tsoi +2025-10-27 13:40:10,490 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761148800000 +2025-10-27 13:40:10,490 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: 生态/ecologically +2025-10-27 13:40:10,491 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:40:10,491 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: User not able to enter into member center neither mobile phone nor QR code. +2025-10-27 13:40:10,491 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:40:10,492 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR864 +2025-10-27 13:40:10,492 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Processing +2025-10-27 13:40:10,492 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 23/10: DMC logs attached as well as vieos of the issue. +2025-10-27 13:40:10,492 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Problem with auth in member center +2025-10-27 13:40:10,493 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: LNNBDDEZXSD449358 +2025-10-27 13:40:10,493 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: EXEED VX FL(M36T) +2025-10-27 13:40:10,493 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: 袁清 +2025-10-27 13:40:10,493 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024: DMC logs attached as well as vieos of the issue. +2025-10-27 13:40:10,493 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:40:10,494 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:40:10,731 - src.integrations.workorder_sync - INFO - 开始转换飞书字段: {'AI建议': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf', 'Created by': 'Vsevolod Tsoi', 'Date creation': 1761235200000, 'Module(模块)': 'DMC', 'Source': 'Mail', 'TR Description': 'Abnormal traffic consumption', 'TR Level': 'Low', 'TR Number': 'TR866', 'TR Status': 'Analysising', 'TR tracking': '24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi \n24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached', 'Type of problem': 'Traffic is over', 'VIN|sim': 'XEYDD14B3SA012164', 'Vehicle Type01': 'TEST', 'Wilfulness(责任人)': 'Vsevolod Tsoi', '处理过程': '1024:建议用户抓去DMC日志回传分析\n1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf'} +2025-10-27 13:40:10,732 - src.integrations.flexible_field_mapper - INFO - 开始转换字段: ['AI建议', 'Created by', 'Date creation', 'Module(模块)', 'Source', 'TR Description', 'TR Level', 'TR Number', 'TR Status', 'TR tracking', 'Type of problem', 'VIN|sim', 'Vehicle Type01', 'Wilfulness(责任人)', '处理过程'] +2025-10-27 13:40:10,732 - src.integrations.flexible_field_mapper - INFO - 映射字段 AI建议 -> ai_suggestion: 1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 Created by -> created_by: Vsevolod Tsoi +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 Date creation -> created_at: 1761235200000 +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 Module(模块) -> module: DMC +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 Source -> source: Mail +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Description -> description: Abnormal traffic consumption +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Level -> priority: Low +2025-10-27 13:40:10,733 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Number -> order_id: TR866 +2025-10-27 13:40:10,734 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR Status -> status: Analysising +2025-10-27 13:40:10,734 - src.integrations.flexible_field_mapper - INFO - 映射字段 TR tracking -> resolution: 24/10: It is recommended that users grab the DMC log back for analysis.@Vsevolod Tsoi +24/10: user states he use Navi only and no other apps. Consumption volume - 3.12 Gb for 3 days. Same symptom as E0X - traffic package size is plus mines the same - see pdf from MNO attached +2025-10-27 13:40:10,734 - src.integrations.flexible_field_mapper - INFO - 映射字段 Type of problem -> category: Traffic is over +2025-10-27 13:40:10,734 - src.integrations.flexible_field_mapper - INFO - 映射字段 VIN|sim -> vin_sim: XEYDD14B3SA012164 +2025-10-27 13:40:10,734 - src.integrations.flexible_field_mapper - INFO - 映射字段 Vehicle Type01 -> vehicle_type: TEST +2025-10-27 13:40:10,735 - src.integrations.flexible_field_mapper - INFO - 映射字段 Wilfulness(责任人) -> wilfulness: Vsevolod Tsoi +2025-10-27 13:40:10,735 - src.integrations.flexible_field_mapper - INFO - 映射字段 处理过程 -> solution: 1024:建议用户抓去DMC日志回传分析 +1024:用户表示他只使用Navi,不使用其他应用程序。 消耗量-3.12 Gb,3天。 与E0X相同的症状-流量包大小加上地雷相同-见所附MNO的pdf +2025-10-27 13:40:10,735 - src.integrations.flexible_field_mapper - INFO - 字段转换完成: 已映射 15, 未映射 0 +2025-10-27 13:40:10,735 - src.integrations.workorder_sync - INFO - 字段转换统计: 总字段 15, 已映射 15, 未映射 0 +2025-10-27 13:40:12,208 - src.integrations.workorder_sync - INFO - 飞书同步完成: {'success': True, 'total_records': 4, 'synced_count': 4, 'created_count': 3, 'updated_count': 1, 'ai_suggestions_generated': True, 'errors': []} +2025-10-27 13:40:12,210 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:40:12] "POST /api/feishu-sync/sync-from-feishu HTTP/1.1" 200 - +2025-10-27 13:40:12,654 - werkzeug - INFO - 127.0.0.1 - - [27/Oct/2025 13:40:12] "GET /api/feishu-sync/status HTTP/1.1" 200 - +2025-10-31 10:20:11,288 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:20:11,288 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:20:15,278 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:20:18,140 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 10:20:18,147 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 10:20:18,225 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 10:20:18,230 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 10:20:18,551 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:18,765 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 10:20:18,769 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 10:20:18,769 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 10:20:21,213 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:21] "GET / HTTP/1.1" 200 - +2025-10-31 10:20:21,523 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:21] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 10:20:21,525 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:21] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 10:20:21,526 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:21] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 10:20:21,532 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:21] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 10:20:22,198 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:22] "GET /api/system/info HTTP/1.1" 200 - +2025-10-31 10:20:22,250 - websockets.server - INFO - connection open +2025-10-31 10:20:22,251 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:22] "GET /api/feishu-sync/config HTTP/1.1" 200 - +2025-10-31 10:20:22,251 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:22,416 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:22,621 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:22] "GET /api/chat/sessions HTTP/1.1" 200 - +2025-10-31 10:20:22,625 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:22] "GET /favicon.ico HTTP/1.1" 404 - +2025-10-31 10:20:22,783 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:22,841 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:22,882 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:23,054 - src.config.unified_config - WARNING - 无法加载默认LLM配置,使用内置默认值: No module named 'config.llm_config' +2025-10-31 10:20:23,056 - src.config.unified_config - INFO - 配置文件加载成功 +2025-10-31 10:20:23,056 - src.integrations.ai_suggestion_service - INFO - 使用LLM配置: openai - qwen-turbo +2025-10-31 10:20:23,057 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Number -> order_id +2025-10-31 10:20:23,059 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Description -> description +2025-10-31 10:20:23,060 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Type of problem -> category +2025-10-31 10:20:23,062 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Level -> priority +2025-10-31 10:20:23,064 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Status -> status +2025-10-31 10:20:23,065 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Source -> source +2025-10-31 10:20:23,067 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date creation -> created_at +2025-10-31 10:20:23,068 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 处理过程 -> solution +2025-10-31 10:20:23,069 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR tracking -> resolution +2025-10-31 10:20:23,071 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Created by -> created_by +2025-10-31 10:20:23,072 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Module(模块) -> module +2025-10-31 10:20:23,073 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Wilfulness(责任人) -> wilfulness +2025-10-31 10:20:23,075 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date of close TR -> date_of_close +2025-10-31 10:20:23,076 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Vehicle Type01 -> vehicle_type +2025-10-31 10:20:23,078 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: VIN|sim -> vin_sim +2025-10-31 10:20:23,083 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: App remote control version -> app_remote_control_version +2025-10-31 10:20:23,085 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: HMI SW -> hmi_sw +2025-10-31 10:20:23,087 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 父记录 -> parent_record +2025-10-31 10:20:23,088 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Has it been updated on the same day -> has_updated_same_day +2025-10-31 10:20:23,090 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Operating time -> operating_time +2025-10-31 10:20:23,091 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: AI建议 -> ai_suggestion +2025-10-31 10:20:23,093 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Issue Start Time -> updated_at +2025-10-31 10:20:23,158 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:23,190 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:20:23,203 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:23] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 10:20:23,541 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:23] "GET /api/feishu-sync/status HTTP/1.1" 200 - +2025-10-31 10:20:24,106 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:24] "GET /api/knowledge/stats HTTP/1.1" 200 - +2025-10-31 10:20:24,796 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:24] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:20:25,581 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:20:25,791 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:25] "GET /api/analytics?days=7&dimension=performance HTTP/1.1" 200 - +2025-10-31 10:20:25,827 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:25] "GET /api/settings HTTP/1.1" 200 - +2025-10-31 10:20:26,455 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:20:26,630 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:26] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:20:27,286 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:20:27,429 - websockets.server - INFO - connection open +2025-10-31 10:20:27,430 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:27,460 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:27] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:20:27,932 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:20:28,108 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:28] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:20:28,769 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:20:28,774 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:28] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:20:28,947 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:28] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:20:29,225 - src.web.service_manager - ERROR - 初始化服务 agent_assistant 失败: unexpected indent (agent_assistant.py, line 215) +2025-10-31 10:20:29,225 - src.web.service_manager - ERROR - 初始化服务 agent_assistant 失败: cannot import name 'TSPAgentAssistant' from 'src.agent_assistant' (d:\code\tsp-assistant\src\agent_assistant.py) +2025-10-31 10:20:29,225 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:29] "GET /api/agent/status HTTP/1.1" 200 - +2025-10-31 10:20:29,226 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:29] "GET /api/agent/tools/stats HTTP/1.1" 200 - +2025-10-31 10:20:31,165 - src.web.service_manager - ERROR - 初始化服务 agent_assistant 失败: unexpected indent (agent_assistant.py, line 215) +2025-10-31 10:20:31,165 - src.web.service_manager - ERROR - 初始化服务 agent_assistant 失败: cannot import name 'TSPAgentAssistant' from 'src.agent_assistant' (d:\code\tsp-assistant\src\agent_assistant.py) +2025-10-31 10:20:31,166 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:31] "GET /api/agent/status HTTP/1.1" 200 - +2025-10-31 10:20:31,166 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:31] "GET /api/agent/tools/stats HTTP/1.1" 200 - +2025-10-31 10:20:32,439 - websockets.server - INFO - connection open +2025-10-31 10:20:32,439 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:35,298 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:35] "GET /api/knowledge?page=1&per_page=5 HTTP/1.1" 200 - +2025-10-31 10:20:36,771 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:36] "GET /api/workorders?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 10:20:37,447 - websockets.server - INFO - connection open +2025-10-31 10:20:37,447 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:40,607 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:40] "GET /api/workorders/78 HTTP/1.1" 200 - +2025-10-31 10:20:42,457 - websockets.server - INFO - connection open +2025-10-31 10:20:42,458 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:45,818 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 'TR866 - Traffic is over' 返回 0 个结果 +2025-10-31 10:20:47,465 - websockets.server - INFO - connection open +2025-10-31 10:20:47,466 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:47,542 - src.core.llm_client - INFO - API请求成功 +2025-10-31 10:20:48,019 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:20:48] "POST /api/workorders/78/ai-suggestion HTTP/1.1" 200 - +2025-10-31 10:20:52,472 - websockets.server - INFO - connection open +2025-10-31 10:20:52,472 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:20:57,476 - websockets.server - INFO - connection open +2025-10-31 10:20:57,476 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + ^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: WebSocketServer.start_server..handle_client_with_cors() missing 1 required positional argument: 'path' +2025-10-31 10:21:05,750 - src.utils.semantic_similarity - INFO - 正在加载语义相似度模型: all-MiniLM-L6-v2 +2025-10-31 10:21:05,750 - sentence_transformers.SentenceTransformer - INFO - Use pytorch device_name: cpu +2025-10-31 10:21:05,750 - sentence_transformers.SentenceTransformer - INFO - Load pretrained SentenceTransformer: all-MiniLM-L6-v2 +2025-10-31 10:21:15,781 - huggingface_hub.utils._http - WARNING - '(MaxRetryError("HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded with url: /sentence-transformers/all-MiniLM-L6-v2/resolve/main/modules.json (Caused by ConnectTimeoutError(, 'Connection to huggingface.co timed out. (connect timeout=10)'))"), '(Request ID: 622918db-4f07-4b0a-90c7-2dbe3ad89dae)')' thrown while requesting HEAD https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/./modules.json +2025-10-31 10:21:15,782 - huggingface_hub.utils._http - WARNING - Retrying in 1s [Retry 1/5]. +2025-10-31 10:21:26,797 - huggingface_hub.utils._http - WARNING - '(MaxRetryError("HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded with url: /sentence-transformers/all-MiniLM-L6-v2/resolve/main/modules.json (Caused by ConnectTimeoutError(, 'Connection to huggingface.co timed out. (connect timeout=10)'))"), '(Request ID: dc47ad84-2c24-4d00-891d-a8b847cc3c24)')' thrown while requesting HEAD https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/./modules.json +2025-10-31 10:21:26,798 - huggingface_hub.utils._http - WARNING - Retrying in 2s [Retry 2/5]. +2025-10-31 10:23:35,972 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:23:35,973 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:23:38,642 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:23:40,154 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 10:23:40,169 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 10:23:40,253 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 10:23:40,255 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 10:23:40,510 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:23:40,717 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 10:23:40,723 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 10:23:40,723 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 10:24:05,102 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:24:05] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:24:05,841 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:24:06,039 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:24:06] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:24:06,177 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:24:06,776 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:24:06,851 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:24:06,978 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:24:06] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:24:17,939 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:24:17] "GET /api/workorders/78 HTTP/1.1" 200 - +2025-10-31 10:24:20,395 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 'TR866 - Traffic is over' 返回 0 个结果 +2025-10-31 10:24:23,287 - src.core.llm_client - INFO - API请求成功 +2025-10-31 10:24:23,734 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:24:23] "POST /api/workorders/78/ai-suggestion HTTP/1.1" 200 - +2025-10-31 10:24:41,514 - src.utils.semantic_similarity - INFO - 正在加载语义相似度模型: all-MiniLM-L6-v2 +2025-10-31 10:24:41,517 - sentence_transformers.SentenceTransformer - INFO - Use pytorch device_name: cpu +2025-10-31 10:24:41,517 - sentence_transformers.SentenceTransformer - INFO - Load pretrained SentenceTransformer: all-MiniLM-L6-v2 +2025-10-31 10:24:51,573 - huggingface_hub.utils._http - WARNING - '(MaxRetryError("HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded with url: /sentence-transformers/all-MiniLM-L6-v2/resolve/main/modules.json (Caused by ConnectTimeoutError(, 'Connection to huggingface.co timed out. (connect timeout=10)'))"), '(Request ID: 2e39a15f-0b3c-4396-ab81-8fe931cc28a1)')' thrown while requesting HEAD https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/./modules.json +2025-10-31 10:24:51,574 - huggingface_hub.utils._http - WARNING - Retrying in 1s [Retry 1/5]. +2025-10-31 10:25:02,586 - huggingface_hub.utils._http - WARNING - '(MaxRetryError("HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded with url: /sentence-transformers/all-MiniLM-L6-v2/resolve/main/modules.json (Caused by ConnectTimeoutError(, 'Connection to huggingface.co timed out. (connect timeout=10)'))"), '(Request ID: a9fe9039-15ce-4c23-b480-1ca6051f03d7)')' thrown while requesting HEAD https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main/./modules.json +2025-10-31 10:25:02,587 - huggingface_hub.utils._http - WARNING - Retrying in 2s [Retry 2/5]. +2025-10-31 10:27:54,276 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:27:54,276 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:27:57,185 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:27:58,439 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 10:27:58,445 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 10:27:58,510 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 10:27:58,511 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 10:27:58,844 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:27:59,048 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 10:27:59,048 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 10:27:59,048 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 10:28:08,896 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:08] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:28:09,938 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:28:10,625 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 23 个条目 +2025-10-31 10:28:14,199 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 'TR866 - Traffic is over' 返回 0 个结果 +2025-10-31 10:28:16,480 - src.core.llm_client - INFO - API请求成功 +2025-10-31 10:28:17,795 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:17] "POST /api/workorders/78/ai-suggestion HTTP/1.1" 200 - +2025-10-31 10:28:32,263 - src.utils.semantic_similarity - INFO - LLM客户端初始化成功,将使用LLM API计算语义相似度 +2025-10-31 10:28:33,078 - src.core.llm_client - INFO - API请求成功 +2025-10-31 10:28:33,079 - src.web.blueprints.workorders - INFO - AI建议与人工描述语义相似度: 0.7500 +2025-10-31 10:28:33,816 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:33] "POST /api/workorders/78/human-resolution HTTP/1.1" 200 - +2025-10-31 10:28:43,259 - src.web.blueprints.workorders - INFO - 工单 78 使用人工描述入库,AI相似度: 0.7500 +2025-10-31 10:28:43,712 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:28:43,926 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:43] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:28:44,979 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:28:45,196 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:45] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:28:45,196 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:45] "POST /api/workorders/78/approve-to-knowledge HTTP/1.1" 200 - +2025-10-31 10:28:49,136 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:49] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:28:50,185 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:28:50] "GET /api/workorders/78 HTTP/1.1" 200 - +2025-10-31 10:29:36,046 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:29:36] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:29:54,173 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:29:54,673 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:29:54] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:29:56,431 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:29:56,870 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:29:56] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:36:11,007 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:36:11] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:36:11,958 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:36:12,441 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:36:12] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:36:13,992 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:36:14,426 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:36:14] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:37:46,235 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:37:46] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:37:47,337 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:37:47,821 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:37:47] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:37:49,375 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:37:49,811 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:37:49] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:39:24,691 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:39:24] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 10:39:25,828 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:39:26,330 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:39:26] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 10:39:27,892 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 10:39:28,339 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 10:39:28] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 10:54:23,005 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:54:23,005 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:54:25,490 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:54:28,187 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xb9 in position 1: invalid start byte (workorder_permissions.py, line 5) +2025-10-31 10:59:12,440 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:59:12,440 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:59:18,642 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:59:20,279 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xb9 in position 1: invalid start byte (workorder_permissions.py, line 5) +2025-10-31 10:59:20,790 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 10:59:20,791 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 10:59:23,478 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 10:59:25,094 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xb9 in position 1: invalid start byte (workorder_permissions.py, line 5) +2025-10-31 11:02:16,212 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 11:02:16,212 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 11:02:18,720 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 11:02:19,954 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xd3 in position 0: invalid continuation byte (workorder_permissions.py, line 8) +2025-10-31 12:19:26,725 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:19:26,726 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:19:29,117 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:19:30,400 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xce in position 6: invalid continuation byte (workorder_permissions.py, line 139) +2025-10-31 12:20:34,696 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:20:34,697 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:20:38,122 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:20:39,365 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xce in position 6: invalid continuation byte (workorder_permissions.py, line 139) +2025-10-31 12:23:38,597 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:23:38,597 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:23:44,511 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:23:46,304 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:23:46,330 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:23:46,414 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 12:23:46,414 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 12:23:46,986 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:23:47,463 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 12:23:47,476 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 12:23:47,477 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 12:23:59,564 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:23:59] "GET / HTTP/1.1" 200 - +2025-10-31 12:23:59,713 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:23:59] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 12:23:59,728 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:23:59] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 12:23:59,728 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:23:59] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 12:23:59,743 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:23:59] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 12:24:00,246 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:00] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 12:24:00,249 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:00] "GET /api/system/info HTTP/1.1" 200 - +2025-10-31 12:24:00,269 - websockets.server - INFO - connection open +2025-10-31 12:24:00,271 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + File "d:\code\tsp-assistant\src\web\websocket_server.py", line 248, in handle_client_with_cors + if websocket.request_headers.get("Origin"): + ^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'ServerConnection' object has no attribute 'request_headers' +2025-10-31 12:24:00,274 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:00] "GET /api/feishu-sync/config HTTP/1.1" 200 - +2025-10-31 12:24:00,832 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:00,963 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:01,143 - src.config.unified_config - WARNING - 无法加载默认LLM配置,使用内置默认值: No module named 'config.llm_config' +2025-10-31 12:24:01,147 - src.config.unified_config - INFO - 配置文件加载成功 +2025-10-31 12:24:01,147 - src.integrations.ai_suggestion_service - INFO - 使用LLM配置: openai - qwen-turbo +2025-10-31 12:24:01,149 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Number -> order_id +2025-10-31 12:24:01,151 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Description -> description +2025-10-31 12:24:01,152 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Type of problem -> category +2025-10-31 12:24:01,159 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Level -> priority +2025-10-31 12:24:01,163 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Status -> status +2025-10-31 12:24:01,168 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:01,169 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Source -> source +2025-10-31 12:24:01,179 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date creation -> created_at +2025-10-31 12:24:01,181 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 处理过程 -> resolution +2025-10-31 12:24:01,182 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR tracking -> resolution +2025-10-31 12:24:01,183 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Created by -> created_by +2025-10-31 12:24:01,184 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Module(模块) -> module +2025-10-31 12:24:01,186 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Wilfulness(责任人) -> wilfulness +2025-10-31 12:24:01,190 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date of close TR -> date_of_close +2025-10-31 12:24:01,193 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Vehicle Type01 -> vehicle_type +2025-10-31 12:24:01,196 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: VIN|sim -> vin_sim +2025-10-31 12:24:01,198 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: App remote control version -> app_remote_control_version +2025-10-31 12:24:01,199 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: HMI SW -> hmi_sw +2025-10-31 12:24:01,201 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 父记录 -> parent_record +2025-10-31 12:24:01,202 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Has it been updated on the same day -> has_updated_same_day +2025-10-31 12:24:01,208 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Operating time -> operating_time +2025-10-31 12:24:01,212 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: AI建议 -> ai_suggestion +2025-10-31 12:24:01,214 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Issue Start Time -> updated_at +2025-10-31 12:24:01,645 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:01] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:24:01,678 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:01] "GET /api/feishu-sync/status HTTP/1.1" 200 - +2025-10-31 12:24:01,897 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:01,926 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:02,044 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:02] "GET /api/knowledge/stats HTTP/1.1" 200 - +2025-10-31 12:24:02,348 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:02] "GET /api/chat/sessions HTTP/1.1" 200 - +2025-10-31 12:24:02,583 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:24:03,371 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:03] "GET /api/workorders?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 12:24:04,015 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:24:04,482 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:04] "GET /api/analytics?days=7&dimension=performance HTTP/1.1" 200 - +2025-10-31 12:24:04,516 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:04] "GET /api/settings HTTP/1.1" 200 - +2025-10-31 12:24:05,425 - websockets.server - INFO - connection open +2025-10-31 12:24:05,426 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + File "d:\code\tsp-assistant\src\web\websocket_server.py", line 248, in handle_client_with_cors + if websocket.request_headers.get("Origin"): + ^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'ServerConnection' object has no attribute 'request_headers' +2025-10-31 12:24:05,829 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:05] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:24:06,288 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:24:06,761 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:06] "GET /api/knowledge?page=1&per_page=5 HTTP/1.1" 200 - +2025-10-31 12:24:06,770 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:06] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:24:07,338 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:07] "GET /api/alerts?page=1&per_page=50 HTTP/1.1" 200 - +2025-10-31 12:24:07,541 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:24:07,719 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:07] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:24:08,433 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:24:08,590 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:08] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:24:08,886 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:08] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:24:10,437 - websockets.server - INFO - connection open +2025-10-31 12:24:10,439 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + File "d:\code\tsp-assistant\src\web\websocket_server.py", line 248, in handle_client_with_cors + if websocket.request_headers.get("Origin"): + ^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'ServerConnection' object has no attribute 'request_headers' +2025-10-31 12:24:10,621 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:24:11,085 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:11] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:24:12,045 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:24:12] "GET /api/knowledge?page=1&per_page=5 HTTP/1.1" 200 - +2025-10-31 12:24:15,449 - websockets.server - INFO - connection open +2025-10-31 12:24:15,450 - websockets.server - ERROR - connection handler failed +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\websockets\asyncio\server.py", line 376, in conn_handler + await self.handler(connection) + File "d:\code\tsp-assistant\src\web\websocket_server.py", line 248, in handle_client_with_cors + if websocket.request_headers.get("Origin"): + ^^^^^^^^^^^^^^^^^^^^^^^^^ +AttributeError: 'ServerConnection' object has no attribute 'request_headers' +2025-10-31 12:26:03,635 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:26:03,635 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:26:05,958 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:26:07,227 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:26:07,246 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:26:07,397 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 12:26:07,398 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 12:26:07,591 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:07,756 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 12:26:07,775 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 12:26:07,775 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 12:26:09,613 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET / HTTP/1.1" 200 - +2025-10-31 12:26:09,721 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 12:26:09,722 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 12:26:09,723 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 12:26:09,729 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 12:26:09,933 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 12:26:09,935 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /api/system/info HTTP/1.1" 200 - +2025-10-31 12:26:09,961 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /api/workorders?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 12:26:09,968 - websockets.server - INFO - connection open +2025-10-31 12:26:09,969 - src.web.websocket_server - INFO - 客户端连接: ('::1', 62620, 0, 0) +2025-10-31 12:26:09,970 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:09] "GET /api/feishu-sync/config HTTP/1.1" 200 - +2025-10-31 12:26:10,098 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:10,309 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:10] "GET /api/chat/sessions HTTP/1.1" 200 - +2025-10-31 12:26:10,601 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:10,693 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:10,874 - src.config.unified_config - WARNING - 无法加载默认LLM配置,使用内置默认值: No module named 'config.llm_config' +2025-10-31 12:26:10,875 - src.config.unified_config - INFO - 配置文件加载成功 +2025-10-31 12:26:10,875 - src.integrations.ai_suggestion_service - INFO - 使用LLM配置: openai - qwen-turbo +2025-10-31 12:26:10,877 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Number -> order_id +2025-10-31 12:26:10,878 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Description -> description +2025-10-31 12:26:10,880 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Type of problem -> category +2025-10-31 12:26:10,882 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Level -> priority +2025-10-31 12:26:10,883 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR Status -> status +2025-10-31 12:26:10,884 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Source -> source +2025-10-31 12:26:10,885 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date creation -> created_at +2025-10-31 12:26:10,886 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 处理过程 -> resolution +2025-10-31 12:26:10,888 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: TR tracking -> resolution +2025-10-31 12:26:10,889 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Created by -> created_by +2025-10-31 12:26:10,890 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Module(模块) -> module +2025-10-31 12:26:10,891 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Wilfulness(责任人) -> wilfulness +2025-10-31 12:26:10,892 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Date of close TR -> date_of_close +2025-10-31 12:26:10,893 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Vehicle Type01 -> vehicle_type +2025-10-31 12:26:10,894 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: VIN|sim -> vin_sim +2025-10-31 12:26:10,895 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: App remote control version -> app_remote_control_version +2025-10-31 12:26:10,898 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: HMI SW -> hmi_sw +2025-10-31 12:26:10,900 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: 父记录 -> parent_record +2025-10-31 12:26:10,903 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Has it been updated on the same day -> has_updated_same_day +2025-10-31 12:26:10,906 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Operating time -> operating_time +2025-10-31 12:26:10,908 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: AI建议 -> ai_suggestion +2025-10-31 12:26:10,909 - src.integrations.flexible_field_mapper - INFO - 添加字段映射: Issue Start Time -> updated_at +2025-10-31 12:26:10,952 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:10,958 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:10] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:11,384 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:11] "GET /api/feishu-sync/status HTTP/1.1" 200 - +2025-10-31 12:26:11,432 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:12,024 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:26:12,908 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:12] "GET /api/knowledge/stats HTTP/1.1" 200 - +2025-10-31 12:26:14,396 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:14,824 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:14] "GET /api/analytics?days=7&dimension=performance HTTP/1.1" 200 - +2025-10-31 12:26:14,853 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:14] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:14,864 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:14] "GET /api/settings HTTP/1.1" 200 - +2025-10-31 12:26:15,038 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:15,213 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:15] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:26:15,547 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:15] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:15,714 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:15,881 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:15] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:26:16,289 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:16,725 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:16] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:26:17,188 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:17,385 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:17,390 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:17] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:26:17,562 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:17] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:26:18,629 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:19,046 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:19] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:26:23,390 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:23] "GET /api/knowledge?page=1&per_page=5 HTTP/1.1" 200 - +2025-10-31 12:26:26,395 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:26] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:26,998 - src.agent_assistant - INFO - TSP Agent助手初始化完成 +2025-10-31 12:26:26,998 - src.agent_assistant - INFO - TSP Agent助手初始化完成 +2025-10-31 12:26:26,999 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:26] "GET /api/agent/status HTTP/1.1" 200 - +2025-10-31 12:26:27,000 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:26] "GET /api/agent/tools/stats HTTP/1.1" 200 - +2025-10-31 12:26:29,007 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:29] "GET /api/chat/sessions HTTP/1.1" 200 - +2025-10-31 12:26:29,007 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:29] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 12:26:29,548 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:29] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:29,711 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:29] "GET /api/knowledge/stats HTTP/1.1" 200 - +2025-10-31 12:26:30,263 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:30] "GET /api/chat/sessions HTTP/1.1" 200 - +2025-10-31 12:26:30,264 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:30] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 12:26:30,821 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:30] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:26:30,987 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:30] "GET /api/knowledge/stats HTTP/1.1" 200 - +2025-10-31 12:26:31,316 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:31] "GET /api/conversations?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 12:26:31,321 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:31,766 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:31] "GET /api/analytics?days=7&dimension=performance HTTP/1.1" 200 - +2025-10-31 12:26:31,810 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:31] "GET /api/settings HTTP/1.1" 200 - +2025-10-31 12:26:32,413 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:32,456 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:32,599 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:32] "GET /api/analytics?days=7&dimension=performance HTTP/1.1" 200 - +2025-10-31 12:26:32,620 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:32] "GET /api/settings HTTP/1.1" 200 - +2025-10-31 12:26:32,636 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:32] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:26:33,275 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:33,301 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:33,379 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/token-monitor/stats HTTP/1.1" 200 - +2025-10-31 12:26:33,442 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/ai-monitor/stats HTTP/1.1" 200 - +2025-10-31 12:26:33,446 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:26:33,477 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:26:33,896 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/ai-monitor/error-distribution HTTP/1.1" 200 - +2025-10-31 12:26:33,896 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:33] "GET /api/ai-monitor/model-comparison HTTP/1.1" 200 - +2025-10-31 12:26:33,928 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:26:34,101 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:34] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:26:34,278 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:34] "GET /api/token-monitor/records HTTP/1.1" 200 - +2025-10-31 12:26:35,579 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:35] "GET /api/system-optimizer/status HTTP/1.1" 200 - +2025-10-31 12:26:35,595 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:35] "GET /api/system-optimizer/security-settings HTTP/1.1" 200 - +2025-10-31 12:26:35,597 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:35] "GET /api/system-optimizer/traffic-settings HTTP/1.1" 200 - +2025-10-31 12:26:35,598 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:35] "GET /api/system-optimizer/cost-settings HTTP/1.1" 200 - +2025-10-31 12:26:36,317 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:36] "GET /api/ai-monitor/error-log HTTP/1.1" 200 - +2025-10-31 12:26:36,347 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:26:36] "GET /api/token-monitor/chart HTTP/1.1" 200 - +2025-10-31 12:27:14,945 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:14] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:27:21,974 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:27:22,442 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:22] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:27:23,117 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:27:23,289 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:23] "GET /api/workorders/by-status/open HTTP/1.1" 200 - +2025-10-31 12:27:23,298 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:23] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:27:29,068 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:29] "GET /api/workorders/by-status/in_progress HTTP/1.1" 200 - +2025-10-31 12:27:49,139 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:49] "GET /api/workorders HTTP/1.1" 200 - +2025-10-31 12:27:53,365 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:53] "GET /api/workorders/by-status/open HTTP/1.1" 200 - +2025-10-31 12:27:56,511 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:56] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:27:57,038 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:27:57] "GET /api/workorders/65 HTTP/1.1" 403 - +2025-10-31 12:35:12,988 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:35:12,988 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:35:15,563 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:35:15,567 - __main__ - ERROR - 启动失败: (unicode error) 'utf-8' codec can't decode byte 0xd3 in position 1: invalid continuation byte (auth.py, line 5) +2025-10-31 12:37:21,363 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:37:21,363 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:37:24,356 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:37:26,154 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:37:26,164 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:37:26,168 - __main__ - ERROR - 启动失败: name 'secrets' is not defined +2025-10-31 12:39:45,468 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:39:45,469 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:39:48,142 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:39:49,939 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:39:49,950 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:39:50,040 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 12:39:50,041 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 12:39:50,243 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:39:50,416 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 12:39:50,423 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 12:39:50,423 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 12:39:54,625 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:39:54] "GET / HTTP/1.1" 302 - +2025-10-31 12:39:54,631 - src.web.app - ERROR - Exception on /login [GET] +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask_cors\extension.py", line 176, in wrapped_function + return cors_after_request(app.make_response(f(*args, **kwargs))) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + ^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "d:\code\tsp-assistant\src\web\app.py", line 86, in login_page + return render_template('login.html') + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 149, in render_template + template = app.jinja_env.get_or_select_template(template_name_or_list) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 1087, in get_or_select_template + return self.get_template(template_name_or_list, parent, globals) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 1016, in get_template + return self._load_template(name, globals) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 975, in _load_template + template = self.loader.load(self, name, self.make_globals(globals)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\loaders.py", line 126, in load + source, filename, uptodate = self.get_source(environment, name) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 65, in get_source + return self._get_source_fast(environment, template) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 96, in _get_source_fast + return loader.get_source(environment, template) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\loaders.py", line 215, in get_source + contents = f.read() + ^^^^^^^^ + File "", line 322, in decode +UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 164: invalid continuation byte +2025-10-31 12:39:54,642 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:39:54] "GET /login HTTP/1.1" 500 - +2025-10-31 12:39:59,605 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:39:59] "GET /api/alerts?per_page=1000 HTTP/1.1" 200 - +2025-10-31 12:39:59,738 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:40:00,083 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:40:00,157 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:40:00,333 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:40:00] "GET /api/analytics HTTP/1.1" 200 - +2025-10-31 12:40:00,411 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:40:00] "GET / HTTP/1.1" 302 - +2025-10-31 12:40:00,418 - src.web.app - ERROR - Exception on /login [GET] +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask_cors\extension.py", line 176, in wrapped_function + return cors_after_request(app.make_response(f(*args, **kwargs))) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + ^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "d:\code\tsp-assistant\src\web\app.py", line 86, in login_page + return render_template('login.html') + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 149, in render_template + template = app.jinja_env.get_or_select_template(template_name_or_list) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 1087, in get_or_select_template + return self.get_template(template_name_or_list, parent, globals) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 1016, in get_template + return self._load_template(name, globals) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\environment.py", line 975, in _load_template + template = self.loader.load(self, name, self.make_globals(globals)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\loaders.py", line 126, in load + source, filename, uptodate = self.get_source(environment, name) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 65, in get_source + return self._get_source_fast(environment, template) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\flask\templating.py", line 96, in _get_source_fast + return loader.get_source(environment, template) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\jinja2\loaders.py", line 215, in get_source + contents = f.read() + ^^^^^^^^ + File "", line 322, in decode +UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 164: invalid continuation byte +2025-10-31 12:40:00,423 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:40:00] "GET /login HTTP/1.1" 500 - +2025-10-31 12:40:00,783 - src.core.query_optimizer - WARNING - 未映射的状态: 'Analysising' (数量: 1) +2025-10-31 12:40:00,953 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:40:00] "GET /api/analytics?timeRange=30&dimension=workorders HTTP/1.1" 200 - +2025-10-31 12:42:44,731 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:42:44,731 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:42:47,447 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:42:49,920 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:42:49,937 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:42:50,041 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 12:42:50,042 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 12:42:50,310 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:42:50,512 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 12:42:50,525 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 12:42:50,525 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 12:42:54,157 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:42:54] "GET /login HTTP/1.1" 200 - +2025-10-31 12:43:01,682 - src.core.database - ERROR - 数据库操作失败: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-10-31 12:43:01,684 - src.web.blueprints.auth - ERROR - 登录失败: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute + cursor.execute(statement, parameters) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 153, in execute + result = self._query(query) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 322, in _query + conn.query(q) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 575, in query + self._affected_rows = self._read_query_result(unbuffered=unbuffered) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 826, in _read_query_result + result.read() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 1203, in read + first_packet = self.connection._read_packet() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 782, in _read_packet + packet.raise_for_error() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\protocol.py", line 219, in raise_for_error + err.raise_mysql_exception(self._data) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\err.py", line 150, in raise_mysql_exception + raise errorclass(errno, errval) +pymysql.err.OperationalError: (1054, "Unknown column 'users.is_active' in 'field list'") + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "d:\code\tsp-assistant\src\web\blueprints\auth.py", line 45, in login + user = db_session.query(User).filter(User.username == username).first() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\query.py", line 2759, in first + return self.limit(1)._iter().first() # type: ignore + ^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\query.py", line 2857, in _iter + result: Union[ScalarResult[_T], Result[_T]] = self.session.execute( + ^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\session.py", line 2351, in execute + return self._execute_internal( + ^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\session.py", line 2249, in _execute_internal + result: Result[Any] = compile_state_cls.orm_execute_statement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\context.py", line 306, in orm_execute_statement + result = conn.execute( + ^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute + return meth( + ^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\sql\elements.py", line 526, in _execute_on_connection + return connection._execute_clauseelement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement + ret = self._execute_context( + ^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context + return self._exec_single_context( + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context + self._handle_dbapi_exception( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 2355, in _handle_dbapi_exception + raise sqlalchemy_exception.with_traceback(exc_info[2]) from e + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute + cursor.execute(statement, parameters) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 153, in execute + result = self._query(query) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 322, in _query + conn.query(q) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 575, in query + self._affected_rows = self._read_query_result(unbuffered=unbuffered) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 826, in _read_query_result + result.read() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 1203, in read + first_packet = self.connection._read_packet() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 782, in _read_packet + packet.raise_for_error() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\protocol.py", line 219, in raise_for_error + err.raise_mysql_exception(self._data) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\err.py", line 150, in raise_mysql_exception + raise errorclass(errno, errval) +sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-10-31 12:43:01,706 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:43:01] "POST /api/auth/login HTTP/1.1" 500 - +2025-10-31 12:43:09,639 - src.core.database - ERROR - 数据库操作失败: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-10-31 12:43:09,639 - src.web.blueprints.auth - ERROR - 登录失败: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +Traceback (most recent call last): + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute + cursor.execute(statement, parameters) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 153, in execute + result = self._query(query) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 322, in _query + conn.query(q) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 575, in query + self._affected_rows = self._read_query_result(unbuffered=unbuffered) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 826, in _read_query_result + result.read() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 1203, in read + first_packet = self.connection._read_packet() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 782, in _read_packet + packet.raise_for_error() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\protocol.py", line 219, in raise_for_error + err.raise_mysql_exception(self._data) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\err.py", line 150, in raise_mysql_exception + raise errorclass(errno, errval) +pymysql.err.OperationalError: (1054, "Unknown column 'users.is_active' in 'field list'") + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "d:\code\tsp-assistant\src\web\blueprints\auth.py", line 45, in login + user = db_session.query(User).filter(User.username == username).first() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\query.py", line 2759, in first + return self.limit(1)._iter().first() # type: ignore + ^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\query.py", line 2857, in _iter + result: Union[ScalarResult[_T], Result[_T]] = self.session.execute( + ^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\session.py", line 2351, in execute + return self._execute_internal( + ^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\session.py", line 2249, in _execute_internal + result: Result[Any] = compile_state_cls.orm_execute_statement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\orm\context.py", line 306, in orm_execute_statement + result = conn.execute( + ^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute + return meth( + ^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\sql\elements.py", line 526, in _execute_on_connection + return connection._execute_clauseelement( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement + ret = self._execute_context( + ^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context + return self._exec_single_context( + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context + self._handle_dbapi_exception( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 2355, in _handle_dbapi_exception + raise sqlalchemy_exception.with_traceback(exc_info[2]) from e + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context + self.dialect.do_execute( + File "D:\code\tsp-assistant\.venv\Lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute + cursor.execute(statement, parameters) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 153, in execute + result = self._query(query) + ^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\cursors.py", line 322, in _query + conn.query(q) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 575, in query + self._affected_rows = self._read_query_result(unbuffered=unbuffered) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 826, in _read_query_result + result.read() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 1203, in read + first_packet = self.connection._read_packet() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\connections.py", line 782, in _read_packet + packet.raise_for_error() + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\protocol.py", line 219, in raise_for_error + err.raise_mysql_exception(self._data) + File "D:\code\tsp-assistant\.venv\Lib\site-packages\pymysql\err.py", line 150, in raise_mysql_exception + raise errorclass(errno, errval) +sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'users.is_active' in 'field list'") +[SQL: SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email, users.password_hash AS users_password_hash, users.`role` AS users_role, users.region AS users_region, users.is_active AS users_is_active, users.last_login AS users_last_login, users.created_at AS users_created_at, users.updated_at AS users_updated_at +FROM users +WHERE users.username = %(username_1)s + LIMIT %(param_1)s] +[parameters: {'username_1': 'admin', 'param_1': 1}] +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-10-31 12:43:09,654 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:43:09] "POST /api/auth/login HTTP/1.1" 500 - +2025-10-31 12:48:22,420 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 12:48:22,420 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 12:48:24,792 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 12:48:26,053 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 12:48:26,060 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 12:48:26,118 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 12:48:26,120 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 12:48:26,308 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 12:48:26,481 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 12:48:26,486 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 12:48:26,487 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 12:48:36,866 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:48:36] "POST /api/auth/login HTTP/1.1" 401 - +2025-10-31 12:49:24,914 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:49:24] "POST /api/auth/login HTTP/1.1" 401 - +2025-10-31 12:49:27,309 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:49:27] "GET /login HTTP/1.1" 200 - +2025-10-31 12:49:34,349 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 12:49:34] "POST /api/auth/login HTTP/1.1" 401 - +2025-10-31 13:01:47,457 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 13:01:47,457 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 13:01:53,606 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 13:01:55,009 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 13:01:55,009 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 13:01:55,076 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 13:01:55,077 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 13:01:55,910 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:01:56,416 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 13:01:56,423 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 13:01:56,423 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 13:01:58,044 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:01:58] "GET / HTTP/1.1" 302 - +2025-10-31 13:01:58,062 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:01:58] "GET /login HTTP/1.1" 200 - +2025-10-31 13:02:05,656 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:02:06,306 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:02:07,605 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:02:07,607 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:02:08,097 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:02:08,131 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:02:08,204 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:02:08,220 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:02:08,220 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:02:08,259 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:08] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:02:24,434 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:24] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:02:24,451 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:24] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:02:24,455 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:24] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:02:24,458 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:24] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:02:24,466 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:24] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:02:41,273 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:02:41] "GET /login HTTP/1.1" 200 - +2025-10-31 13:09:30,301 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 13:09:30,301 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 13:09:33,057 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 13:09:34,598 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 13:09:34,598 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 13:09:34,674 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 13:09:34,675 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 13:09:34,893 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:09:35,068 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 13:09:35,084 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 13:09:35,084 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 13:09:56,870 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:56] "GET /dashboard HTTP/1.1" 302 - +2025-10-31 13:09:56,870 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:56] "GET /login HTTP/1.1" 200 - +2025-10-31 13:09:58,797 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:09:59,072 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:09:59,537 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:09:59,537 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:09:59,719 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:09:59,758 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:09:59,829 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:09:59,872 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:09:59,874 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:09:59,880 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:09:59] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:10:08,046 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:10:08,071 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:08,079 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:08,081 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:10:08,090 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:10:08,477 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:10:08,503 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:08,523 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:10:08,524 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:08,530 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:08] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:10:11,997 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:11] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:10:12,033 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:12] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:12,038 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:12] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:10:12,039 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:12] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:10:12,047 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:12] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:10:26,711 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:26] "GET /dashboard/settings HTTP/1.1" 404 - +2025-10-31 13:10:31,609 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:31] "GET /settings HTTP/1.1" 404 - +2025-10-31 13:10:33,562 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:33] "GET /dashboard/settings HTTP/1.1" 404 - +2025-10-31 13:10:36,311 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:36] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:10:36,339 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:10:36] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:12:01,745 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:12:01] "GET /index HTTP/1.1" 404 - +2025-10-31 13:13:36,482 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:13:36] "GET /chat HTTP/1.1" 200 - +2025-10-31 13:13:36,525 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:13:36] "GET /static/js/chat.js HTTP/1.1" 200 - +2025-10-31 13:13:42,132 - websockets.server - INFO - connection open +2025-10-31 13:13:42,132 - src.web.websocket_server - INFO - 客户端连接: ('::1', 50207, 0, 0) +2025-10-31 13:13:42,143 - src.dialogue.realtime_chat - INFO - 创建新会话: session_user_001_1761887622 +2025-10-31 13:13:44,228 - src.knowledge_base.knowledge_manager - INFO - 搜索查询 'APP显示车辆信息错误' 返回 1 个结果 +2025-10-31 13:13:47,831 - src.core.llm_client - INFO - API请求成功 +2025-10-31 13:14:03,097 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:03] "GET /feishu HTTP/1.1" 404 - +2025-10-31 13:14:03,132 - src.web.websocket_server - INFO - 客户端断开: ('::1', 50207, 0, 0) +2025-10-31 13:14:10,964 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:10] "GET /alert HTTP/1.1" 404 - +2025-10-31 13:14:15,050 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:15] "GET /feishu HTTP/1.1" 404 - +2025-10-31 13:14:15,220 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:15] "GET /chat HTTP/1.1" 200 - +2025-10-31 13:14:15,260 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:15] "GET /static/js/chat.js HTTP/1.1" 304 - +2025-10-31 13:14:16,060 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /index HTTP/1.1" 404 - +2025-10-31 13:14:16,603 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:14:16,649 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:14:16,661 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:14:16,663 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:14:16,692 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:14:16] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:19:02,767 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 13:19:02,767 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 13:19:05,918 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 13:19:07,029 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 13:19:07,035 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 13:19:07,095 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 13:19:07,095 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 13:19:07,364 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:19:07,596 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 13:19:07,612 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 13:19:07,612 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 13:19:13,296 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:13] "GET /dashboard HTTP/1.1" 302 - +2025-10-31 13:19:13,304 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:13] "GET /login HTTP/1.1" 200 - +2025-10-31 13:19:14,579 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:19:14,892 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:19:15,481 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:19:15,482 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:19:15,723 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:19:15,752 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:19:15,834 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:19:15,856 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:19:15,857 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:19:15,867 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:15] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:19:16,233 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:19:16,463 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:19:16,727 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:16] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:19:16,733 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:16] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:19:46,771 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:19:46] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:20:15,384 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:15] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:20:15,414 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:15] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:20:15,420 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:15] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:20:15,421 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:15] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:20:15,431 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:15] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:20:15,653 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:20:15,889 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:20:16,105 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:16] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:20:16,105 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:16] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:20:46,845 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:20:46] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:21:16,123 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:21:16] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:21:46,795 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:21:46] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:22:16,810 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:22:16] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:22:46,795 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:22:46] "GET /api/system/health HTTP/1.1" 404 - +2025-10-31 13:26:13,386 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 13:26:13,386 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 13:26:18,852 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 13:26:20,394 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 13:26:20,410 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 13:26:20,473 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 13:26:20,474 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 13:26:20,940 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:26:21,389 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 13:26:21,395 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 13:26:21,396 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 13:26:23,582 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:23] "GET /dashboard HTTP/1.1" 302 - +2025-10-31 13:26:23,582 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:23] "GET /login HTTP/1.1" 200 - +2025-10-31 13:26:24,871 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:26:25,381 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:26:26,499 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:26:26,500 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:26:26,934 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:26] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:26:26,955 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:26] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:26:27,045 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:27] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:26:27,055 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:27] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:26:27,055 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:27] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:26:27,073 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:27] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:26:27,193 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:26:27,630 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:26:28,520 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:28] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:26:29,229 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:26:30,345 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:26:37,214 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:37] "GET /api/alerts?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 13:26:38,300 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:26:38] "GET /api/alerts?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 13:28:31,774 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:31] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:28:31,799 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:31] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:31,807 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:31] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:31,813 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:31] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:28:31,815 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:31] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:28:32,057 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:28:32,489 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:28:32,871 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:28:32,902 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:32,913 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:32,913 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:28:32,922 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:28:32,925 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:32] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:28:37,660 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:37] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:28:37,701 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:37] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:37,706 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:37] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:37,708 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:37] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:28:37,721 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:37] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:28:40,801 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:28:41,058 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:28:41,166 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:28:41,491 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:28:41,725 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:28:41,725 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:28:41,923 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:41] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:28:41,939 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:41] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:28:41,958 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:41] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:28:42,000 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:42] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:42,004 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:42] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:42,022 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:42] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:28:42,023 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:42] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:28:42,222 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:28:42,453 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:28:42,685 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:42] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:28:57,659 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:57] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:28:57,696 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:57] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:57,709 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:57] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:28:57,710 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:57] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:28:57,724 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:57] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:28:57,946 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:28:58,395 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:28:58,822 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:28:58] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:29:52,429 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:52] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:29:52,484 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:52] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:29:52,485 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:52] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 200 - +2025-10-31 13:29:52,486 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:52] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:29:52,500 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:52] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:29:52,708 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:29:52,945 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:29:53,173 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:29:53] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:29:59,774 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-10-31 13:29:59,774 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-10-31 13:30:05,973 - src.core.database - INFO - 数据库初始化成功 +2025-10-31 13:30:07,165 - src.core.backup_manager - INFO - 备份数据库初始化成功: tsp_assistant.db +2025-10-31 13:30:07,173 - src.integrations.config_manager - INFO - 配置加载成功 +2025-10-31 13:30:07,239 - werkzeug - INFO - WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.210.229.242:5000 +2025-10-31 13:30:07,240 - werkzeug - INFO - Press CTRL+C to quit +2025-10-31 13:30:07,759 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:30:08,243 - src.web.websocket_server - INFO - 启动WebSocket服务器: ws://localhost:8765 +2025-10-31 13:30:08,263 - websockets.server - INFO - server listening on 127.0.0.1:8765 +2025-10-31 13:30:08,263 - websockets.server - INFO - server listening on [::1]:8765 +2025-10-31 13:30:12,599 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:12] "GET /dashboard HTTP/1.1" 302 - +2025-10-31 13:30:12,606 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:12] "GET /login HTTP/1.1" 200 - +2025-10-31 13:30:13,778 - src.web.blueprints.auth - INFO - 登录尝试:用户名=admin +2025-10-31 13:30:14,392 - src.web.blueprints.auth - INFO - 密码验证通过:用户 admin +2025-10-31 13:30:15,631 - src.web.blueprints.auth - INFO - Session创建成功:用户 admin, 角色 admin +2025-10-31 13:30:15,631 - src.web.blueprints.auth - INFO - 用户 admin 登录成功 +2025-10-31 13:30:16,126 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "POST /api/auth/login HTTP/1.1" 200 - +2025-10-31 13:30:16,158 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /dashboard HTTP/1.1" 200 - +2025-10-31 13:30:16,292 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /static/js/dashboard.js?v=1.0.9 HTTP/1.1" 304 - +2025-10-31 13:30:16,292 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /static/css/design-system.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:30:16,292 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /static/css/style.css?v=1.0.0 HTTP/1.1" 304 - +2025-10-31 13:30:16,292 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /static/js/chat.js?v=1.0.0 HTTP/1.1" 200 - +2025-10-31 13:30:16,309 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:16] "GET /static/css/design-system.css HTTP/1.1" 304 - +2025-10-31 13:30:16,465 - src.web.blueprints.auth - INFO - 获取用户信息:user_id=2 +2025-10-31 13:30:16,963 - src.web.blueprints.auth - INFO - 获取用户信息成功:username=admin +2025-10-31 13:30:17,462 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:30:17] "GET /api/auth/current-user HTTP/1.1" 200 - +2025-10-31 13:30:17,990 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:30:19,014 - src.knowledge_base.knowledge_manager - INFO - 向量化器加载成功,包含 24 个条目 +2025-10-31 13:30:19,988 - websockets.server - INFO - connection open +2025-10-31 13:30:19,988 - src.web.websocket_server - INFO - 客户端连接: ('::1', 51692, 0, 0) +2025-10-31 13:30:19,992 - src.dialogue.realtime_chat - INFO - 创建新会话: session_user_001_1761888619 +2025-10-31 13:31:03,239 - src.dialogue.realtime_chat - INFO - 结束会话: session_user_001_1761888619 +2025-10-31 13:31:08,610 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:31:08] "GET /api/alerts?page=1&per_page=10 HTTP/1.1" 200 - +2025-10-31 13:31:09,889 - werkzeug - INFO - 127.0.0.1 - - [31/Oct/2025 13:31:09] "GET /api/alerts?page=1&per_page=10 HTTP/1.1" 200 - +2025-11-05 10:11:10,804 - __main__ - INFO - 正在启动TSP智能助手综合管理平台... +2025-11-05 10:11:10,804 - __main__ - INFO - 跳过系统检查,直接启动服务... +2025-11-05 10:11:21,533 - src.core.database - ERROR - 数据库初始化失败: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on '43.134.68.207' (timed out)") +(Background on this error at: https://sqlalche.me/e/20/e3q8) +2025-11-05 10:11:21,534 - __main__ - ERROR - 启动失败: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on '43.134.68.207' (timed out)") +(Background on this error at: https://sqlalche.me/e/20/e3q8) diff --git a/monitoring/grafana/provisioning/dashboards/dashboard.yml b/monitoring/grafana/provisioning/dashboards/dashboard.yml deleted file mode 100644 index be165c4..0000000 --- a/monitoring/grafana/provisioning/dashboards/dashboard.yml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: 1 - -providers: - - name: 'default' - orgId: 1 - folder: '' - type: file - disableDeletion: false - updateIntervalSeconds: 10 - allowUiUpdates: true - options: - path: /etc/grafana/provisioning/dashboards diff --git a/monitoring/grafana/provisioning/datasources/prometheus.yml b/monitoring/grafana/provisioning/datasources/prometheus.yml deleted file mode 100644 index 1a57b69..0000000 --- a/monitoring/grafana/provisioning/datasources/prometheus.yml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: 1 - -datasources: - - name: Prometheus - type: prometheus - access: proxy - url: http://prometheus:9090 - isDefault: true - editable: true diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml deleted file mode 100644 index 991013d..0000000 --- a/monitoring/prometheus.yml +++ /dev/null @@ -1,45 +0,0 @@ -global: - scrape_interval: 15s - evaluation_interval: 15s - -rule_files: - # - "first_rules.yml" - # - "second_rules.yml" - -scrape_configs: - # Prometheus自身监控 - - job_name: 'prometheus' - static_configs: - - targets: ['localhost:9090'] - - # TSP助手应用监控 - - job_name: 'tsp-assistant' - static_configs: - - targets: ['tsp-assistant:5000'] - metrics_path: '/api/metrics' - scrape_interval: 30s - scrape_timeout: 10s - - # MySQL监控 - - job_name: 'mysql' - static_configs: - - targets: ['mysql:3306'] - scrape_interval: 30s - - # Redis监控 - - job_name: 'redis' - static_configs: - - targets: ['redis:6379'] - scrape_interval: 30s - - # Nginx监控 - - job_name: 'nginx' - static_configs: - - targets: ['nginx:80'] - scrape_interval: 30s - - # Node Exporter(系统监控) - - job_name: 'node-exporter' - static_configs: - - targets: ['node-exporter:9100'] - scrape_interval: 30s diff --git a/requirements.txt b/requirements.txt index 1c1caf3..be271b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,84 +1,80 @@ # 核心依赖 -sqlalchemy>=2.0.0 -requests>=2.31.0 -numpy>=1.24.0 -scikit-learn>=1.3.0 +sqlalchemy>=2.0.32 +requests>=2.32.3 +numpy>=1.26.4 +scikit-learn>=1.4.2 # 数据库驱动 -pymysql>=1.1.0 -cryptography>=41.0.0 -flask>=2.3.0 -flask-cors>=4.0.0 -websockets>=11.0.0 +pymysql>=1.1.1 +cryptography>=43.0.1 +flask>=3.0.3 +flask-cors>=5.0.0 +websockets>=15.0.1 # 中文处理 jieba>=0.42.1 # 系统监控 -psutil>=5.9.0 +psutil>=5.9.8 # 数据处理 -pandas>=2.0.0 -openpyxl>=3.1.0 +pandas>=2.2.2 +openpyxl>=3.1.5 -# 向量化 -sentence-transformers>=2.2.0 -transformers>=4.30.0 -torch>=2.0.0 +# 向量化(可选,如果不需要可以注释掉以节省空间) +# sentence-transformers>=2.7.1 +# transformers>=4.43.2 +# torch>=2.4.1 # 日志和配置 -python-dotenv>=1.0.0 -structlog>=23.0.0 +python-dotenv>=1.0.1 +structlog>=24.4.0 # 时间处理 -python-dateutil>=2.8.0 +python-dateutil>=2.9.0 # JSON处理 -ujson>=5.8.0 +ujson>=5.10.0 # 异步支持 -aiohttp>=3.8.0 -asyncio>=3.4.3 +aiohttp>=3.10.10 +# asyncio是Python内置模块,不需要安装 # Redis缓存 -redis>=4.5.0 -redis-py-cluster>=2.1.0 +redis>=5.2.0 +redis-py-cluster>=2.1.3 # 测试框架 -pytest>=7.4.0 -pytest-asyncio>=0.21.0 -pytest-cov>=4.1.0 +pytest>=8.3.3 +pytest-asyncio>=0.24.0 +pytest-cov>=6.0.0 # 代码质量 -black>=23.0.0 -flake8>=6.0.0 -mypy>=1.5.0 -isort>=5.12.0 - -# 性能监控 -prometheus-client>=0.17.0 -grafana-api>=1.0.0 +black>=24.8.0 +flake8>=7.1.1 +mypy>=1.11.1 +isort>=5.13.2 # 安全 -bcrypt>=4.0.0 -pyjwt>=2.8.0 +bcrypt>=4.2.1 +pyjwt>=2.9.0 # 文件处理 python-magic>=0.4.27 -pillow>=10.0.0 +pillow>=11.0.0 # 网络工具 -urllib3>=2.0.0 -httpx>=0.24.0 +urllib3>=2.2.3 +httpx>=0.27.2 # 数据验证 -pydantic>=2.0.0 -marshmallow>=3.20.0 +pydantic>=2.9.2 +marshmallow>=3.21.4 -# 任务队列 -celery>=5.3.0 -kombu>=5.3.0 +# 任务队列(可选) +# celery>=5.4.0 +# kombu>=5.4.1 -# 文档生成 -sphinx>=7.0.0 -sphinx-rtd-theme>=1.3.0 +# 文档生成(可选) +# sphinx>=7.5.0 +# sphinx-rtd-theme>=2.0.0 diff --git a/simple_git_push.bat b/simple_git_push.bat new file mode 100644 index 0000000..7b09b7c --- /dev/null +++ b/simple_git_push.bat @@ -0,0 +1,108 @@ +@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 + diff --git a/src/__pycache__/agent_assistant.cpython-311.pyc b/src/__pycache__/agent_assistant.cpython-311.pyc index 2e266e8..eb66b05 100644 Binary files a/src/__pycache__/agent_assistant.cpython-311.pyc and b/src/__pycache__/agent_assistant.cpython-311.pyc differ diff --git a/src/agent_assistant.py b/src/agent_assistant.py index 87f1330..49b5f69 100644 --- a/src/agent_assistant.py +++ b/src/agent_assistant.py @@ -212,7 +212,7 @@ class TSPAgentAssistant: try: self.is_agent_mode = enabled logger.info(f"Agent模式: {'启用' if enabled else '禁用'}") - return True + return True except Exception as e: logger.error(f"切换Agent模式失败: {e}") return False @@ -233,8 +233,8 @@ class TSPAgentAssistant: """停止主动监控""" try: self.ai_monitoring_active = False - logger.info("主动监控已停止") - return True + logger.info("主动监控已停止") + return True except Exception as e: logger.error(f"停止主动监控失败: {e}") return False @@ -261,14 +261,14 @@ class TSPAgentAssistant: recent_executions = self.get_action_history(20) # 生成分析报告 - analysis = { + analysis = { "tool_performance": tool_performance, "recent_activity": len(recent_executions), "success_rate": tool_performance.get("success_rate", 0), "recommendations": self._generate_recommendations(tool_performance) - } - - return analysis + } + + return analysis except Exception as e: logger.error(f"运行智能分析失败: {e}") @@ -357,8 +357,8 @@ class TSPAgentAssistant: try: logger.info(f"保存知识条目 {i+1}: {entry.get('question', '')[:50]}...") # 这里应该调用知识库管理器保存 - saved_count += 1 - logger.info(f"知识条目 {i+1} 保存成功") + saved_count += 1 + logger.info(f"知识条目 {i+1} 保存成功") except Exception as save_error: logger.error(f"保存知识条目 {i+1} 时出错: {save_error}") @@ -380,9 +380,9 @@ class TSPAgentAssistant: with open(file_path, 'r', encoding='utf-8') as f: return f.read() elif file_ext == '.pdf': - return "PDF文件需要安装PyPDF2库" + return "PDF文件需要安装PyPDF2库" elif file_ext in ['.doc', '.docx']: - return "Word文件需要安装python-docx库" + return "Word文件需要安装python-docx库" else: return "不支持的文件格式" except Exception as e: diff --git a/src/core/__pycache__/models.cpython-311.pyc b/src/core/__pycache__/models.cpython-311.pyc index 1881618..44aed6f 100644 Binary files a/src/core/__pycache__/models.cpython-311.pyc and b/src/core/__pycache__/models.cpython-311.pyc differ diff --git a/src/core/__pycache__/workorder_permissions.cpython-311.pyc b/src/core/__pycache__/workorder_permissions.cpython-311.pyc new file mode 100644 index 0000000..372dcfa Binary files /dev/null and b/src/core/__pycache__/workorder_permissions.cpython-311.pyc differ diff --git a/src/core/models.py b/src/core/models.py index e3177f8..8565b7b 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -8,7 +8,7 @@ Base = declarative_base() class WorkOrder(Base): """工单模型""" __tablename__ = "work_orders" - + id = Column(Integer, primary_key=True) order_id = Column(String(50), unique=True, nullable=False) title = Column(String(200), nullable=False) @@ -20,13 +20,13 @@ class WorkOrder(Base): updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) resolution = Column(Text) satisfaction_score = Column(Float) - + # 飞书集成字段 feishu_record_id = Column(String(100), unique=True, nullable=True) # 飞书记录ID assignee = Column(String(100), nullable=True) # 负责人 solution = Column(Text, nullable=True) # 解决方案 ai_suggestion = Column(Text, nullable=True) # AI建议 - + # 扩展飞书字段 source = Column(String(50), nullable=True) # 来源(Mail, Telegram bot等) module = Column(String(100), nullable=True) # 模块(local O&M, OTA等) @@ -40,14 +40,23 @@ class WorkOrder(Base): parent_record = Column(String(100), nullable=True) # 父记录 has_updated_same_day = Column(String(50), nullable=True) # 是否同日更新 operating_time = Column(String(100), nullable=True) # 操作时间 - + + # 工单分发和权限管理字段 + assigned_module = Column(String(50), nullable=True) # 分配的模块(TBOX、OTA等) + module_owner = Column(String(100), nullable=True) # 业务接口人/模块负责人 + dispatcher = Column(String(100), nullable=True) # 分发人(运维人员) + dispatch_time = Column(DateTime, nullable=True) # 分发时间 + region = Column(String(50), nullable=True) # 区域(overseas/domestic)- 用于区分海外/国内 + # 关联对话记录 conversations = relationship("Conversation", back_populates="work_order") + # 关联处理过程记录 + process_history = relationship("WorkOrderProcessHistory", back_populates="work_order", order_by="WorkOrderProcessHistory.process_time") class Conversation(Base): """对话记录模型""" __tablename__ = "conversations" - + id = Column(Integer, primary_key=True) work_order_id = Column(Integer, ForeignKey("work_orders.id")) user_message = Column(Text, nullable=False) @@ -56,13 +65,13 @@ class Conversation(Base): confidence_score = Column(Float) knowledge_used = Column(Text) # 使用的知识库条目 response_time = Column(Float) # 响应时间(秒) - + work_order = relationship("WorkOrder", back_populates="conversations") class KnowledgeEntry(Base): """知识库条目模型""" __tablename__ = "knowledge_entries" - + id = Column(Integer, primary_key=True) question = Column(Text, nullable=False) answer = Column(Text, nullable=False) @@ -80,7 +89,7 @@ class KnowledgeEntry(Base): class VehicleData(Base): """车辆实时数据模型""" __tablename__ = "vehicle_data" - + id = Column(Integer, primary_key=True) vehicle_id = Column(String(50), nullable=False) # 车辆ID vehicle_vin = Column(String(17)) # 车架号 @@ -88,7 +97,7 @@ class VehicleData(Base): data_value = Column(Text, nullable=False) # 数据值(JSON格式) timestamp = Column(DateTime, default=datetime.now) # 数据时间戳 is_active = Column(Boolean, default=True) # 是否有效 - + # 索引 __table_args__ = ( {'extend_existing': True} @@ -97,7 +106,7 @@ class VehicleData(Base): class Analytics(Base): """分析统计模型""" __tablename__ = "analytics" - + id = Column(Integer, primary_key=True) date = Column(DateTime, nullable=False) total_orders = Column(Integer, default=0) @@ -111,7 +120,7 @@ class Analytics(Base): class Alert(Base): """预警模型""" __tablename__ = "alerts" - + id = Column(Integer, primary_key=True) rule_name = Column(String(100), nullable=False) alert_type = Column(String(50), nullable=False) @@ -126,7 +135,7 @@ class Alert(Base): class WorkOrderSuggestion(Base): """工单AI建议与人工描述表""" __tablename__ = "work_order_suggestions" - + id = Column(Integer, primary_key=True) work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False) ai_suggestion = Column(Text) @@ -136,3 +145,31 @@ class WorkOrderSuggestion(Base): use_human_resolution = Column(Boolean, default=False) # 是否使用人工描述入库 created_at = Column(DateTime, default=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + +class WorkOrderProcessHistory(Base): + """工单处理过程记录表""" + __tablename__ = "work_order_process_history" + + id = Column(Integer, primary_key=True) + work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False) + + # 处理人员信息 + processor_name = Column(String(100), nullable=False) # 处理人员姓名 + processor_role = Column(String(50), nullable=True) # 处理人员角色(运维、业务方等) + processor_region = Column(String(50), nullable=True) # 处理人员区域(overseas/domestic) + + # 处理内容 + process_content = Column(Text, nullable=False) # 处理内容/操作描述 + action_type = Column(String(50), nullable=False) # 操作类型(dispatch、process、close、reassign等) + + # 处理结果 + previous_status = Column(String(50), nullable=True) # 处理前的状态 + new_status = Column(String(50), nullable=True) # 处理后的状态 + assigned_module = Column(String(50), nullable=True) # 分配的模块(如果是分发操作) + + # 时间戳 + process_time = Column(DateTime, default=datetime.now, nullable=False) # 处理时间 + created_at = Column(DateTime, default=datetime.now) + + # 关联工单 + work_order = relationship("WorkOrder", back_populates="process_history") diff --git a/src/core/workorder_permissions.py b/src/core/workorder_permissions.py new file mode 100644 index 0000000..bbea54d --- /dev/null +++ b/src/core/workorder_permissions.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +""" +工单权限管理模块 +实现基于角色的访问控制(RBAC)和工单分发流程 +""" + +import logging +from typing import List, Dict, Optional, Set +from enum import Enum + +logger = logging.getLogger(__name__) + +class UserRole(Enum): + """用户角色枚举""" + # 属地运维(海外/国内) + OVERSEAS_OPS = "overseas_ops" # 海外属地运维 + DOMESTIC_OPS = "domestic_ops" # 国内属地运维 + + # 业务方接口人(各模块负责人) + TBOX_OWNER = "tbox_owner" # TBOX模块负责人 + OTA_OWNER = "ota_owner" # OTA模块负责人 + DMC_OWNER = "dmc_owner" # DMC模块负责人 + MES_OWNER = "mes_owner" # MES模块负责人 + APP_OWNER = "app_owner" # APP模块负责人 + PKI_OWNER = "pki_owner" # PKI模块负责人 + TSP_OWNER = "tsp_owner" # TSP模块负责人 + + # 系统角色 + ADMIN = "admin" # 系统管理员 + VIEWER = "viewer" # 只读用户 + +class WorkOrderModule(Enum): + """工单模块枚举""" + TBOX = "TBOX" + OTA = "OTA" + DMC = "DMC" + MES = "MES" + APP = "APP" + PKI = "PKI" + TSP = "TSP" + LOCAL_OPS = "local_ops" # 属地运维处理 + UNASSIGNED = "unassigned" # 未分配 + +class WorkOrderStatus: + """工单状态常量""" + PENDING = "pending" # 待处理 + ASSIGNED = "assigned" # 已分配 + IN_PROGRESS = "in_progress" # 处理中 + RESOLVED = "resolved" # 已解决 + CLOSED = "closed" # 已关闭 + +class WorkOrderPermissionManager: + """工单权限管理器""" + + # 所有模块集合(供属地运维和管理员使用) + ALL_MODULES = { + WorkOrderModule.TBOX, WorkOrderModule.OTA, WorkOrderModule.DMC, + WorkOrderModule.MES, WorkOrderModule.APP, WorkOrderModule.PKI, + WorkOrderModule.TSP, WorkOrderModule.LOCAL_OPS + } + + # 角色到模块的映射 + ROLE_MODULE_MAP = { + UserRole.TBOX_OWNER: {WorkOrderModule.TBOX}, + UserRole.OTA_OWNER: {WorkOrderModule.OTA}, + UserRole.DMC_OWNER: {WorkOrderModule.DMC}, + UserRole.MES_OWNER: {WorkOrderModule.MES}, + UserRole.APP_OWNER: {WorkOrderModule.APP}, + UserRole.PKI_OWNER: {WorkOrderModule.PKI}, + UserRole.TSP_OWNER: {WorkOrderModule.TSP}, + UserRole.OVERSEAS_OPS: ALL_MODULES, # 可访问所有模块 + UserRole.DOMESTIC_OPS: ALL_MODULES, # 可访问所有模块 + UserRole.ADMIN: ALL_MODULES, # 管理员可访问所有 + UserRole.VIEWER: set(), # 只读,由其他逻辑控制 + } + + @staticmethod + def can_view_all_workorders(role: UserRole) -> bool: + """判断角色是否可以查看所有工单(属地运维和管理员)""" + return role in [UserRole.OVERSEAS_OPS, UserRole.DOMESTIC_OPS, UserRole.ADMIN] + + @staticmethod + def get_accessible_modules(role: UserRole) -> Set[WorkOrderModule]: + """获取角色可访问的模块列表""" + return WorkOrderPermissionManager.ROLE_MODULE_MAP.get(role, set()) + + @staticmethod + def can_access_module(role: UserRole, module: WorkOrderModule) -> bool: + """判断角色是否可以访问指定模块""" + accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role) + + # 属地运维和管理员可以访问所有模块 + if WorkOrderPermissionManager.can_view_all_workorders(role): + return True + + # 业务方只能访问自己的模块 + return module in accessible_modules + + @staticmethod + def can_dispatch_workorder(role: UserRole) -> bool: + """判断角色是否可以进行工单分发(属地运维和管理员)""" + return role in [UserRole.OVERSEAS_OPS, UserRole.DOMESTIC_OPS, UserRole.ADMIN] + + @staticmethod + def can_update_workorder(role: UserRole, workorder_module: Optional[WorkOrderModule], + assigned_to_module: Optional[WorkOrderModule]) -> bool: + """判断角色是否可以更新工单""" + # 管理员和属地运维可以更新所有工单 + if WorkOrderPermissionManager.can_view_all_workorders(role): + return True + + # 业务方只能更新分配给自己的模块的工单 + if workorder_module and assigned_to_module: + accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role) + return workorder_module in accessible_modules and workorder_module == assigned_to_module + + return False + + @staticmethod + def filter_workorders_by_permission(role: UserRole, workorders: List[Dict]) -> List[Dict]: + """根据权限过滤工单列表""" + if WorkOrderPermissionManager.can_view_all_workorders(role): + # 属地运维和管理员可以看到所有工单 + return workorders + + # 业务方只能看到自己模块的工单 + accessible_modules = WorkOrderPermissionManager.get_accessible_modules(role) + filtered = [] + + for wo in workorders: + module_str = wo.get("module") or wo.get("assigned_module") + if module_str: + try: + module = WorkOrderModule(module_str) + if module in accessible_modules: + filtered.append(wo) + except ValueError: + # 如果模块值不在枚举中,跳过 + continue + else: + # 未分配的工单,业务方看不到 + pass + + return filtered + +class WorkOrderDispatchManager: + """工单分发管理器""" + + # 模块到业务接口人的映射(可以动态配置) + MODULE_OWNER_MAP = { + WorkOrderModule.TBOX: "TBOX业务接口人", + WorkOrderModule.OTA: "OTA业务接口人", + WorkOrderModule.DMC: "DMC业务接口人", + WorkOrderModule.MES: "MES业务接口人", + WorkOrderModule.APP: "APP业务接口人", + WorkOrderModule.PKI: "PKI业务接口人", + WorkOrderModule.TSP: "TSP业务接口人", + } + + @staticmethod + def get_module_owner(module: WorkOrderModule) -> str: + """获取模块的业务接口人""" + return WorkOrderDispatchManager.MODULE_OWNER_MAP.get(module, "未指定") + + @staticmethod + def dispatch_workorder(workorder_id: int, target_module: WorkOrderModule, + dispatcher_role: UserRole, dispatcher_name: str) -> Dict: + """ + 分发工单到指定模块 + + Args: + workorder_id: 工单ID + target_module: 目标模块 + dispatcher_role: 分发者角色(必须是运维或管理员) + dispatcher_name: 分发者姓名 + + Returns: + 分发结果 + """ + # 检查分发权限 + if not WorkOrderPermissionManager.can_dispatch_workorder(dispatcher_role): + return { + "success": False, + "error": "无权进行工单分发,只有属地运维和管理员可以分发工单" + } + + # 获取模块负责人 + module_owner = WorkOrderDispatchManager.get_module_owner(target_module) + + # 这里应该更新数据库中的工单信息 + # 实际实现时需要调用数据库更新逻辑 + return { + "success": True, + "message": f"工单已分发到{target_module.value}模块", + "assigned_module": target_module.value, + "module_owner": module_owner, + "dispatcher": dispatcher_name, + "dispatcher_role": dispatcher_role.value + } + + @staticmethod + def suggest_module(description: str, title: str = "") -> Optional[WorkOrderModule]: + """ + 根据工单描述建议分配模块(可以使用AI分析) + + Args: + description: 工单描述 + title: 工单标题 + + Returns: + 建议的模块 + """ + # 简单的关键词匹配(实际可以使用AI分析) + text = (title + " " + description).lower() + + keyword_module_map = { + WorkOrderModule.TBOX: ["tbox", "telematics", "车载", "车联网"], + WorkOrderModule.OTA: ["ota", "over-the-air", "升级", "update"], + WorkOrderModule.DMC: ["dmc", "device management", "设备管理"], + WorkOrderModule.MES: ["mes", "manufacturing", "制造"], + WorkOrderModule.APP: ["app", "application", "应用", "remote control"], + WorkOrderModule.PKI: ["pki", "certificate", "证书"], + WorkOrderModule.TSP: ["tsp", "service", "服务"], + } + + for module, keywords in keyword_module_map.items(): + for keyword in keywords: + if keyword in text: + return module + + return WorkOrderModule.UNASSIGNED diff --git a/src/integrations/__pycache__/ai_suggestion_service.cpython-311.pyc b/src/integrations/__pycache__/ai_suggestion_service.cpython-311.pyc index c647f3f..01a7555 100644 Binary files a/src/integrations/__pycache__/ai_suggestion_service.cpython-311.pyc and b/src/integrations/__pycache__/ai_suggestion_service.cpython-311.pyc differ diff --git a/src/integrations/__pycache__/workorder_sync.cpython-311.pyc b/src/integrations/__pycache__/workorder_sync.cpython-311.pyc index 7c08544..cc71659 100644 Binary files a/src/integrations/__pycache__/workorder_sync.cpython-311.pyc and b/src/integrations/__pycache__/workorder_sync.cpython-311.pyc differ diff --git a/src/integrations/ai_suggestion_service.py b/src/integrations/ai_suggestion_service.py index c53b8e0..ca75e8a 100644 --- a/src/integrations/ai_suggestion_service.py +++ b/src/integrations/ai_suggestion_service.py @@ -23,7 +23,7 @@ class AISuggestionService: self.llm_config = get_config().llm 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) -> str: + def generate_suggestion(self, tr_description: str, process_history: Optional[str] = None, vin: Optional[str] = None, existing_ai_suggestion: Optional[str] = None) -> str: """ 生成AI建议 - 参考处理过程记录生成建议 @@ -31,6 +31,7 @@ class AISuggestionService: tr_description: TR描述 process_history: 处理过程记录(可选,用于了解当前问题状态) vin: 车架号(可选) + existing_ai_suggestion: 现有的AI建议(可选,用于判断是否是首次建议) Returns: AI建议文本 @@ -41,6 +42,11 @@ class AISuggestionService: chat_manager = RealtimeChatManager() + # 判断是否是首次建议(通过检查现有AI建议) + is_first_suggestion = True + if existing_ai_suggestion and existing_ai_suggestion.strip(): + is_first_suggestion = False + # 构建上下文信息 context_info = "" if process_history and process_history.strip(): @@ -49,17 +55,29 @@ class AISuggestionService: 已处理的步骤: {process_history}""" + # 根据是否为首次建议,设置不同的提示词 + if is_first_suggestion: + # 首次建议:只给出一般性的排查步骤,不要提进站抓取日志 + suggestion_instruction = """要求: +1. 首次给客户建议,只提供远程可操作的一般性排查步骤 +2. 如检查网络、重启系统、确认配置等常见操作 +3. 绝对不要提到"进站"、"抓取日志"等需要线下操作的内容 +4. 语言简洁精炼,用逗号连接,不要用序号或分行""" + else: + # 后续建议:如果已有处理记录但未解决,可以考虑更深入的方案 + suggestion_instruction = """要求: +1. 基于已有处理步骤,给出下一步的排查建议 +2. 如果远程操作都无法解决,可以考虑更深入的诊断方案 +3. 语言简洁精炼,用逗号连接,不要用序号或分行""" + # 构建用户消息 - 要求生成简洁的简短建议 user_message = f"""请为以下问题提供精炼的技术支持操作建议: 格式要求: -1. 用逗号连接,一句话表达,不要用序号或分行 -2. 现状+步骤,语言精炼 -3. 总长度控制在150字以内 +1. 现状+步骤,语言精炼 +2. 总长度控制在150字以内 -根据问题复杂程度选择结尾: -- 简单问题:给出具体操作步骤即可,不需要提日志分析 -- 复杂问题:如远程操作无法解决,结尾才使用"建议邀请用户进站抓取日志分析" +{suggestion_instruction} 问题描述:{tr_description}{context_info}""" @@ -76,13 +94,13 @@ class AISuggestionService: logger.info(f"AI生成原始内容: {content[:100]}...") # 二次处理:替换默认建议(在清理前先替换) - content = self._post_process_suggestion(content) + content = self._post_process_suggestion(content, is_first_suggestion) # 清理并限制长度 cleaned = self._clean_response(content) # 再次检查,确保替换生效 - cleaned = self._post_process_suggestion(cleaned) + cleaned = self._post_process_suggestion(cleaned, is_first_suggestion) # 记录清理后的内容 logger.info(f"AI建议清理后: {cleaned[:100]}...") @@ -178,12 +196,13 @@ class AISuggestionService: return cleaned - def _post_process_suggestion(self, content: str) -> str: + def _post_process_suggestion(self, content: str, is_first_suggestion: bool = True) -> str: """ 二次处理建议内容:替换默认建议文案 Args: content: 清理后的内容 + is_first_suggestion: 是否是首次建议 Returns: 处理后的内容 @@ -191,22 +210,38 @@ class AISuggestionService: if not content or not content.strip(): return content - # 替换各种形式的"联系售后技术支持"为"邀请用户进站抓取日志分析" - replacements = [ - ("建议联系售后技术支持进一步排查", "建议邀请用户进站抓取日志分析"), - ("联系售后技术支持进行进一步排查", "邀请用户进站抓取日志分析"), - ("建议联系售后技术支持", "建议邀请用户进站抓取日志分析"), - ("联系售后技术支持", "邀请用户进站抓取日志分析"), - ("如问题仍未解决,建议联系售后技术支持进行进一步排查", "如问题仍未解决,建议邀请用户进站抓取日志分析"), - ("若仍无效,建议联系售后技术支持进一步排查", "若仍无效,建议邀请用户进站抓取日志分析"), - ("仍无效,建议联系售后技术支持", "仍无效,建议邀请用户进站抓取日志分析"), - ] - result = content - for old_text, new_text in replacements: - if old_text in result: - result = result.replace(old_text, new_text) - logger.info(f"✓ 替换建议文案: '{old_text}' -> '{new_text}'") + + # 如果是首次建议,移除所有"进站"、"抓取日志"相关的内容 + if is_first_suggestion: + # 移除进站相关的文案 + station_keywords = [ + "进站", "抓取日志", "邀请用户进站", "建议邀请用户进站", + "建议进站", "需要进站", "前往服务站", "联系售后", "售后技术支持" + ] + for keyword in station_keywords: + if keyword in result: + # 找到包含关键词的句子并移除 + lines = result.split(',') + new_lines = [line for line in lines if keyword not in line] + result = ','.join(new_lines) + logger.info(f"首次建议,移除包含'{keyword}'的内容") + else: + # 非首次建议:替换"联系售后技术支持"为"邀请用户进站抓取日志分析" + replacements = [ + ("建议联系售后技术支持进一步排查", "建议邀请用户进站抓取日志分析"), + ("联系售后技术支持进行进一步排查", "邀请用户进站抓取日志分析"), + ("建议联系售后技术支持", "建议邀请用户进站抓取日志分析"), + ("联系售后技术支持", "邀请用户进站抓取日志分析"), + ("如问题仍未解决,建议联系售后技术支持进行进一步排查", "如问题仍未解决,建议邀请用户进站抓取日志分析"), + ("若仍无效,建议联系售后技术支持进一步排查", "若仍无效,建议邀请用户进站抓取日志分析"), + ("仍无效,建议联系售后技术支持", "仍无效,建议邀请用户进站抓取日志分析"), + ] + + for old_text, new_text in replacements: + if old_text in result: + result = result.replace(old_text, new_text) + logger.info(f"✓ 替换建议文案: '{old_text}' -> '{new_text}'") # 如果没有任何替换,记录一下 if result == content: @@ -342,7 +377,7 @@ class AISuggestionService: logger.info(f"记录 {record.get('record_id', i)} - 现有AI建议前100字符: {existing_ai_suggestion[:100]}") if tr_description: - ai_suggestion = self.generate_suggestion(tr_description, process_history, vin) + ai_suggestion = self.generate_suggestion(tr_description, process_history, vin, existing_ai_suggestion) # 处理同一天多次更新的情况 new_suggestion = self._format_ai_suggestion_with_numbering( time_str, ai_suggestion, existing_ai_suggestion diff --git a/src/integrations/workorder_sync.py b/src/integrations/workorder_sync.py index 306d3c6..b69c392 100644 --- a/src/integrations/workorder_sync.py +++ b/src/integrations/workorder_sync.py @@ -53,13 +53,13 @@ class WorkOrderSyncService: self.field_mapping = { # 核心字段 "TR Number": "order_id", - "TR Description": "description", + "TR Description": "description", # 问题描述 "Type of problem": "category", "TR Level": "priority", "TR Status": "status", "Source": "source", "Date creation": "created_at", - "处理过程": "solution", + "处理过程": "resolution", # 处理过程历史记录(存储完整历史到resolution字段) "TR tracking": "resolution", # 扩展字段 @@ -194,18 +194,28 @@ class WorkOrderSyncService: workorder_data = self._convert_feishu_to_local(parsed_fields) workorder_data["feishu_record_id"] = feishu_id + # 过滤掉WorkOrder模型不支持的字段(防止dict参数错误) + valid_fields = {} + for key, value in workorder_data.items(): + if hasattr(WorkOrder, key): + # 确保值不是dict、list等复杂类型 + if isinstance(value, (dict, list)): + logger.warning(f"字段 '{key}' 包含复杂类型 {type(value).__name__},跳过") + continue + valid_fields[key] = value + if existing_workorder: # 更新现有记录 - for key, value in workorder_data.items(): + for key, value in valid_fields.items(): if key != "feishu_record_id": setattr(existing_workorder, key, value) existing_workorder.updated_at = datetime.now() updated_count += 1 else: # 创建新记录 - workorder_data["created_at"] = datetime.now() - workorder_data["updated_at"] = datetime.now() - new_workorder = WorkOrder(**workorder_data) + valid_fields["created_at"] = datetime.now() + valid_fields["updated_at"] = datetime.now() + new_workorder = WorkOrder(**valid_fields) session.add(new_workorder) created_count += 1 @@ -337,21 +347,17 @@ class WorkOrderSyncService: """创建新工单""" try: with db_manager.get_session() as session: - workorder = WorkOrder( - order_id=local_data.get("order_id"), - title=local_data.get("title"), - description=local_data.get("description"), - category=local_data.get("category"), - priority=local_data.get("priority"), - status=local_data.get("status"), - created_at=local_data.get("created_at"), - updated_at=local_data.get("updated_at"), - resolution=local_data.get("solution"), - feishu_record_id=local_data.get("feishu_record_id"), - assignee=local_data.get("assignee"), - solution=local_data.get("solution"), - ai_suggestion=local_data.get("ai_suggestion") - ) + # 只使用WorkOrder模型支持的字段 + valid_data = {} + for key, value in local_data.items(): + if hasattr(WorkOrder, key): + # 确保值不是dict、list等复杂类型 + if isinstance(value, (dict, list)): + logger.warning(f"字段 '{key}' 包含复杂类型 {type(value).__name__},跳过") + continue + valid_data[key] = value + + workorder = WorkOrder(**valid_data) session.add(workorder) session.commit() session.refresh(workorder) @@ -432,15 +438,38 @@ class WorkOrderSyncService: logger.warning(f"时间字段转换失败: {e}, 使用当前时间") local_data[local_field] = datetime.now() - # 生成标题 - tr_number = feishu_fields.get("TR Number", "") - problem_type = feishu_fields.get("Type of problem", "") - if tr_number and problem_type: - local_data["title"] = f"{tr_number} - {problem_type}" - elif tr_number: - local_data["title"] = f"{tr_number} - TR工单" + # 生成标题:使用TR Description作为标题 + tr_description = feishu_fields.get("TR Description", "") + if tr_description: + # 标题直接使用问题描述,如果太长则截断 + if len(tr_description) > 200: + local_data["title"] = tr_description[:197] + "..." + else: + local_data["title"] = tr_description else: - local_data["title"] = "TR工单" + # 如果没有描述,使用TR Number + tr_number = feishu_fields.get("TR Number", "") + if tr_number: + local_data["title"] = f"{tr_number} - TR工单" + else: + local_data["title"] = "TR工单" + + # 处理"处理过程"字段:提取最新一条作为solution + # "处理过程"字段已映射到resolution,这里需要: + # 1. resolution存储完整的"处理过程"历史 + # 2. solution存储"处理过程"的最新一条 + process_history = local_data.get("resolution", "") + if process_history and isinstance(process_history, str): + # 按换行分割,获取最后一行(最新一条) + process_lines = [line.strip() for line in process_history.split('\n') if line.strip()] + if process_lines: + # 最新一条作为solution + local_data["solution"] = process_lines[-1] + # 完整历史保留在resolution(已在字段映射中设置) + else: + local_data["solution"] = "" + else: + local_data["solution"] = "" # 设置默认值 if "status" not in local_data: diff --git a/src/utils/__pycache__/semantic_similarity.cpython-311.pyc b/src/utils/__pycache__/semantic_similarity.cpython-311.pyc new file mode 100644 index 0000000..7ad4cdd Binary files /dev/null and b/src/utils/__pycache__/semantic_similarity.cpython-311.pyc differ diff --git a/src/utils/semantic_similarity.py b/src/utils/semantic_similarity.py index d9a0588..4df6736 100644 --- a/src/utils/semantic_similarity.py +++ b/src/utils/semantic_similarity.py @@ -3,43 +3,59 @@ """ 语义相似度计算服务 -使用sentence-transformers进行更准确的语义相似度计算 +使用LLM API进行更准确的语义相似度计算,提高理解力并节约服务端资源 """ import logging import numpy as np +import re from typing import List, Tuple, Optional from sentence_transformers import SentenceTransformer -import torch logger = logging.getLogger(__name__) class SemanticSimilarityCalculator: - """语义相似度计算器""" + """语义相似度计算器 - 使用LLM API""" - def __init__(self, model_name: str = "all-MiniLM-L6-v2"): + def __init__(self, use_llm: bool = True): """ 初始化语义相似度计算器 Args: - model_name: 使用的预训练模型名称 - - all-MiniLM-L6-v2: 英文模型,速度快,推荐用于生产环境 - - paraphrase-multilingual-MiniLM-L12-v2: 多语言模型,支持中文 - - paraphrase-multilingual-mpnet-base-v2: 多语言模型,精度高 + use_llm: 是否使用LLM API计算相似度(默认True,推荐) + - True: 使用LLM API,理解力更强,无需加载本地模型 + - False: 使用本地模型(需要下载HuggingFace模型) """ - self.model_name = model_name + self.use_llm = use_llm self.model = None - self._load_model() + self.llm_client = None + + if use_llm: + self._init_llm_client() + else: + self._load_model() + + def _init_llm_client(self): + """初始化LLM客户端""" + try: + from ..core.llm_client import QwenClient + self.llm_client = QwenClient() + logger.info("LLM客户端初始化成功,将使用LLM API计算语义相似度") + except Exception as e: + logger.error(f"初始化LLM客户端失败: {e}") + self.llm_client = None + # 回退到本地模型 + self.use_llm = False + self._load_model() def _load_model(self): - """加载预训练模型""" + """加载预训练模型(仅在use_llm=False时使用)""" try: - logger.info(f"正在加载语义相似度模型: {self.model_name}") - self.model = SentenceTransformer(self.model_name) - logger.info("语义相似度模型加载成功") + logger.info(f"正在加载本地语义相似度模型: all-MiniLM-L6-v2") + self.model = SentenceTransformer("all-MiniLM-L6-v2") + logger.info("本地语义相似度模型加载成功") except Exception as e: - logger.error(f"加载语义相似度模型失败: {e}") - # 回退到简单模型 + logger.error(f"加载本地语义相似度模型失败: {e}") self.model = None def calculate_similarity(self, text1: str, text2: str, fast_mode: bool = True) -> float: @@ -49,7 +65,7 @@ class SemanticSimilarityCalculator: Args: text1: 第一个文本 text2: 第二个文本 - fast_mode: 是否使用快速模式(结合传统方法) + fast_mode: 是否使用快速模式(仅在使用本地模型时有效) Returns: 相似度分数 (0-1之间) @@ -58,27 +74,22 @@ class SemanticSimilarityCalculator: return 0.0 try: - # 快速模式:先使用传统方法快速筛选 - if fast_mode: - tfidf_sim = self._calculate_tfidf_similarity(text1, text2) - - # 如果传统方法相似度很高或很低,直接返回 - if tfidf_sim >= 0.9: - return tfidf_sim - elif tfidf_sim <= 0.3: - return tfidf_sim - - # 中等相似度时,使用语义方法进行精确计算 - if self.model is not None: + # 优先使用LLM API计算相似度 + if self.use_llm and self.llm_client: + return self._calculate_llm_similarity(text1, text2) + + # 回退到本地模型或TF-IDF + if self.model is not None: + if fast_mode: + # 快速模式:先使用TF-IDF快速筛选 + tfidf_sim = self._calculate_tfidf_similarity(text1, text2) + if tfidf_sim >= 0.9 or tfidf_sim <= 0.3: + return tfidf_sim + # 中等相似度时,使用语义方法进行精确计算 semantic_sim = self._calculate_semantic_similarity(text1, text2) - # 结合两种方法的结果 return (tfidf_sim * 0.3 + semantic_sim * 0.7) else: - return tfidf_sim - - # 完整模式:直接使用语义相似度 - if self.model is not None: - return self._calculate_semantic_similarity(text1, text2) + return self._calculate_semantic_similarity(text1, text2) else: return self._calculate_tfidf_similarity(text1, text2) @@ -86,6 +97,80 @@ class SemanticSimilarityCalculator: logger.error(f"计算语义相似度失败: {e}") return self._calculate_tfidf_similarity(text1, text2) + def _calculate_llm_similarity(self, text1: str, text2: str) -> float: + """使用LLM API计算语义相似度""" + try: + # 构建prompt,让LLM比较两个文本的相似度 + prompt = f"""请比较以下两个文本的语义相似度,并给出0-1之间的分数(保留2位小数),其中: +- 1.0 表示完全相同 +- 0.8-0.9 表示非常相似 +- 0.6-0.7 表示较为相似 +- 0.4-0.5 表示部分相似 +- 0.0-0.3 表示差异很大 + +文本1: {text1} + +文本2: {text2} + +请只返回0-1之间的数字(保留2位小数),不要包含其他文字。例如:0.85""" + + messages = [ + {"role": "system", "content": "你是一个专业的文本相似度评估专家,请准确评估两个文本的语义相似度。"}, + {"role": "user", "content": prompt} + ] + + result = self.llm_client.chat_completion( + messages=messages, + temperature=0.1, # 低温度以获得更稳定的结果 + max_tokens=50 + ) + + if "error" in result: + logger.error(f"LLM API调用失败: {result['error']}") + # 回退到TF-IDF + return self._calculate_tfidf_similarity(text1, text2) + + # 提取响应中的数字 + response_content = result.get("choices", [{}])[0].get("message", {}).get("content", "") + similarity = self._extract_similarity_from_response(response_content) + + logger.debug(f"LLM计算语义相似度: {similarity:.4f}") + return similarity + + except Exception as e: + logger.error(f"LLM语义相似度计算失败: {e}") + # 回退到TF-IDF + return self._calculate_tfidf_similarity(text1, text2) + + def _extract_similarity_from_response(self, response: str) -> float: + """从LLM响应中提取相似度分数""" + try: + # 尝试提取0-1之间的浮点数 + patterns = [ + r'(\d+\.\d{1,2})', # 匹配两位小数的浮点数 + r'(\d+\.\d+)', # 匹配任意小数的浮点数 + r'(\d+)' # 匹配整数(可能是百分比形式) + ] + + for pattern in patterns: + matches = re.findall(pattern, response) + if matches: + value = float(matches[0]) + # 如果值大于1,可能是百分比形式,需要除以100 + if value > 1: + value = value / 100.0 + # 确保在0-1范围内 + value = max(0.0, min(1.0, value)) + return value + + # 如果没有找到数字,返回默认值 + logger.warning(f"无法从响应中提取相似度分数: {response}") + return 0.5 + + except Exception as e: + logger.error(f"提取相似度分数失败: {e}, 响应: {response}") + return 0.5 + def _calculate_semantic_similarity(self, text1: str, text2: str) -> float: """使用sentence-transformers计算语义相似度""" try: @@ -159,6 +244,11 @@ class SemanticSimilarityCalculator: return [] try: + # 优先使用LLM API + if self.use_llm and self.llm_client: + return [self._calculate_llm_similarity(t1, t2) for t1, t2 in text_pairs] + + # 回退到本地模型或TF-IDF if self.model is not None: return self._batch_semantic_similarity(text_pairs) else: @@ -214,17 +304,24 @@ class SemanticSimilarityCalculator: return "语义差异较大,建议重新生成" def is_model_available(self) -> bool: - """检查模型是否可用""" - return self.model is not None + """检查模型是否可用(LLM或本地模型)""" + if self.use_llm: + return self.llm_client is not None + else: + return self.model is not None # 全局实例 _similarity_calculator = None -def get_similarity_calculator() -> SemanticSimilarityCalculator: - """获取全局相似度计算器实例""" +def get_similarity_calculator(use_llm: bool = True) -> SemanticSimilarityCalculator: + """获取全局相似度计算器实例 + + Args: + use_llm: 是否使用LLM API(默认True,推荐) + """ global _similarity_calculator if _similarity_calculator is None: - _similarity_calculator = SemanticSimilarityCalculator() + _similarity_calculator = SemanticSimilarityCalculator(use_llm=use_llm) return _similarity_calculator def calculate_semantic_similarity(text1: str, text2: str, fast_mode: bool = True) -> float: diff --git a/src/web/__pycache__/app.cpython-311.pyc b/src/web/__pycache__/app.cpython-311.pyc index 6dc7c33..c9896b4 100644 Binary files a/src/web/__pycache__/app.cpython-311.pyc and b/src/web/__pycache__/app.cpython-311.pyc differ diff --git a/src/web/__pycache__/websocket_server.cpython-311.pyc b/src/web/__pycache__/websocket_server.cpython-311.pyc index 0f6925c..d9c646f 100644 Binary files a/src/web/__pycache__/websocket_server.cpython-311.pyc and b/src/web/__pycache__/websocket_server.cpython-311.pyc differ diff --git a/src/web/app.py b/src/web/app.py index b00cf24..a371851 100644 --- a/src/web/app.py +++ b/src/web/app.py @@ -115,9 +115,9 @@ def create_chat_session(): data = request.get_json() user_id = data.get('user_id', 'anonymous') work_order_id = data.get('work_order_id') - + session_id = service_manager.get_chat_manager().create_session(user_id, work_order_id) - + return jsonify({ "success": True, "session_id": session_id, @@ -133,10 +133,10 @@ def send_chat_message(): data = request.get_json() session_id = data.get('session_id') message = data.get('message') - + if not session_id or not message: return jsonify({"error": "缺少必要参数"}), 400 - + result = service_manager.get_chat_manager().process_message(session_id, message) return jsonify(result) except Exception as e: @@ -164,10 +164,10 @@ def create_work_order(): description = data.get('description') category = data.get('category', '技术问题') priority = data.get('priority', 'medium') - + if not session_id or not title or not description: return jsonify({"error": "缺少必要参数"}), 400 - + result = service_manager.get_chat_manager().create_work_order(session_id, title, description, category, priority) return jsonify(result) except Exception as e: @@ -281,7 +281,7 @@ def toggle_agent_mode(): enabled = data.get('enabled', True) success = service_manager.get_agent_assistant().toggle_agent_mode(enabled) return jsonify({ - "success": success, + "success": success, "message": f"Agent模式已{'启用' if enabled else '禁用'}" }) except Exception as e: @@ -293,7 +293,7 @@ def start_agent_monitoring(): try: success = service_manager.get_agent_assistant().start_proactive_monitoring() return jsonify({ - "success": success, + "success": success, "message": "Agent监控已启动" if success else "启动失败" }) except Exception as e: @@ -305,7 +305,7 @@ def stop_agent_monitoring(): try: success = service_manager.get_agent_assistant().stop_proactive_monitoring() return jsonify({ - "success": success, + "success": success, "message": "Agent监控已停止" if success else "停止失败" }) except Exception as e: @@ -336,13 +336,13 @@ def agent_chat(): 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( @@ -351,7 +351,7 @@ def agent_chat(): work_order_id=None, enable_proactive=True )) - + return jsonify({ "success": True, "response": result.get('response', 'Agent已处理您的请求'), @@ -440,18 +440,18 @@ 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) @@ -461,15 +461,15 @@ def export_analytics(): 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 @@ -484,7 +484,7 @@ def get_vehicle_data(): 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) @@ -492,7 +492,7 @@ def get_vehicle_data(): 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 @@ -560,10 +560,10 @@ def test_api_connection(): 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}", @@ -579,7 +579,7 @@ def test_model_response(): try: data = request.get_json() test_message = data.get('test_message', '你好,请简单介绍一下你自己') - + # 这里可以调用LLM客户端进行回答测试 # 暂时返回模拟结果 return jsonify({ diff --git a/src/web/blueprints/__pycache__/auth.cpython-311.pyc b/src/web/blueprints/__pycache__/auth.cpython-311.pyc new file mode 100644 index 0000000..ed657b1 Binary files /dev/null and b/src/web/blueprints/__pycache__/auth.cpython-311.pyc differ diff --git a/src/web/blueprints/__pycache__/workorders.cpython-311.pyc b/src/web/blueprints/__pycache__/workorders.cpython-311.pyc index 70eaa09..65642a9 100644 Binary files a/src/web/blueprints/__pycache__/workorders.cpython-311.pyc and b/src/web/blueprints/__pycache__/workorders.cpython-311.pyc differ diff --git a/src/web/blueprints/workorders.py b/src/web/blueprints/workorders.py index 350fe7f..077c776 100644 --- a/src/web/blueprints/workorders.py +++ b/src/web/blueprints/workorders.py @@ -10,6 +10,7 @@ import logging import uuid import time from datetime import datetime +from typing import Optional from flask import Blueprint, request, jsonify, send_file from werkzeug.utils import secure_filename from sqlalchemy import text @@ -25,13 +26,13 @@ class SimpleAIAccuracyConfig: self.manual_review_threshold = 0.80 self.ai_suggestion_confidence = 0.95 self.human_resolution_confidence = 0.90 - + def should_auto_approve(self, similarity: float) -> bool: return similarity >= self.auto_approve_threshold - + def should_use_human_resolution(self, similarity: float) -> bool: return similarity < self.use_human_resolution_threshold - + def get_confidence_score(self, similarity: float, use_human: bool = False) -> float: if use_human: return self.human_resolution_confidence @@ -40,12 +41,83 @@ class SimpleAIAccuracyConfig: from src.main import TSPAssistant from src.core.database import db_manager -from src.core.models import WorkOrder, Conversation, WorkOrderSuggestion, KnowledgeEntry +from src.core.models import WorkOrder, Conversation, WorkOrderSuggestion, KnowledgeEntry, WorkOrderProcessHistory from src.core.query_optimizer import query_optimizer from src.web.service_manager import service_manager +from src.core.workorder_permissions import ( + WorkOrderPermissionManager, WorkOrderDispatchManager, + UserRole, WorkOrderModule +) workorders_bp = Blueprint('workorders', __name__, url_prefix='/api/workorders') +def get_current_user_role() -> UserRole: + """获取当前用户角色(临时实现,实际需要集成认证系统)""" + # TODO: 从session或token中获取用户信息 + # 在没有认证系统之前,默认返回ADMIN以便可以查看所有工单 + # 实际实现时需要从认证系统获取真实角色 + role_str = request.headers.get('X-User-Role', 'admin') # 临时改为admin,避免VIEWER无法查看数据 + try: + return UserRole(role_str) + except ValueError: + return UserRole.ADMIN # 临时返回ADMIN,避免VIEWER无法查看数据 + +def get_current_user_name() -> str: + """获取当前用户名(临时实现,实际需要集成认证系统)""" + # TODO: 从session或token中获取用户信息 + return request.headers.get('X-User-Name', 'anonymous') + +def add_process_history( + workorder_id: int, + processor_name: str, + process_content: str, + action_type: str, + processor_role: Optional[str] = None, + processor_region: Optional[str] = None, + previous_status: Optional[str] = None, + new_status: Optional[str] = None, + assigned_module: Optional[str] = None +) -> WorkOrderProcessHistory: + """ + 添加工单处理过程记录 + + Args: + workorder_id: 工单ID + processor_name: 处理人员姓名 + process_content: 处理内容 + action_type: 操作类型(dispatch、process、close、reassign等) + processor_role: 处理人员角色 + processor_region: 处理人员区域 + previous_status: 处理前的状态 + new_status: 处理后的状态 + assigned_module: 分配的模块 + + Returns: + 创建的处理记录对象 + """ + try: + with db_manager.get_session() as session: + history = WorkOrderProcessHistory( + work_order_id=workorder_id, + processor_name=processor_name, + processor_role=processor_role, + processor_region=processor_region, + process_content=process_content, + action_type=action_type, + previous_status=previous_status, + new_status=new_status, + assigned_module=assigned_module, + process_time=datetime.now() + ) + session.add(history) + session.commit() + session.refresh(history) + logger.info(f"工单 {workorder_id} 添加处理记录: {action_type} by {processor_name}") + return history + except Exception as e: + logger.error(f"添加处理记录失败: {e}") + raise + # 移除get_assistant函数,使用service_manager def _ensure_workorder_template_file() -> str: @@ -53,14 +125,14 @@ def _ensure_workorder_template_file() -> str: # 获取项目根目录 current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.abspath(os.path.join(current_dir, '..', '..', '..')) - + # 模板文件路径(项目根目录下的uploads) template_path = os.path.join(project_root, 'uploads', 'workorder_template.xlsx') - + # 确保目录存在 uploads_dir = os.path.join(project_root, 'uploads') os.makedirs(uploads_dir, exist_ok=True) - + if not os.path.exists(template_path): # 尝试从其他可能的位置复制模板 possible_locations = [ @@ -68,7 +140,7 @@ def _ensure_workorder_template_file() -> str: os.path.join(current_dir, 'uploads', 'workorder_template.xlsx'), os.path.join(os.getcwd(), 'uploads', 'workorder_template.xlsx') ] - + source_found = False for source_path in possible_locations: if os.path.exists(source_path): @@ -79,7 +151,7 @@ def _ensure_workorder_template_file() -> str: break except Exception as e: logger.warning(f"复制模板文件失败: {e}") - + if not source_found: # 自动生成一个最小可用模板 try: @@ -91,42 +163,66 @@ def _ensure_workorder_template_file() -> str: logger.info(f"自动生成模板文件: {template_path}") except Exception as gen_err: raise FileNotFoundError('模板文件缺失且自动生成失败,请检查依赖:openpyxl/pandas') from gen_err - + return template_path @workorders_bp.route('') def get_workorders(): - """获取工单列表(分页)""" + """获取工单列表(分页,带权限过滤)""" try: + # 获取当前用户角色和权限 + current_role = get_current_user_role() + # 获取分页参数 page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 10, type=int) status_filter = request.args.get('status', '') priority_filter = request.args.get('priority', '') - + module_filter = request.args.get('module', '') # 模块过滤 + # 从数据库获取分页数据 from src.core.database import db_manager from src.core.models import WorkOrder - + with db_manager.get_session() as session: # 构建查询 query = session.query(WorkOrder) - + + # 权限过滤:业务方只能看到自己模块的工单 + if not WorkOrderPermissionManager.can_view_all_workorders(current_role): + # 获取用户可访问的模块 + accessible_modules = WorkOrderPermissionManager.get_accessible_modules(current_role) + if accessible_modules: + # 构建模块列表过滤条件 + module_names = [m.value for m in accessible_modules] + query = query.filter(WorkOrder.assigned_module.in_(module_names)) + else: + # 如果没有可访问的模块,返回空列表 + return jsonify({ + "workorders": [], + "total": 0, + "page": page, + "per_page": per_page, + "total_pages": 0 # 统一使用total_pages字段 + }) + # 应用过滤器 if status_filter: query = query.filter(WorkOrder.status == status_filter) if priority_filter: query = query.filter(WorkOrder.priority == priority_filter) - + if module_filter: + query = query.filter(WorkOrder.assigned_module == module_filter) + # 按创建时间倒序排列 query = query.order_by(WorkOrder.created_at.desc()) - + # 计算总数 total = query.count() - + # 分页查询 workorders = query.offset((page - 1) * per_page).limit(per_page).all() - + # 转换为字典 workorders_data = [] for workorder in workorders: @@ -135,6 +231,11 @@ def get_workorders(): 'order_id': workorder.order_id, 'title': workorder.title, 'description': workorder.description, + 'assigned_module': workorder.assigned_module, + 'module_owner': workorder.module_owner, + 'dispatcher': workorder.dispatcher, + 'dispatch_time': workorder.dispatch_time.isoformat() if workorder.dispatch_time else None, + 'region': workorder.region, 'category': workorder.category, 'priority': workorder.priority, 'status': workorder.status, @@ -146,10 +247,10 @@ def get_workorders(): 'updated_at': workorder.updated_at.isoformat() if workorder.updated_at else None, 'date_of_close': workorder.date_of_close.isoformat() if workorder.date_of_close else None }) - + # 计算分页信息 total_pages = (total + per_page - 1) // per_page - + return jsonify({ 'workorders': workorders_data, 'page': page, @@ -157,13 +258,13 @@ def get_workorders(): 'total': total, 'total_pages': total_pages }) - + except Exception as e: return jsonify({"error": str(e)}), 500 @workorders_bp.route('', methods=['POST']) def create_workorder(): - """创建工单""" + """创建工单(初始状态为待分发)""" try: data = request.get_json() result = service_manager.get_assistant().create_work_order( @@ -172,23 +273,72 @@ def create_workorder(): category=data['category'], priority=data['priority'] ) - + + # 获取当前用户信息(用于记录创建人) + current_user = get_current_user_name() + current_role = get_current_user_role() + + # 创建工单后,设置为待分发状态(未分配模块) + if result and 'id' in result: + workorder_id = result.get('id') + with db_manager.get_session() as session: + workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() + if workorder: + # 初始状态为待分发 + workorder.assigned_module = WorkOrderModule.UNASSIGNED.value + workorder.status = "pending" # 待处理/待分发 + workorder.created_by = current_user # 记录创建人 + session.commit() + + # 记录创建工单的处理历史 + processor_region = "overseas" if current_role == UserRole.OVERSEAS_OPS else "domestic" + add_process_history( + workorder_id=workorder_id, + processor_name=current_user, + process_content=f"工单已创建:{data.get('title', '')[:50]}", + action_type="create", + processor_role=current_role.value, + processor_region=processor_region, + previous_status=None, + new_status="pending" + ) + # 清除工单相关缓存 from src.core.cache_manager import cache_manager cache_manager.clear() # 清除所有缓存 - + return jsonify({"success": True, "workorder": result}) except Exception as e: return jsonify({"error": str(e)}), 500 @workorders_bp.route('/') def get_workorder_details(workorder_id): - """获取工单详情(含数据库对话记录)""" + """获取工单详情(含数据库对话记录,带权限检查)""" try: + # 获取当前用户角色和权限 + current_role = get_current_user_role() + with db_manager.get_session() as session: w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() if not w: return jsonify({"error": "工单不存在"}), 404 + + # 权限检查:业务方只能访问自己模块的工单 + if not WorkOrderPermissionManager.can_view_all_workorders(current_role): + # 检查是否有权限访问该工单 + assigned_module_str = w.assigned_module + if not assigned_module_str or assigned_module_str == WorkOrderModule.UNASSIGNED.value: + # 未分配的工单,业务方不能访问 + return jsonify({"error": "无权访问该工单"}), 403 + + try: + assigned_module = WorkOrderModule(assigned_module_str) + accessible_modules = WorkOrderPermissionManager.get_accessible_modules(current_role) + if assigned_module not in accessible_modules: + return jsonify({"error": "无权访问该工单"}), 403 + except ValueError: + # 如果模块值无效,业务方不能访问 + return jsonify({"error": "无权访问该工单"}), 403 convs = session.query(Conversation).filter(Conversation.work_order_id == w.id).order_by(Conversation.timestamp.asc()).all() conv_list = [] for c in convs: @@ -198,6 +348,27 @@ def get_workorder_details(workorder_id): "assistant_response": c.assistant_response, "timestamp": c.timestamp.isoformat() if c.timestamp else None }) + + # 获取处理过程记录 + process_history_list = session.query(WorkOrderProcessHistory).filter( + WorkOrderProcessHistory.work_order_id == w.id + ).order_by(WorkOrderProcessHistory.process_time.asc()).all() + + process_history_data = [] + for ph in process_history_list: + process_history_data.append({ + "id": ph.id, + "processor_name": ph.processor_name, + "processor_role": ph.processor_role, + "processor_region": ph.processor_region, + "process_content": ph.process_content, + "action_type": ph.action_type, + "previous_status": ph.previous_status, + "new_status": ph.new_status, + "assigned_module": ph.assigned_module, + "process_time": ph.process_time.isoformat() if ph.process_time else None + }) + # 在会话内构建工单数据 workorder = { "id": w.id, @@ -211,7 +382,13 @@ def get_workorder_details(workorder_id): "updated_at": w.updated_at.isoformat() if w.updated_at else None, "resolution": w.resolution, "satisfaction_score": w.satisfaction_score, - "conversations": conv_list + "assigned_module": w.assigned_module, + "module_owner": w.module_owner, + "dispatcher": w.dispatcher, + "dispatch_time": w.dispatch_time.isoformat() if w.dispatch_time else None, + "region": w.region, + "conversations": conv_list, + "process_history": process_history_data # 处理过程记录 } return jsonify(workorder) except Exception as e: @@ -219,29 +396,72 @@ def get_workorder_details(workorder_id): @workorders_bp.route('/', methods=['PUT']) def update_workorder(workorder_id): - """更新工单(写入数据库)""" + """更新工单(写入数据库,自动记录处理历史)""" try: + # 获取当前用户信息 + current_user = get_current_user_name() + current_role = get_current_user_role() + data = request.get_json() if not data.get('title') or not data.get('description'): return jsonify({"error": "标题和描述不能为空"}), 400 + with db_manager.get_session() as session: w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() if not w: return jsonify({"error": "工单不存在"}), 404 + + # 记录更新前的状态 + previous_status = w.status + previous_priority = w.priority + + # 更新工单信息 w.title = data.get('title', w.title) w.description = data.get('description', w.description) w.category = data.get('category', w.category) w.priority = data.get('priority', w.priority) - w.status = data.get('status', w.status) + new_status = data.get('status', w.status) + w.status = new_status w.resolution = data.get('resolution', w.resolution) w.satisfaction_score = data.get('satisfaction_score', w.satisfaction_score) w.updated_at = datetime.now() + session.commit() - + + # 如果状态或优先级发生变化,记录处理历史 + has_status_change = previous_status != new_status + has_priority_change = previous_priority != data.get('priority', w.priority) + + if has_status_change or has_priority_change: + # 构建处理内容 + change_items = [] + if has_status_change: + change_items.append(f"状态变更:{previous_status} → {new_status}") + if has_priority_change: + change_items.append(f"优先级变更:{previous_priority} → {data.get('priority', w.priority)}") + + process_content = ";".join(change_items) + if data.get('resolution'): + process_content += f";解决方案:{data.get('resolution', '')[:100]}" + + # 判断区域 + processor_region = "overseas" if current_role == UserRole.OVERSEAS_OPS else "domestic" + + add_process_history( + workorder_id=workorder_id, + processor_name=current_user, + process_content=process_content or "更新工单信息", + action_type="update", + processor_role=current_role.value, + processor_region=processor_region, + previous_status=previous_status, + new_status=new_status + ) + # 清除工单相关缓存 from src.core.cache_manager import cache_manager cache_manager.clear() # 清除所有缓存 - + updated = { "id": w.id, "title": w.title, @@ -265,25 +485,25 @@ def delete_workorder(workorder_id): workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() if not workorder: return jsonify({"error": "工单不存在"}), 404 - + # 先删除所有相关的子记录(按外键依赖顺序) # 1. 删除工单建议记录 try: session.execute(text("DELETE FROM work_order_suggestions WHERE work_order_id = :id"), {"id": workorder_id}) except Exception as e: print(f"删除工单建议记录失败: {e}") - + # 2. 删除对话记录 session.query(Conversation).filter(Conversation.work_order_id == workorder_id).delete() - + # 3. 删除工单 session.delete(workorder) session.commit() - + # 清除工单相关缓存 from src.core.cache_manager import cache_manager cache_manager.clear() # 清除所有缓存 - + return jsonify({ "success": True, "message": "工单删除成功" @@ -362,22 +582,22 @@ def save_workorder_human_resolution(workorder_id): except Exception: sim = 0.0 rec.ai_similarity = sim - + # 使用简化的配置 config = SimpleAIAccuracyConfig() - + # 自动审批条件 approved = config.should_auto_approve(sim) rec.approved = approved - + # 记录使用人工描述入库的标记(当AI准确率低于阈值时) use_human_resolution = config.should_use_human_resolution(sim) rec.use_human_resolution = use_human_resolution - + session.commit() return jsonify({ - "success": True, - "similarity": sim, + "success": True, + "similarity": sim, "approved": approved, "use_human_resolution": use_human_resolution }) @@ -392,14 +612,14 @@ def approve_workorder_to_knowledge(workorder_id): w = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() if not w: return jsonify({"error": "工单不存在"}), 404 - + rec = session.query(WorkOrderSuggestion).filter(WorkOrderSuggestion.work_order_id == w.id).first() if not rec: return jsonify({"error": "未找到工单建议记录"}), 400 - + # 使用简化的配置 config = SimpleAIAccuracyConfig() - + # 确定使用哪个内容入库 if rec.use_human_resolution and rec.human_resolution: # AI准确率低于阈值,使用人工描述入库 @@ -415,7 +635,7 @@ def approve_workorder_to_knowledge(workorder_id): logger.info(f"工单 {workorder_id} 使用AI建议入库,相似度: {rec.ai_similarity:.4f}") else: return jsonify({"error": "未找到可入库的内容"}), 400 - + # 入库为知识条目 entry = KnowledgeEntry( question=w.title or (w.description[:20] if w.description else '工单问题'), @@ -429,9 +649,9 @@ def approve_workorder_to_knowledge(workorder_id): ) session.add(entry) session.commit() - + return jsonify({ - "success": True, + "success": True, "knowledge_id": entry.id, "used_content": "human_resolution" if rec.use_human_resolution else "ai_suggestion", "confidence_score": confidence_score @@ -447,25 +667,25 @@ def import_workorders(): # 检查是否有文件上传 if 'file' not in request.files: return jsonify({"error": "没有上传文件"}), 400 - + file = request.files['file'] if file.filename == '': return jsonify({"error": "没有选择文件"}), 400 - + if not file.filename.endswith(('.xlsx', '.xls')): return jsonify({"error": "只支持Excel文件(.xlsx, .xls)"}), 400 - + # 保存上传的文件 filename = secure_filename(file.filename) upload_path = os.path.join('uploads', filename) os.makedirs('uploads', exist_ok=True) file.save(upload_path) - + # 解析Excel文件 try: df = pd.read_excel(upload_path) imported_workorders = [] - + # 处理每一行数据 for index, row in df.iterrows(): # 根据Excel列名映射到工单字段 @@ -474,16 +694,16 @@ def import_workorders(): category = str(row.get('分类', row.get('category', '技术问题'))) priority = str(row.get('优先级', row.get('priority', 'medium'))) status = str(row.get('状态', row.get('status', 'open'))) - + # 验证必填字段 if not title or title.strip() == '': continue - + # 生成唯一的工单ID timestamp = int(time.time()) unique_id = str(uuid.uuid4())[:8] order_id = f"IMP_{timestamp}_{unique_id}" - + # 创建工单到数据库 try: with db_manager.get_session() as session: @@ -497,26 +717,26 @@ def import_workorders(): created_at=datetime.now(), updated_at=datetime.now() ) - + # 处理可选字段 if pd.notna(row.get('解决方案', row.get('resolution'))): workorder.resolution = str(row.get('解决方案', row.get('resolution'))) - + if pd.notna(row.get('满意度', row.get('satisfaction_score'))): try: workorder.satisfaction_score = int(row.get('满意度', row.get('satisfaction_score'))) except (ValueError, TypeError): workorder.satisfaction_score = None - + session.add(workorder) session.commit() - + logger.info(f"成功导入工单: {order_id} - {title}") - + except Exception as db_error: logger.error(f"导入工单到数据库失败: {db_error}") continue - + # 添加到返回列表 imported_workorders.append({ "id": workorder.id, @@ -531,23 +751,23 @@ def import_workorders(): "resolution": workorder.resolution, "satisfaction_score": workorder.satisfaction_score }) - + # 清理上传的文件 os.remove(upload_path) - + return jsonify({ "success": True, "message": f"成功导入 {len(imported_workorders)} 个工单", "imported_count": len(imported_workorders), "workorders": imported_workorders }) - + except Exception as e: # 清理上传的文件 if os.path.exists(upload_path): os.remove(upload_path) return jsonify({"error": f"解析Excel文件失败: {str(e)}"}), 400 - + except Exception as e: return jsonify({"error": str(e)}), 500 @@ -560,7 +780,7 @@ def download_import_template(): "success": True, "template_url": f"/uploads/workorder_template.xlsx" }) - + except Exception as e: return jsonify({"error": str(e)}), 500 @@ -569,27 +789,256 @@ def download_import_template_file(): """直接返回工单导入模板文件(下载)""" try: template_path = _ensure_workorder_template_file() - + # 检查文件是否存在 if not os.path.exists(template_path): logger.error(f"模板文件不存在: {template_path}") return jsonify({"error": "模板文件不存在"}), 404 - + # 检查文件大小 file_size = os.path.getsize(template_path) if file_size == 0: logger.error(f"模板文件为空: {template_path}") return jsonify({"error": "模板文件为空"}), 500 - + logger.info(f"准备下载模板文件: {template_path}, 大小: {file_size} bytes") - + try: # Flask>=2 使用 download_name return send_file(template_path, as_attachment=True, download_name='工单导入模板.xlsx', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') except TypeError: # 兼容 Flask<2 的 attachment_filename return send_file(template_path, as_attachment=True, attachment_filename='工单导入模板.xlsx', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - + except Exception as e: logger.error(f"下载模板文件失败: {e}") return jsonify({"error": f"下载失败: {str(e)}"}), 500 + +@workorders_bp.route('//dispatch', methods=['POST']) +def dispatch_workorder(workorder_id): + """工单分发:运维将工单分配给业务模块""" + try: + # 获取当前用户角色和权限 + current_role = get_current_user_role() + current_user = get_current_user_name() + + # 检查分发权限 + if not WorkOrderPermissionManager.can_dispatch_workorder(current_role): + return jsonify({ + "success": False, + "error": "无权进行工单分发,只有属地运维和管理员可以分发工单" + }), 403 + + # 获取请求数据 + data = request.get_json() or {} + target_module_str = data.get('target_module', '') + + if not target_module_str: + return jsonify({"success": False, "error": "请指定目标模块"}), 400 + + # 验证模块 + try: + target_module = WorkOrderModule(target_module_str) + except ValueError: + return jsonify({"success": False, "error": f"无效的模块: {target_module_str}"}), 400 + + # 获取工单 + with db_manager.get_session() as session: + workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() + if not workorder: + return jsonify({"success": False, "error": "工单不存在"}), 404 + + # 执行分发 + module_owner = WorkOrderDispatchManager.get_module_owner(target_module) + + # 记录分发前的状态 + previous_status = workorder.status + + # 更新工单信息 + workorder.assigned_module = target_module.value + workorder.module_owner = module_owner + workorder.dispatcher = current_user + workorder.dispatch_time = datetime.now() + workorder.status = "assigned" # 更新状态为已分配 + + # 根据区域自动设置(可以从工单source或其他字段判断) + # 这里简化处理,可以根据实际需求调整 + if not workorder.region: + # 如果source包含特定关键词,可以判断区域 + source = workorder.source or "" + if any(keyword in source.lower() for keyword in ["overseas", "abroad", "海外"]): + workorder.region = "overseas" + else: + workorder.region = "domestic" + + session.commit() + + # 记录处理历史:工单分发 + processor_region = "overseas" if current_role == UserRole.OVERSEAS_OPS else "domestic" + add_process_history( + workorder_id=workorder_id, + processor_name=current_user, + process_content=f"工单已分发到{target_module.value}模块,业务接口人:{module_owner}", + action_type="dispatch", + processor_role=current_role.value, + processor_region=processor_region, + previous_status=previous_status, + new_status="assigned", + assigned_module=target_module.value + ) + + logger.info(f"工单 {workorder_id} 已分发到 {target_module.value} 模块,分发人: {current_user}") + + return jsonify({ + "success": True, + "message": f"工单已成功分发到{target_module.value}模块", + "workorder": { + "id": workorder.id, + "assigned_module": workorder.assigned_module, + "module_owner": workorder.module_owner, + "dispatcher": workorder.dispatcher, + "dispatch_time": workorder.dispatch_time.isoformat() if workorder.dispatch_time else None, + "status": workorder.status + } + }) + + except Exception as e: + logger.error(f"工单分发失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + +@workorders_bp.route('//suggest-module', methods=['POST']) +def suggest_workorder_module(workorder_id): + """AI建议工单应该分配的模块""" + try: + with db_manager.get_session() as session: + workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() + if not workorder: + return jsonify({"success": False, "error": "工单不存在"}), 404 + + # 使用AI分析建议模块 + suggested_module = WorkOrderDispatchManager.suggest_module( + description=workorder.description or "", + title=workorder.title or "" + ) + + return jsonify({ + "success": True, + "suggested_module": suggested_module.value if suggested_module else None, + "module_owner": WorkOrderDispatchManager.get_module_owner(suggested_module) if suggested_module else None + }) + + except Exception as e: + logger.error(f"模块建议失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + +@workorders_bp.route('/modules', methods=['GET']) +def get_available_modules(): + """获取所有可用的模块列表""" + try: + modules = [ + {"value": m.value, "name": m.name, "owner": WorkOrderDispatchManager.get_module_owner(m)} + for m in WorkOrderModule + if m != WorkOrderModule.UNASSIGNED + ] + return jsonify({"success": True, "modules": modules}) + except Exception as e: + return jsonify({"success": False, "error": str(e)}), 500 + +@workorders_bp.route('//process-history', methods=['GET']) +def get_workorder_process_history(workorder_id): + """获取工单处理过程记录""" + try: + with db_manager.get_session() as session: + workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() + if not workorder: + return jsonify({"error": "工单不存在"}), 404 + + # 获取处理历史 + history_list = session.query(WorkOrderProcessHistory).filter( + WorkOrderProcessHistory.work_order_id == workorder_id + ).order_by(WorkOrderProcessHistory.process_time.asc()).all() + + history_data = [] + for ph in history_list: + history_data.append({ + "id": ph.id, + "processor_name": ph.processor_name, + "processor_role": ph.processor_role, + "processor_region": ph.processor_region, + "process_content": ph.process_content, + "action_type": ph.action_type, + "previous_status": ph.previous_status, + "new_status": ph.new_status, + "assigned_module": ph.assigned_module, + "process_time": ph.process_time.isoformat() if ph.process_time else None + }) + + return jsonify({ + "success": True, + "workorder_id": workorder_id, + "process_history": history_data, + "total": len(history_data) + }) + except Exception as e: + logger.error(f"获取处理历史失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + +@workorders_bp.route('//process-history', methods=['POST']) +def add_workorder_process_history(workorder_id): + """手动添加工单处理过程记录""" + try: + # 获取当前用户信息 + current_user = get_current_user_name() + current_role = get_current_user_role() + + data = request.get_json() or {} + process_content = data.get('process_content', '').strip() + + if not process_content: + return jsonify({"success": False, "error": "处理内容不能为空"}), 400 + + with db_manager.get_session() as session: + workorder = session.query(WorkOrder).filter(WorkOrder.id == workorder_id).first() + if not workorder: + return jsonify({"success": False, "error": "工单不存在"}), 404 + + # 获取可选参数 + action_type = data.get('action_type', 'process') # 默认操作类型为process + processor_role = data.get('processor_role', current_role.value) + processor_region = data.get('processor_region') + if not processor_region: + # 根据角色自动判断区域 + processor_region = "overseas" if current_role == UserRole.OVERSEAS_OPS else "domestic" + + previous_status = data.get('previous_status', workorder.status) + new_status = data.get('new_status', workorder.status) + assigned_module = data.get('assigned_module', workorder.assigned_module) + + # 添加处理记录 + history = add_process_history( + workorder_id=workorder_id, + processor_name=data.get('processor_name', current_user), + process_content=process_content, + action_type=action_type, + processor_role=processor_role, + processor_region=processor_region, + previous_status=previous_status, + new_status=new_status, + assigned_module=assigned_module + ) + + return jsonify({ + "success": True, + "message": "处理记录已添加", + "history": { + "id": history.id, + "processor_name": history.processor_name, + "processor_role": history.processor_role, + "process_content": history.process_content, + "action_type": history.action_type, + "process_time": history.process_time.isoformat() if history.process_time else None + } + }) + except Exception as e: + logger.error(f"添加处理记录失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 diff --git a/src/web/static/js/chat.js b/src/web/static/js/chat.js index e1d1fe8..e38fe8f 100644 --- a/src/web/static/js/chat.js +++ b/src/web/static/js/chat.js @@ -6,7 +6,7 @@ class ChatClient { this.sessionId = null; this.isConnected = false; this.messageCount = 0; - + this.init(); } @@ -18,24 +18,24 @@ class ChatClient { bindEvents() { // 开始对话 document.getElementById('start-chat').addEventListener('click', () => this.startChat()); - + // 结束对话 document.getElementById('end-chat').addEventListener('click', () => this.endChat()); - + // 发送消息 document.getElementById('send-button').addEventListener('click', () => this.sendMessage()); - + // 回车发送 document.getElementById('message-input').addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.sendMessage(); } }); - + // 创建工单 document.getElementById('create-work-order').addEventListener('click', () => this.showWorkOrderModal()); document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder()); - + // 快速操作按钮 document.querySelectorAll('.quick-action-btn').forEach(btn => { btn.addEventListener('click', (e) => { @@ -50,17 +50,17 @@ class ChatClient { try { // 连接WebSocket await this.connectWebSocket(); - + // 创建会话 const userId = document.getElementById('user-id').value || 'anonymous'; const workOrderId = document.getElementById('work-order-id').value || null; - + const response = await this.sendWebSocketMessage({ type: 'create_session', user_id: userId, work_order_id: workOrderId ? parseInt(workOrderId) : null }); - + if (response.type === 'session_created') { this.sessionId = response.session_id; this.updateSessionInfo(); @@ -69,7 +69,7 @@ class ChatClient { } else { this.showError('创建会话失败'); } - + } catch (error) { console.error('启动对话失败:', error); this.showError('启动对话失败: ' + error.message); @@ -84,11 +84,11 @@ class ChatClient { session_id: this.sessionId }); } - + this.sessionId = null; this.disableChat(); this.addSystemMessage('对话已结束。'); - + } catch (error) { console.error('结束对话失败:', error); } @@ -97,48 +97,48 @@ class ChatClient { async sendMessage() { const input = document.getElementById('message-input'); const message = input.value.trim(); - + if (!message || !this.sessionId) { return; } - + // 清空输入框 input.value = ''; - + // 添加用户消息 this.addMessage('user', message); - + // 显示打字指示器 this.showTypingIndicator(); - + try { const response = await this.sendWebSocketMessage({ type: 'send_message', session_id: this.sessionId, message: message }); - + this.hideTypingIndicator(); - + if (response.type === 'message_response' && response.result.success) { const result = response.result; - + // 添加助手回复 this.addMessage('assistant', result.content, { knowledge_used: result.knowledge_used, confidence_score: result.confidence_score, work_order_id: result.work_order_id }); - + // 更新工单ID if (result.work_order_id) { document.getElementById('work-order-id').value = result.work_order_id; } - + } else { this.addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。'); } - + } catch (error) { this.hideTypingIndicator(); console.error('发送消息失败:', error); @@ -151,12 +151,12 @@ class ChatClient { const description = document.getElementById('wo-description').value; const category = document.getElementById('wo-category').value; const priority = document.getElementById('wo-priority').value; - + if (!title || !description) { this.showError('请填写工单标题和描述'); return; } - + try { const response = await this.sendWebSocketMessage({ type: 'create_work_order', @@ -166,23 +166,23 @@ class ChatClient { category: category, priority: priority }); - + if (response.type === 'work_order_created' && response.result.success) { const workOrderId = response.result.work_order_id; document.getElementById('work-order-id').value = workOrderId; this.addSystemMessage(`工单创建成功!工单号: ${response.result.order_id}`); - + // 关闭模态框 const modal = bootstrap.Modal.getInstance(document.getElementById('workOrderModal')); modal.hide(); - + // 清空表单 document.getElementById('work-order-form').reset(); - + } else { this.showError('创建工单失败: ' + (response.result.error || '未知错误')); } - + } catch (error) { console.error('创建工单失败:', error); this.showError('创建工单失败: ' + error.message); @@ -193,7 +193,7 @@ class ChatClient { return new Promise((resolve, reject) => { try { this.websocket = new WebSocket('ws://localhost:8765'); - + // 设置连接超时 const timeout = setTimeout(() => { if (this.websocket.readyState !== WebSocket.OPEN) { @@ -201,26 +201,26 @@ class ChatClient { reject(new Error('WebSocket连接超时,请检查服务器是否启动')); } }, 5000); // 5秒超时 - + this.websocket.onopen = () => { clearTimeout(timeout); this.isConnected = true; this.updateConnectionStatus(true); resolve(); }; - + this.websocket.onclose = () => { clearTimeout(timeout); this.isConnected = false; this.updateConnectionStatus(false); }; - + this.websocket.onerror = (error) => { clearTimeout(timeout); console.error('WebSocket错误:', error); reject(new Error('WebSocket连接失败,请检查服务器是否启动')); }; - + this.websocket.onmessage = (event) => { try { const data = JSON.parse(event.data); @@ -229,7 +229,7 @@ class ChatClient { console.error('解析WebSocket消息失败:', error); } }; - + } catch (error) { reject(error); } @@ -242,15 +242,15 @@ class ChatClient { reject(new Error('WebSocket未连接')); return; } - + const messageId = 'msg_' + Date.now(); message.messageId = messageId; - + // 设置超时 const timeout = setTimeout(() => { reject(new Error('请求超时')); }, 10000); - + // 监听响应 const handleResponse = (event) => { try { @@ -264,7 +264,7 @@ class ChatClient { // 忽略解析错误 } }; - + this.websocket.addEventListener('message', handleResponse); this.websocket.send(JSON.stringify(message)); }); @@ -277,29 +277,29 @@ class ChatClient { addMessage(role, content, metadata = {}) { const messagesContainer = document.getElementById('chat-messages'); - + // 如果是第一条消息,清空欢迎信息 if (this.messageCount === 0) { messagesContainer.innerHTML = ''; } - + const messageDiv = document.createElement('div'); messageDiv.className = `message ${role}`; - + const avatar = document.createElement('div'); avatar.className = 'message-avatar'; avatar.textContent = role === 'user' ? 'U' : 'A'; - + const contentDiv = document.createElement('div'); contentDiv.className = 'message-content'; contentDiv.innerHTML = content; - + // 添加时间戳 const timeDiv = document.createElement('div'); timeDiv.className = 'message-time'; timeDiv.textContent = new Date().toLocaleTimeString(); contentDiv.appendChild(timeDiv); - + // 添加元数据 if (metadata.knowledge_used && metadata.knowledge_used.length > 0) { const knowledgeDiv = document.createElement('div'); @@ -307,21 +307,21 @@ class ChatClient { knowledgeDiv.innerHTML = `基于 ${metadata.knowledge_used.length} 条知识库信息生成`; contentDiv.appendChild(knowledgeDiv); } - + if (metadata.confidence_score) { const confidenceDiv = document.createElement('div'); confidenceDiv.className = 'confidence-score'; confidenceDiv.textContent = `置信度: ${(metadata.confidence_score * 100).toFixed(1)}%`; contentDiv.appendChild(confidenceDiv); } - + if (metadata.work_order_id) { const workOrderDiv = document.createElement('div'); workOrderDiv.className = 'work-order-info'; workOrderDiv.innerHTML = `关联工单: ${metadata.work_order_id}`; contentDiv.appendChild(workOrderDiv); } - + if (role === 'user') { messageDiv.appendChild(contentDiv); messageDiv.appendChild(avatar); @@ -329,20 +329,20 @@ class ChatClient { messageDiv.appendChild(avatar); messageDiv.appendChild(contentDiv); } - + messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; - + this.messageCount++; } addSystemMessage(content) { const messagesContainer = document.getElementById('chat-messages'); - + const messageDiv = document.createElement('div'); messageDiv.className = 'text-center text-muted py-2'; messageDiv.innerHTML = `${content}`; - + messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; } @@ -394,7 +394,7 @@ class ChatClient { this.showError('请先开始对话'); return; } - + const modal = new bootstrap.Modal(document.getElementById('workOrderModal')); modal.show(); } diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js index 49a7b57..09d4352 100644 --- a/src/web/static/js/dashboard.js +++ b/src/web/static/js/dashboard.js @@ -1,6738 +1 @@ -// TSP智能助手综合管理平台前端脚本 - -// 多语言支持 -const translations = { - zh: { - // 导航栏 - 'nav-title': 'TSP智能助手', - 'nav-health-checking': '检查中...', - 'nav-health-normal': '系统正常', - 'nav-health-warning': '系统警告', - 'nav-health-error': '系统错误', - - // 侧边栏 - 'sidebar-dashboard': '仪表板', - 'sidebar-workorders': '工单管理', - 'sidebar-conversations': '智能对话', - 'sidebar-agent': 'Agent管理', - 'sidebar-alerts': '预警管理', - 'sidebar-knowledge': '知识库', - 'sidebar-analytics': '数据分析', - 'sidebar-feishu-sync': '飞书同步', - 'sidebar-conversation-history': '对话历史', - 'sidebar-token-monitor': 'Token监控', - 'sidebar-ai-monitor': 'AI监控', - 'sidebar-system-optimizer': '系统优化', - 'sidebar-system': '系统设置', - - // 预警管理页面 - 'alerts-critical': '严重预警', - 'alerts-warning': '警告预警', - 'alerts-info': '信息预警', - 'alerts-total': '总预警数', - 'alerts-active': '活跃预警', - 'alerts-resolve': '解决', - - // 设置页面 - 'settings-title': '系统设置', - 'settings-basic': '基础设置', - 'settings-system-info': '系统信息', - 'settings-log-config': '日志配置', - 'settings-current-port': '当前服务端口', - 'settings-websocket-port': 'WebSocket端口', - 'settings-log-level': '日志级别', - 'settings-save': '保存设置', - 'settings-save-success': '设置保存成功', - 'settings-save-failed': '保存设置失败', - 'settings-port-note': '服务端口配置需要在配置文件中修改,前端页面仅显示当前状态。', - 'settings-log-note': '调整系统日志的详细程度' - }, - en: { - // Navigation - 'nav-title': 'TSP Intelligent Assistant', - 'nav-health-checking': 'Checking...', - 'nav-health-normal': 'System Normal', - 'nav-health-warning': 'System Warning', - 'nav-health-error': 'System Error', - - // Sidebar - 'sidebar-dashboard': 'Dashboard', - 'sidebar-workorders': 'Work Orders', - 'sidebar-conversations': 'Smart Chat', - 'sidebar-agent': 'Agent Management', - 'sidebar-alerts': 'Alert Management', - 'sidebar-knowledge': 'Knowledge Base', - 'sidebar-analytics': 'Analytics', - 'sidebar-feishu-sync': 'Feishu Sync', - 'sidebar-conversation-history': 'Conversation History', - 'sidebar-token-monitor': 'Token Monitor', - 'sidebar-ai-monitor': 'AI Monitor', - 'sidebar-system-optimizer': 'System Optimizer', - 'sidebar-system': 'System Settings', - - // Alert Management page - 'alerts-critical': 'Critical Alerts', - 'alerts-warning': 'Warning Alerts', - 'alerts-info': 'Info Alerts', - 'alerts-total': 'Total Alerts', - 'alerts-active': 'Active Alerts', - 'alerts-resolve': 'Resolve', - - // Settings page - 'settings-title': 'System Settings', - 'settings-basic': 'Basic Settings', - 'settings-system-info': 'System Information', - 'settings-log-config': 'Log Configuration', - 'settings-current-port': 'Current Service Port', - 'settings-websocket-port': 'WebSocket Port', - 'settings-log-level': 'Log Level', - 'settings-save': 'Save Settings', - 'settings-save-success': 'Settings saved successfully', - 'settings-save-failed': 'Failed to save settings', - 'settings-port-note': 'Service port configuration needs to be modified in configuration files. Frontend only displays current status.', - 'settings-log-note': 'Adjust the detail level of system logs' - } -}; - -// 全局语言切换函数 -function switchLanguage(lang) { - localStorage.setItem('preferred-language', lang); - document.documentElement.lang = lang; - - // 更新按钮状态 - document.getElementById('lang-zh').classList.toggle('active', lang === 'zh'); - document.getElementById('lang-en').classList.toggle('active', lang === 'en'); - - // 更新页面文本 - updatePageLanguage(lang); -} - -// 更新页面语言 -function updatePageLanguage(lang) { - const t = translations[lang]; - if (!t) return; - - // 更新所有带有 data-i18n 属性的元素 - const elements = document.querySelectorAll('[data-i18n]'); - elements.forEach(element => { - const key = element.getAttribute('data-i18n'); - if (t[key]) { - // 检查元素是否包含图标(i标签) - const icon = element.querySelector('i'); - if (icon) { - // 如果包含图标,只更新文本部分 - const textNodes = Array.from(element.childNodes).filter(node => - node.nodeType === Node.TEXT_NODE || (node.nodeType === Node.ELEMENT_NODE && node.tagName !== 'I') - ); - // 保留图标,更新其他文本内容 - element.innerHTML = icon.outerHTML + ' ' + t[key]; - } else { - // 如果没有图标,直接更新文本内容 - element.textContent = t[key]; - } - } - }); - - // 更新导航栏标题(特殊处理,因为包含图标) - const navBrand = document.querySelector('.navbar-brand'); - if (navBrand) { - const icon = navBrand.querySelector('i'); - if (icon) { - navBrand.innerHTML = `${t['nav-title']}`; - } - } - - // 更新当前标签页标题 - const currentTabTitle = document.getElementById('current-tab-title'); - if (currentTabTitle) { - const currentTab = document.querySelector('.nav-link.active'); - if (currentTab) { - const tabKey = currentTab.getAttribute('data-i18n'); - if (tabKey && t[tabKey]) { - // 检查当前标签是否包含图标 - const icon = currentTab.querySelector('i'); - if (icon) { - currentTabTitle.innerHTML = icon.outerHTML + ' ' + t[tabKey]; - } else { - currentTabTitle.textContent = t[tabKey]; - } - } - } - } -} - -class TSPDashboard { - constructor() { - this.currentTab = 'dashboard'; - this.charts = {}; - this.refreshIntervals = {}; - this.websocket = null; - this.sessionId = null; - this.isAgentMode = true; - this.currentLanguage = localStorage.getItem('preferred-language') || 'zh'; - - // 优化:添加前端缓存 - this.cache = new Map(); - this.cacheTimeout = 30000; // 30秒缓存 - - // 智能更新机制 - this.lastUpdateTimes = { - alerts: 0, - workorders: 0, - health: 0, - analytics: 0 - }; - - this.updateThresholds = { - alerts: 10000, // 10秒 - workorders: 30000, // 30秒 - health: 30000, // 30秒 - analytics: 60000 // 60秒 - }; - - this.isPageVisible = true; - - // 分页配置 - this.paginationConfig = { - defaultPageSize: 10, - pageSizeOptions: [5, 10, 20, 50], - maxVisiblePages: 5 - }; - - this.init(); - this.restorePageState(); - this.initLanguage(); - this.initSmartUpdate(); - - // 添加页面卸载时的清理逻辑 - window.addEventListener('beforeunload', () => { - this.destroyAllCharts(); - this.cleanupConnections(); - }); - } - - initLanguage() { - // 初始化语言设置 - document.documentElement.lang = this.currentLanguage; - document.getElementById('lang-zh').classList.toggle('active', this.currentLanguage === 'zh'); - document.getElementById('lang-en').classList.toggle('active', this.currentLanguage === 'en'); - updatePageLanguage(this.currentLanguage); - } - - async generateAISuggestion(workorderId) { - const button = document.querySelector(`button[onclick="dashboard.generateAISuggestion(${workorderId})"]`); - const textarea = document.getElementById(`aiSuggestion_${workorderId}`); - - try { - // 添加加载状态 - if (button) { - button.classList.add('btn-loading'); - button.disabled = true; - } - if (textarea) { - textarea.classList.add('ai-loading'); - textarea.value = '正在生成AI建议,请稍候...'; - } - - const resp = await fetch(`/api/workorders/${workorderId}/ai-suggestion`, { method: 'POST' }); - const data = await resp.json(); - - if (data.success) { - if (textarea) { - textarea.value = data.suggestion || ''; - textarea.classList.remove('ai-loading'); - textarea.classList.add('success-animation'); - - // 移除成功动画类 - setTimeout(() => { - textarea.classList.remove('success-animation'); - }, 600); - } - this.showNotification('AI建议已生成', 'success'); - } else { - throw new Error(data.error || '生成失败'); - } - } catch (e) { - console.error('生成AI建议失败:', e); - if (textarea) { - textarea.value = 'AI建议生成失败,请重试'; - textarea.classList.remove('ai-loading'); - } - this.showNotification('生成AI建议失败: ' + e.message, 'error'); - } finally { - // 移除加载状态 - if (button) { - button.classList.remove('btn-loading'); - button.disabled = false; - } - } - } - - async saveHumanResolution(workorderId) { - try { - const text = document.getElementById(`humanResolution_${workorderId}`).value.trim(); - if (!text) { this.showNotification('请输入人工描述', 'warning'); return; } - const resp = await fetch(`/api/workorders/${workorderId}/human-resolution`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ human_resolution: text }) - }); - const data = await resp.json(); - if (data.success) { - const simEl = document.getElementById(`aiSim_${workorderId}`); - const apprEl = document.getElementById(`aiApproved_${workorderId}`); - const approveBtn = document.getElementById(`approveBtn_${workorderId}`); - const percent = Math.round((data.similarity || 0) * 100); - - // 更新相似度显示,使用语义相似度 - if (simEl) { - simEl.innerHTML = `语义相似度: ${percent}%`; - - // 使用新的CSS类 - if (percent >= 90) { - simEl.className = 'similarity-badge high'; - } else if (percent >= 80) { - simEl.className = 'similarity-badge medium'; - } else { - simEl.className = 'similarity-badge low'; - } - - simEl.title = this.getSimilarityExplanation(percent); - } - - // 更新审批状态 - if (apprEl) { - if (data.use_human_resolution) { - apprEl.textContent = '将使用人工描述入库'; - apprEl.className = 'status-badge human-resolution'; - } else if (data.approved) { - apprEl.textContent = '已自动审批'; - apprEl.className = 'status-badge approved'; - } else { - apprEl.textContent = '未审批'; - apprEl.className = 'status-badge pending'; - } - } - - // 更新审批按钮状态 - if (approveBtn) { - const canApprove = data.approved || data.use_human_resolution; - approveBtn.disabled = !canApprove; - - if (data.use_human_resolution) { - approveBtn.textContent = '使用人工描述入库'; - approveBtn.className = 'approve-btn'; - approveBtn.title = 'AI准确率低于90%,将使用人工描述入库'; - } else if (data.approved) { - approveBtn.textContent = '已自动审批'; - approveBtn.className = 'approve-btn approved'; - approveBtn.title = 'AI建议与人工描述高度一致'; - } else { - approveBtn.textContent = '审批入库'; - approveBtn.className = 'approve-btn'; - approveBtn.title = '手动审批入库'; - } - } - - // 显示更详细的反馈信息 - const message = this.getSimilarityMessage(percent, data.approved, data.use_human_resolution); - this.showNotification(message, data.approved ? 'success' : data.use_human_resolution ? 'warning' : 'info'); - } else { - throw new Error(data.error || '保存失败'); - } - } catch (e) { - console.error('保存人工描述失败:', e); - this.showNotification('保存人工描述失败: ' + e.message, 'error'); - } - } - - async approveToKnowledge(workorderId) { - try { - const resp = await fetch(`/api/workorders/${workorderId}/approve-to-knowledge`, { method: 'POST' }); - const data = await resp.json(); - if (data.success) { - const contentType = data.used_content === 'human_resolution' ? '人工描述' : 'AI建议'; - const confidence = Math.round((data.confidence_score || 0) * 100); - this.showNotification(`已入库为知识条目!使用${contentType},置信度: ${confidence}%`, 'success'); - } else { - throw new Error(data.error || '入库失败'); - } - } catch (e) { - console.error('入库失败:', e); - this.showNotification('入库失败: ' + e.message, 'error'); - } - } - init() { - this.bindEvents(); - // 优化:并行加载初始数据,提高响应速度 - this.loadInitialDataAsync(); - this.startAutoRefresh(); - this.initCharts(); - } - - async loadInitialDataAsync() { - // 并行加载多个数据源 - try { - await Promise.all([ - this.loadDashboard(), - this.loadWorkOrders(), - this.loadConversationHistory(), - this.loadKnowledgeBase() - ]); - } catch (error) { - console.error('并行加载数据失败:', error); - // 回退到串行加载 - await this.loadInitialData(); - } - } - - // 优化:添加缓存方法 - getCachedData(key) { - const cached = this.cache.get(key); - if (cached && Date.now() - cached.timestamp < this.cacheTimeout) { - return cached.data; - } - return null; - } - - // 统一分页组件 - createPaginationComponent(data, containerId, loadFunction, itemName = '条记录') { - const paginationContainer = document.getElementById(containerId); - if (!paginationContainer) return; - - // 调试信息 - console.log(`分页数据 (${containerId}):`, data); - - const { page, total_pages, total, per_page } = data; - - // 检查必要字段 - if (page === undefined || total_pages === undefined || total === undefined || per_page === undefined) { - console.error(`分页数据不完整 (${containerId}):`, { page, total_pages, total, per_page }); - paginationContainer.innerHTML = '
分页数据加载中...
'; - return; - } - - if (total_pages <= 1) { - paginationContainer.innerHTML = ''; - return; - } - - let paginationHtml = ` -
-
- 共 ${total} ${itemName},第 ${page} / ${total_pages} 页 -
- - -
-
- -
- `; - - paginationContainer.innerHTML = paginationHtml; - } - - // 加载指定页面 - loadPage(loadFunction, page, containerId) { - const pageSize = this.getPageSize(containerId); - if (loadFunction === 'loadAlerts') { - this.loadAlerts(page, true); - } else if (loadFunction === 'loadWorkOrders') { - this.loadWorkOrders(page, true); - } else if (loadFunction === 'loadKnowledge') { - this.loadKnowledge(page); - } else if (loadFunction === 'loadConversationHistory') { - this.loadConversationHistory(page); - } - } - - // 改变每页显示条数 - changePageSize(containerId, pageSize, loadFunction) { - // 保存页面大小到localStorage - localStorage.setItem(`pageSize_${containerId}`, pageSize); - - // 重新加载第一页 - if (loadFunction === 'loadAlerts') { - this.loadAlerts(1, true); - } else if (loadFunction === 'loadWorkOrders') { - this.loadWorkOrders(1, true); - } else if (loadFunction === 'loadKnowledge') { - this.loadKnowledge(1); - } else if (loadFunction === 'loadConversationHistory') { - this.loadConversationHistory(1); - } - } - - // 获取页面大小 - getPageSize(containerId) { - const saved = localStorage.getItem(`pageSize_${containerId}`); - return saved ? parseInt(saved) : this.paginationConfig.defaultPageSize; - } - - setCachedData(key, data) { - this.cache.set(key, { - data: data, - timestamp: Date.now() - }); - } - - clearCache() { - this.cache.clear(); - } - - bindEvents() { - // 标签页切换 - document.querySelectorAll('[data-tab]').forEach(tab => { - tab.addEventListener('click', (e) => { - e.preventDefault(); - this.switchTab(tab.dataset.tab); - }); - }); - - // 对话控制 - document.getElementById('start-chat').addEventListener('click', () => this.startChat()); - document.getElementById('end-chat').addEventListener('click', () => this.endChat()); - document.getElementById('create-work-order').addEventListener('click', () => this.showCreateWorkOrderModal()); - document.getElementById('send-button').addEventListener('click', () => this.sendMessage()); - document.getElementById('message-input').addEventListener('keypress', (e) => { - if (e.key === 'Enter') this.sendMessage(); - }); - - // 快速操作按钮 - document.querySelectorAll('.quick-action-btn').forEach(btn => { - btn.addEventListener('click', () => { - const message = btn.dataset.message; - document.getElementById('message-input').value = message; - this.sendMessage(); - }); - }); - - // Agent控制 - document.getElementById('agent-mode-toggle').addEventListener('change', (e) => { - this.toggleAgentMode(e.target.checked); - }); - - // Agent对话功能 - document.getElementById('send-agent-message').addEventListener('click', () => this.sendAgentMessage()); - document.getElementById('clear-agent-chat').addEventListener('click', () => this.clearAgentChat()); - document.getElementById('agent-message-input').addEventListener('keypress', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - this.sendAgentMessage(); - } - }); - - // Agent控制按钮 - document.getElementById('trigger-sample-action').addEventListener('click', () => this.triggerSampleAction()); - document.getElementById('clear-agent-history').addEventListener('click', () => this.clearAgentHistory()); - document.getElementById('start-agent-monitoring').addEventListener('click', () => this.startAgentMonitoring()); - document.getElementById('stop-agent-monitoring').addEventListener('click', () => this.stopAgentMonitoring()); - document.getElementById('proactive-monitoring').addEventListener('click', () => this.proactiveMonitoring()); - document.getElementById('intelligent-analysis').addEventListener('click', () => this.intelligentAnalysis()); - - // 预警管理 - document.getElementById('refresh-alerts').addEventListener('click', () => this.loadAlerts()); - document.getElementById('alert-filter').addEventListener('change', () => this.updateAlertsDisplay()); - - // 统计数字点击事件 - document.addEventListener('click', (e) => { - if (e.target.classList.contains('clickable-stat')) { - const type = e.target.dataset.type; - const status = e.target.dataset.status || e.target.dataset.level; - this.showStatPreview(type, status); - } - }); - - // 知识库管理 - document.getElementById('search-knowledge').addEventListener('click', () => this.searchKnowledge()); - document.getElementById('knowledge-search').addEventListener('keypress', (e) => { - if (e.key === 'Enter') this.searchKnowledge(); - }); - - // 工单管理 - document.getElementById('refresh-workorders').addEventListener('click', () => this.loadWorkOrders()); - document.getElementById('workorder-status-filter').addEventListener('change', () => this.loadWorkOrders()); - document.getElementById('workorder-priority-filter').addEventListener('change', () => this.loadWorkOrders()); - - // 模态框 - document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder()); - document.getElementById('add-knowledge-btn').addEventListener('click', () => this.addKnowledge()); - document.getElementById('upload-file-btn').addEventListener('click', () => this.uploadFile()); - - // 置信度滑块 - document.getElementById('knowledge-confidence').addEventListener('input', (e) => { - document.getElementById('confidence-value').textContent = e.target.value; - }); - document.getElementById('file-confidence').addEventListener('input', (e) => { - document.getElementById('file-confidence-value').textContent = e.target.value; - }); - - // 处理方式选择 - document.getElementById('process-method').addEventListener('change', (e) => { - const manualDiv = document.getElementById('manual-question-div'); - if (e.target.value === 'manual') { - manualDiv.style.display = 'block'; - } else { - manualDiv.style.display = 'none'; - } - }); - - // 系统设置 - document.getElementById('system-settings-form').addEventListener('submit', (e) => { - e.preventDefault(); - this.saveSystemSettings(); - }); - - // API测试按钮事件 - const testApiBtn = document.getElementById('test-api-connection'); - if (testApiBtn) { - testApiBtn.addEventListener('click', () => this.testApiConnection()); - } - - const testModelBtn = document.getElementById('test-model-response'); - if (testModelBtn) { - testModelBtn.addEventListener('click', () => this.testModelResponse()); - } - - // 重启服务按钮事件 - const restartBtn = document.getElementById('restart-service'); - if (restartBtn) { - restartBtn.addEventListener('click', () => { - if (confirm('确定要重启服务吗?这将中断当前连接。')) { - this.showNotification('重启服务功能待实现', 'info'); - } - }); - } - } - - switchTab(tabName) { - // 更新导航状态 - document.querySelectorAll('.nav-link').forEach(link => { - link.classList.remove('active'); - }); - document.querySelector(`[data-tab="${tabName}"]`).classList.add('active'); - - // 显示对应内容 - document.querySelectorAll('.tab-content').forEach(content => { - content.style.display = 'none'; - }); - document.getElementById(`${tabName}-tab`).style.display = 'block'; - - this.currentTab = tabName; - - // 如果切换到分析页面,重新初始化图表 - if (tabName === 'analytics') { - // 延迟一点时间确保DOM已更新 - setTimeout(() => { - this.initializeCharts(); - }, 100); - } - - // 保存当前页面状态 - this.savePageState(); - - // 加载对应数据 - switch(tabName) { - case 'dashboard': - this.loadDashboardData(); - break; - case 'chat': - this.loadChatData(); - break; - case 'agent': - this.loadAgentData(); - break; - case 'alerts': - this.loadAlerts(); - break; - case 'knowledge': - this.loadKnowledge(); - break; - case 'workorders': - this.loadWorkOrders(); - break; - case 'conversation-history': - this.loadConversationHistory(); - break; - case 'token-monitor': - this.loadTokenMonitor(); - break; - case 'ai-monitor': - this.loadAIMonitor(); - break; - case 'system-optimizer': - this.loadSystemOptimizer(); - break; - case 'analytics': - this.loadAnalytics(); - break; - case 'settings': - this.loadSettings(); - break; - } - } - - savePageState() { - const state = { - currentTab: this.currentTab, - timestamp: Date.now() - }; - localStorage.setItem('tsp_dashboard_state', JSON.stringify(state)); - } - - restorePageState() { - try { - const savedState = localStorage.getItem('tsp_dashboard_state'); - if (savedState) { - const state = JSON.parse(savedState); - // 如果状态保存时间不超过1小时,则恢复 - if (Date.now() - state.timestamp < 3600000) { - this.switchTab(state.currentTab); - } - } - } catch (error) { - console.warn('恢复页面状态失败:', error); - } - } - - async loadInitialData() { - await Promise.all([ - this.loadHealth(), - this.loadDashboardData(), - this.loadSystemInfo() - ]); - } - - // 初始化智能更新机制 - initSmartUpdate() { - // 页面可见性检测 - document.addEventListener('visibilitychange', () => { - this.isPageVisible = !document.hidden; - if (this.isPageVisible) { - // 页面重新可见时,立即更新数据 - this.smartRefresh(); - } - }); - - // 尝试连接WebSocket获取实时更新 - this.initWebSocketConnection(); - - // 智能定时刷新 - this.startSmartRefresh(); - } - - // 初始化WebSocket连接 - initWebSocketConnection() { - try { - const wsUrl = `ws://localhost:8765/dashboard`; - this.websocket = new WebSocket(wsUrl); - - this.websocket.onopen = () => { - console.log('WebSocket连接已建立'); - // 发送订阅消息 - this.websocket.send(JSON.stringify({ - type: 'subscribe', - topics: ['alerts', 'workorders', 'health'] - })); - }; - - this.websocket.onmessage = (event) => { - try { - const data = JSON.parse(event.data); - this.handleRealtimeUpdate(data); - } catch (error) { - console.error('WebSocket消息解析失败:', error); - } - }; - - this.websocket.onclose = () => { - console.log('WebSocket连接已关闭'); - // 5秒后重连 - setTimeout(() => { - if (this.isPageVisible) { - this.initWebSocketConnection(); - } - }, 5000); - }; - - this.websocket.onerror = (error) => { - console.error('WebSocket连接错误:', error); - }; - } catch (error) { - console.log('WebSocket连接失败,使用轮询模式:', error); - } - } - - // 处理实时更新 - handleRealtimeUpdate(data) { - switch (data.type) { - case 'alert_update': - // 直接更新预警统计,无需API调用 - this.updateAlertStatistics(data.alerts); - this.lastUpdateTimes.alerts = Date.now(); - break; - case 'workorder_update': - // 更新工单统计 - if (this.currentTab === 'workorders') { - this.loadWorkOrders(); - } - this.lastUpdateTimes.workorders = Date.now(); - break; - case 'health_update': - // 更新健康状态 - this.updateHealthDisplay(data.health); - this.lastUpdateTimes.health = Date.now(); - break; - } - } - - // 智能刷新机制 - startSmartRefresh() { - // 每5秒检查一次是否需要更新 - this.refreshIntervals.smart = setInterval(() => { - if (this.isPageVisible) { - this.smartRefresh(); - } - }, 5000); - } - - // 智能刷新逻辑 - smartRefresh() { - const now = Date.now(); - - // 如果最近有用户操作,跳过自动更新 - if (now - this.lastUpdateTimes.alerts < 5000) { // 5秒内不自动更新 - return; - } - - // 检查预警数据是否需要更新 - if (now - this.lastUpdateTimes.alerts > this.updateThresholds.alerts) { - this.refreshAlertStats(); - this.lastUpdateTimes.alerts = now; - } - - // 检查健康状态是否需要更新 - if (now - this.lastUpdateTimes.health > this.updateThresholds.health) { - this.loadHealth(); - this.lastUpdateTimes.health = now; - } - - // 检查分析数据是否需要更新 - if (now - this.lastUpdateTimes.analytics > this.updateThresholds.analytics) { - this.loadAnalytics(); - this.lastUpdateTimes.analytics = now; - } - - // 根据当前标签页决定是否更新工单数据 - if (this.currentTab === 'workorders' && - now - this.lastUpdateTimes.workorders > this.updateThresholds.workorders) { - this.loadWorkOrders(); - this.lastUpdateTimes.workorders = now; - } - } - - startAutoRefresh() { - // 保留原有的刷新机制作为备用 - this.refreshIntervals.health = setInterval(() => { - if (this.isPageVisible) { - this.loadHealth(); - } - }, 30000); - - this.refreshIntervals.currentTab = setInterval(() => { - if (this.isPageVisible) { - this.refreshCurrentTab(); - } - }, 30000); - } - - refreshCurrentTab() { - switch(this.currentTab) { - case 'dashboard': - this.loadDashboardData(); - break; - case 'alerts': - this.loadAlerts(); - break; - case 'agent': - this.loadAgentData(); - break; - } - } - - // 刷新预警统计(优化版) - async refreshAlertStats() { - try { - // 检查缓存 - const cacheKey = 'alerts_stats'; - const cachedData = this.cache.get(cacheKey); - const now = Date.now(); - - if (cachedData && (now - cachedData.timestamp) < this.cacheTimeout) { - // 使用缓存数据 - this.updateAlertStatistics(cachedData.data); - return; - } - - const response = await fetch('/api/alerts?per_page=1000'); // 获取全部数据 - const data = await response.json(); - - if (data.alerts) { - // 更新缓存 - this.cache.set(cacheKey, { - data: data.alerts, - timestamp: now - }); - - this.updateAlertStatistics(data.alerts); - } - } catch (error) { - console.error('刷新预警统计失败:', error); - // 静默处理错误,避免频繁的错误日志 - } - } - - // 更新健康状态显示 - updateHealthDisplay(healthData) { - if (healthData.status) { - const statusElement = document.getElementById('health-status'); - if (statusElement) { - statusElement.textContent = healthData.status; - statusElement.className = `badge ${this.getHealthBadgeClass(healthData.status)}`; - } - } - - if (healthData.details) { - const detailsElement = document.getElementById('health-details'); - if (detailsElement) { - detailsElement.innerHTML = healthData.details; - } - } - } - - async loadHealth() { - try { - const response = await fetch('/api/health'); - const data = await response.json(); - this.updateHealthDisplay(data); - } catch (error) { - console.error('加载健康状态失败:', error); - } - } - - updateHealthDisplay(health) { - const healthScore = health.health_score || 0; - const healthStatus = health.status || 'unknown'; - - // 更新健康指示器 - const healthDot = document.getElementById('health-dot'); - const healthStatusText = document.getElementById('health-status'); - const systemHealthDot = document.getElementById('system-health-dot'); - const systemHealthText = document.getElementById('system-health-text'); - const healthProgress = document.getElementById('health-progress'); - - if (healthDot) { - healthDot.className = `health-dot ${healthStatus}`; - } - if (healthStatusText) { - healthStatusText.textContent = this.getHealthStatusText(healthStatus); - } - if (systemHealthDot) { - systemHealthDot.className = `health-dot ${healthStatus}`; - } - if (systemHealthText) { - systemHealthText.textContent = this.getHealthStatusText(healthStatus); - } - if (healthProgress) { - healthProgress.style.width = `${healthScore * 100}%`; - healthProgress.className = `progress-bar ${this.getHealthColor(healthScore)}`; - } - - // 更新内存和CPU使用率 - const memoryUsage = health.memory_usage || 0; - const memoryProgress = document.getElementById('memory-progress'); - if (memoryProgress) { - memoryProgress.style.width = `${memoryUsage}%`; - } - - const cpuUsage = health.cpu_usage || 0; - const cpuProgress = document.getElementById('cpu-progress'); - if (cpuProgress) { - cpuProgress.style.width = `${cpuUsage}%`; - } - } - - getHealthStatusText(status) { - const statusMap = { - 'excellent': '优秀', - 'good': '良好', - 'fair': '一般', - 'poor': '较差', - 'critical': '严重', - 'unknown': '未知' - }; - return statusMap[status] || status; - } - - getHealthColor(score) { - if (score >= 0.8) return 'bg-success'; - if (score >= 0.6) return 'bg-info'; - if (score >= 0.4) return 'bg-warning'; - return 'bg-danger'; - } - - async loadDashboardData() { - try { - const [sessionsResponse, alertsResponse, workordersResponse, knowledgeResponse] = await Promise.all([ - fetch('/api/chat/sessions'), - fetch('/api/alerts?per_page=1000'), // 获取全部预警数据 - fetch('/api/workorders'), - fetch('/api/knowledge/stats') - ]); - - const sessions = await sessionsResponse.json(); - const alerts = await alertsResponse.json(); - const workorders = await workordersResponse.json(); - const knowledge = await knowledgeResponse.json(); - - // 更新统计卡片 - document.getElementById('total-sessions').textContent = sessions.sessions?.length || 0; - document.getElementById('total-alerts').textContent = alerts.alerts?.length || 0; - document.getElementById('total-workorders').textContent = workorders.workorders?.filter(w => w.status === 'open').length || 0; - document.getElementById('knowledge-count').textContent = knowledge.total_entries || 0; - - // 更新预警统计数字 - if (alerts.alerts) { - this.updateAlertStatistics(alerts.alerts); - } - - // 更新知识库详细统计 - document.getElementById('knowledge-total').textContent = knowledge.total_entries || 0; - document.getElementById('knowledge-active').textContent = knowledge.active_entries || 0; - const confidencePercent = Math.round((knowledge.average_confidence || 0) * 100); - document.getElementById('knowledge-confidence').style.width = `${confidencePercent}%`; - document.getElementById('knowledge-confidence').setAttribute('aria-valuenow', confidencePercent); - document.getElementById('knowledge-confidence').textContent = `${confidencePercent}%`; - - // 更新性能图表 - await this.updatePerformanceChart(sessions, alerts, workorders); - - // 更新系统健康状态 - await this.updateSystemHealth(); - - // 加载分析数据并更新统计卡片 - await this.loadAnalytics(); - - } catch (error) { - console.error('加载仪表板数据失败:', error); - } - } - - - - initCharts() { - // 性能趋势图 - const performanceCtx = document.getElementById('performanceChart'); - if (performanceCtx) { - this.charts.performance = new Chart(performanceCtx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: '工单数量', - data: [], - borderColor: '#007bff', - backgroundColor: 'rgba(0, 123, 255, 0.1)', - tension: 0.4 - }, { - label: '预警数量', - data: [], - borderColor: '#dc3545', - backgroundColor: 'rgba(220, 53, 69, 0.1)', - tension: 0.4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - scales: { - y: { - beginAtZero: true - } - } - } - }); - } - - // 分析图表 - const analyticsCtx = document.getElementById('analyticsChart'); - if (analyticsCtx) { - this.charts.analytics = new Chart(analyticsCtx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: '满意度', - data: [], - borderColor: '#28a745', - backgroundColor: 'rgba(40, 167, 69, 0.1)', - tension: 0.4 - }, { - label: '解决时间(小时)', - data: [], - borderColor: '#ffc107', - backgroundColor: 'rgba(255, 193, 7, 0.1)', - tension: 0.4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - scales: { - y: { - beginAtZero: true - } - } - } - }); - } - - // 类别分布图 - const categoryCtx = document.getElementById('categoryChart'); - if (categoryCtx) { - this.charts.category = new Chart(categoryCtx, { - type: 'doughnut', - data: { - labels: [], - datasets: [{ - data: [], - backgroundColor: [ - '#007bff', - '#28a745', - '#ffc107', - '#dc3545', - '#17a2b8' - ] - }] - }, - options: { - responsive: true, - maintainAspectRatio: false - } - }); - } - } - - async updatePerformanceChart(sessions, alerts, workorders) { - if (!this.charts.performance) return; - - try { - // 获取真实的分析数据 - const response = await fetch('/api/analytics?days=7&dimension=performance'); - const analyticsData = await response.json(); - - if (analyticsData.trend && analyticsData.trend.length > 0) { - // 使用真实数据 - const labels = analyticsData.trend.map(item => { - const date = new Date(item.date); - return `${date.getMonth() + 1}/${date.getDate()}`; - }); - - const workorderData = analyticsData.trend.map(item => item.workorders || 0); - const alertData = analyticsData.trend.map(item => item.alerts || 0); - - this.charts.performance.data.labels = labels; - this.charts.performance.data.datasets[0].data = workorderData; - this.charts.performance.data.datasets[1].data = alertData; - this.charts.performance.update(); - } else { - // 如果没有真实数据,显示提示 - this.charts.performance.data.labels = ['暂无数据']; - this.charts.performance.data.datasets[0].data = [0]; - this.charts.performance.data.datasets[1].data = [0]; - this.charts.performance.update(); - } - } catch (error) { - console.error('获取性能趋势数据失败:', error); - // 出错时显示空数据 - this.charts.performance.data.labels = ['数据加载失败']; - this.charts.performance.data.datasets[0].data = [0]; - this.charts.performance.data.datasets[1].data = [0]; - this.charts.performance.update(); - } - } - - // 更新系统健康状态显示 - async updateSystemHealth() { - try { - const response = await fetch('/api/settings'); - const settings = await response.json(); - - // 更新健康分数 - const healthScore = Math.max(0, 100 - (settings.memory_usage_percent || 0) - (settings.cpu_usage_percent || 0)); - const healthProgress = document.getElementById('health-progress'); - const healthDot = document.getElementById('system-health-dot'); - const healthText = document.getElementById('system-health-text'); - - if (healthProgress) { - healthProgress.style.width = `${healthScore}%`; - healthProgress.setAttribute('aria-valuenow', healthScore); - } - - if (healthDot) { - healthDot.className = 'health-dot'; - if (healthScore >= 80) healthDot.classList.add('excellent'); - else if (healthScore >= 60) healthDot.classList.add('good'); - else if (healthScore >= 40) healthDot.classList.add('fair'); - else if (healthScore >= 20) healthDot.classList.add('poor'); - else healthDot.classList.add('critical'); - } - - if (healthText) { - const statusText = healthScore >= 80 ? '优秀' : - healthScore >= 60 ? '良好' : - healthScore >= 40 ? '一般' : - healthScore >= 20 ? '较差' : '严重'; - healthText.textContent = `${statusText} (${healthScore}%)`; - } - - // 更新内存使用 - const memoryProgress = document.getElementById('memory-progress'); - if (memoryProgress && settings.memory_usage_percent !== undefined) { - memoryProgress.style.width = `${settings.memory_usage_percent}%`; - memoryProgress.setAttribute('aria-valuenow', settings.memory_usage_percent); - } - - // 更新CPU使用 - const cpuProgress = document.getElementById('cpu-progress'); - if (cpuProgress && settings.cpu_usage_percent !== undefined) { - cpuProgress.style.width = `${settings.cpu_usage_percent}%`; - cpuProgress.setAttribute('aria-valuenow', settings.cpu_usage_percent); - } - - } catch (error) { - console.error('更新系统健康状态失败:', error); - } - } - - // 对话功能 - async startChat() { - try { - const userId = document.getElementById('user-id').value; - const workOrderId = document.getElementById('work-order-id').value; - - const response = await fetch('/api/chat/session', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - user_id: userId, - work_order_id: workOrderId ? parseInt(workOrderId) : null - }) - }); - - const data = await response.json(); - if (data.success) { - this.sessionId = data.session_id; - document.getElementById('start-chat').disabled = true; - document.getElementById('end-chat').disabled = false; - document.getElementById('message-input').disabled = false; - document.getElementById('send-button').disabled = false; - document.getElementById('session-info').textContent = `会话ID: ${this.sessionId}`; - document.getElementById('connection-status').className = 'badge bg-success'; - document.getElementById('connection-status').innerHTML = '已连接'; - - this.showNotification('对话已开始', 'success'); - } else { - this.showNotification('开始对话失败', 'error'); - } - } catch (error) { - console.error('开始对话失败:', error); - this.showNotification('开始对话失败', 'error'); - } - } - - async endChat() { - try { - if (!this.sessionId) return; - - const response = await fetch(`/api/chat/session/${this.sessionId}`, { - method: 'DELETE' - }); - - const data = await response.json(); - if (data.success) { - this.sessionId = null; - document.getElementById('start-chat').disabled = false; - document.getElementById('end-chat').disabled = true; - document.getElementById('message-input').disabled = true; - document.getElementById('send-button').disabled = true; - document.getElementById('session-info').textContent = '未开始对话'; - document.getElementById('connection-status').className = 'badge bg-secondary'; - document.getElementById('connection-status').innerHTML = '未连接'; - - this.showNotification('对话已结束', 'info'); - } else { - this.showNotification('结束对话失败', 'error'); - } - } catch (error) { - console.error('结束对话失败:', error); - this.showNotification('结束对话失败', 'error'); - } - } - - async sendMessage() { - const messageInput = document.getElementById('message-input'); - const message = messageInput.value.trim(); - - if (!message || !this.sessionId) return; - - // 显示用户消息 - this.addMessage('user', message); - messageInput.value = ''; - - // 显示占位提示:小奇正在查询中 - const typingId = this.showTypingIndicator(); - - // 发送消息到服务器 - try { - const response = await fetch('/api/chat/message', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - session_id: this.sessionId, - message: message - }) - }); - - const data = await response.json(); - if (data.success) { - this.updateTypingIndicator(typingId, data.response, data.knowledge_used); - } else { - this.updateTypingIndicator(typingId, '抱歉,处理您的消息时出现了错误。', null, true); - } - } catch (error) { - console.error('发送消息失败:', error); - this.updateTypingIndicator(typingId, '网络连接错误,请稍后重试。', null, true); - } - } - - showTypingIndicator() { - const messagesContainer = document.getElementById('chat-messages'); - const id = `typing-${Date.now()}`; - const messageDiv = document.createElement('div'); - messageDiv.className = 'message assistant'; - messageDiv.id = id; - const avatar = document.createElement('div'); - avatar.className = 'message-avatar'; - avatar.innerHTML = ''; - const contentDiv = document.createElement('div'); - contentDiv.className = 'message-content'; - contentDiv.innerHTML = ` -
小奇正在查询中,请稍后…
-
${new Date().toLocaleTimeString()}
- `; - messageDiv.appendChild(avatar); - messageDiv.appendChild(contentDiv); - messagesContainer.appendChild(messageDiv); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - return id; - } - - updateTypingIndicator(typingId, content, knowledgeUsed = null, isError = false) { - const node = document.getElementById(typingId); - if (!node) { - // 回退:若占位不存在则直接追加 - this.addMessage('assistant', content, knowledgeUsed, isError); - return; - } - const contentDiv = node.querySelector('.message-content'); - if (contentDiv) { - contentDiv.innerHTML = ` -
${content}
-
${new Date().toLocaleTimeString()}
- `; - if (knowledgeUsed && knowledgeUsed.length > 0) { - const knowledgeDiv = document.createElement('div'); - knowledgeDiv.className = 'knowledge-info'; - knowledgeDiv.innerHTML = ` - - 使用了知识库: ${knowledgeUsed.map(k => k.question || k.source || '实时数据').join(', ')} - `; - contentDiv.appendChild(knowledgeDiv); - } - if (isError) { - contentDiv.style.borderLeft = '4px solid #dc3545'; - } else { - contentDiv.style.borderLeft = ''; - } - } - const messagesContainer = document.getElementById('chat-messages'); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - } - - addMessage(role, content, knowledgeUsed = null, isError = false) { - const messagesContainer = document.getElementById('chat-messages'); - - // 移除欢迎消息 - const welcomeMsg = messagesContainer.querySelector('.text-center'); - if (welcomeMsg) { - welcomeMsg.remove(); - } - - const messageDiv = document.createElement('div'); - messageDiv.className = `message ${role}`; - - const avatar = document.createElement('div'); - avatar.className = 'message-avatar'; - avatar.innerHTML = role === 'user' ? '' : ''; - - const contentDiv = document.createElement('div'); - contentDiv.className = 'message-content'; - contentDiv.innerHTML = ` -
${content}
-
${new Date().toLocaleTimeString()}
- `; - - if (knowledgeUsed && knowledgeUsed.length > 0) { - const knowledgeDiv = document.createElement('div'); - knowledgeDiv.className = 'knowledge-info'; - knowledgeDiv.innerHTML = ` - - 使用了知识库: ${knowledgeUsed.map(k => k.question).join(', ')} - `; - contentDiv.appendChild(knowledgeDiv); - } - - if (isError) { - contentDiv.style.borderLeft = '4px solid #dc3545'; - } - - messageDiv.appendChild(avatar); - messageDiv.appendChild(contentDiv); - messagesContainer.appendChild(messageDiv); - - // 滚动到底部 - messagesContainer.scrollTop = messagesContainer.scrollHeight; - } - - // Agent功能 - async toggleAgentMode(enabled) { - try { - const response = await fetch('/api/agent/toggle', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ enabled }) - }); - - const data = await response.json(); - if (data.success) { - this.isAgentMode = enabled; - this.showNotification(`Agent模式已${enabled ? '启用' : '禁用'}`, 'success'); - this.loadAgentData(); - } else { - this.showNotification('切换Agent模式失败', 'error'); - } - } catch (error) { - console.error('切换Agent模式失败:', error); - this.showNotification('切换Agent模式失败', 'error'); - } - } - - async loadAgentData() { - try { - const [statusResp, toolsResp] = await Promise.all([ - fetch('/api/agent/status'), - fetch('/api/agent/tools/stats') - ]); - const data = await statusResp.json(); - const toolsData = await toolsResp.json(); - - if (data.success) { - document.getElementById('agent-current-state').textContent = data.status || '未知'; - document.getElementById('agent-active-goals').textContent = data.active_goals || 0; - const tools = (toolsData.success ? toolsData.tools : (data.tools || [])) || []; - document.getElementById('agent-available-tools').textContent = tools.length || 0; - - // 更新工具列表(使用真实统计) - this.updateToolsList(tools); - - // 更新执行历史 - this.updateAgentExecutionHistory(data.execution_history || []); - } - } catch (error) { - console.error('加载Agent数据失败:', error); - } - } - - updateToolsList(tools) { - const toolsList = document.getElementById('tools-list'); - if (!tools || tools.length === 0) { - toolsList.innerHTML = '

暂无工具

'; - return; - } - - const toolsHtml = tools.map(tool => { - const usage = tool.usage_count || 0; - const success = Math.round((tool.success_rate || 0) * 100); - const meta = tool.metadata || {}; - return ` -
-
- ${tool.name} - ${meta.description ? `
${meta.description}
` : ''} - 使用次数: ${usage} -
-
- ${success}% - -
-
- `; - }).join(''); - - toolsList.innerHTML = toolsHtml; - - // 绑定执行事件 - toolsList.querySelectorAll('button[data-tool]').forEach(btn => { - btn.addEventListener('click', async () => { - const tool = btn.getAttribute('data-tool'); - // 简单参数输入(可扩展为动态表单) - let params = {}; - try { - const input = prompt('请输入执行参数(JSON):', '{}'); - if (input) params = JSON.parse(input); - } catch (e) { - this.showNotification('参数格式错误,应为JSON', 'warning'); - return; - } - try { - const resp = await fetch('/api/agent/tools/execute', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ tool, parameters: params }) - }); - const res = await resp.json(); - if (res.success) { - this.showNotification(`工具 ${tool} 执行成功`, 'success'); - await this.loadAgentData(); - } else { - this.showNotification(res.error || `工具 ${tool} 执行失败`, 'error'); - } - } catch (err) { - console.error('执行工具失败:', err); - this.showNotification('执行工具失败: ' + err.message, 'error'); - } - }); - }); - - // 追加自定义工具注册入口 - const addDiv = document.createElement('div'); - addDiv.className = 'mt-3'; - addDiv.innerHTML = ` -
- - - -
- `; - toolsList.appendChild(addDiv); - document.getElementById('register-tool-btn').addEventListener('click', async () => { - const name = document.getElementById('custom-tool-name').value.trim(); - const description = document.getElementById('custom-tool-desc').value.trim(); - if (!name) { this.showNotification('请输入工具名称', 'warning'); return; } - try { - const resp = await fetch('/api/agent/tools/register', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name, description }) - }); - const res = await resp.json(); - if (res.success) { - this.showNotification('工具注册成功', 'success'); - this.loadAgentData(); - } else { - this.showNotification(res.error || '工具注册失败', 'error'); - } - } catch (e) { - console.error('注册工具失败:', e); - this.showNotification('注册工具失败', 'error'); - } - }); - } - - updateExecutionHistory(history) { - const historyContainer = document.getElementById('agent-execution-history'); - if (history.length === 0) { - historyContainer.innerHTML = '

暂无执行历史

'; - return; - } - - const historyHtml = history.slice(-5).map(item => ` -
-
- ${item.type || '未知任务'} - ${new Date(item.timestamp).toLocaleString()} -
-
${item.description || '无描述'}
-
- - ${item.success ? '成功' : '失败'} - -
-
- `).join(''); - - historyContainer.innerHTML = historyHtml; - } - - async startAgentMonitoring() { - try { - const response = await fetch('/api/agent/monitoring/start', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('Agent监控已启动', 'success'); - } else { - this.showNotification('启动Agent监控失败', 'error'); - } - } catch (error) { - console.error('启动Agent监控失败:', error); - this.showNotification('启动Agent监控失败', 'error'); - } - } - - async stopAgentMonitoring() { - try { - const response = await fetch('/api/agent/monitoring/stop', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('Agent监控已停止', 'success'); - } else { - this.showNotification('停止Agent监控失败', 'error'); - } - } catch (error) { - console.error('停止Agent监控失败:', error); - this.showNotification('停止Agent监控失败', 'error'); - } - } - - async proactiveMonitoring() { - try { - const response = await fetch('/api/agent/proactive-monitoring', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification(`主动监控完成,发现 ${data.proactive_actions?.length || 0} 个行动机会`, 'info'); - } else { - this.showNotification('主动监控失败', 'error'); - } - } catch (error) { - console.error('主动监控失败:', error); - this.showNotification('主动监控失败', 'error'); - } - } - - async intelligentAnalysis() { - try { - const response = await fetch('/api/agent/intelligent-analysis', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('智能分析完成', 'success'); - // 更新分析图表 - this.updateAnalyticsChart(data.analysis); - } else { - this.showNotification('智能分析失败', 'error'); - } - } catch (error) { - console.error('智能分析失败:', error); - this.showNotification('智能分析失败', 'error'); - } - } - - updateAnalyticsChart(analysis) { - if (!this.charts.analytics || !analysis) return; - - // 更新分析图表数据 - const labels = analysis.trends?.dates || []; - const satisfactionData = analysis.trends?.satisfaction || []; - const resolutionTimeData = analysis.trends?.resolution_time || []; - - this.charts.analytics.data.labels = labels; - this.charts.analytics.data.datasets[0].data = satisfactionData; - this.charts.analytics.data.datasets[1].data = resolutionTimeData; - this.charts.analytics.update(); - } - - // 预警管理 - async loadAlerts(page = 1, forceRefresh = false) { - const cacheKey = `alerts_page_${page}`; - - if (!forceRefresh && this.cache.has(cacheKey)) { - const cachedData = this.cache.get(cacheKey); - this.updateAlertsDisplay(cachedData.alerts); - this.updateAlertsPagination(cachedData); - this.updateAlertStatistics(cachedData.alerts); // 添加统计更新 - return; - } - - try { - const pageSize = this.getPageSize('alerts-pagination'); - const response = await fetch(`/api/alerts?page=${page}&per_page=${pageSize}`); - const data = await response.json(); - - this.cache.set(cacheKey, data); - this.updateAlertsDisplay(data.alerts); - this.updateAlertsPagination(data); - this.updateAlertStatistics(data.alerts); // 添加统计更新 - } catch (error) { - console.error('加载预警失败:', error); - this.showNotification('加载预警失败', 'error'); - } - } - - updateAlertsDisplay(alerts) { - const container = document.getElementById('alerts-container'); - - if (alerts.length === 0) { - container.innerHTML = ` -
- -
暂无活跃预警
-

系统运行正常,没有需要处理的预警

-
- `; - return; - } - - // 添加预警列表的批量操作头部 - const headerHtml = ` -
-
- - -
-
- -
-
- `; - - const alertsHtml = alerts.map(alert => ` -
-
-
- -
-
- ${this.getLevelText(alert.level)} - ${alert.rule_name || '未知规则'} - ${this.formatTime(alert.created_at)} -
-
${alert.message}
-
- 类型: ${this.getTypeText(alert.alert_type)} | - 级别: ${this.getLevelText(alert.level)} -
-
-
-
- -
-
-
- `).join(''); - - container.innerHTML = headerHtml + alertsHtml; - } - - updateAlertsPagination(data) { - this.createPaginationComponent(data, 'alerts-pagination', 'loadAlerts', '条预警'); - } - - updateAlertStatistics(alerts) { - // 如果传入的是分页数据,需要重新获取全部数据来计算统计 - if (alerts && alerts.length > 0) { - // 检查是否是分页数据(通常分页数据少于50条) - const pageSize = this.getPageSize('alerts-pagination'); - if (alerts.length <= pageSize) { - // 可能是分页数据,需要获取全部数据 - this.updateAlertStatisticsFromAPI(); - return; - } - } - - // 使用传入的数据计算统计 - const stats = (alerts || []).reduce((acc, alert) => { - acc[alert.level] = (acc[alert.level] || 0) + 1; - acc.total = (acc.total || 0) + 1; - return acc; - }, {}); - - document.getElementById('critical-alerts').textContent = stats.critical || 0; - document.getElementById('warning-alerts').textContent = stats.warning || 0; - document.getElementById('info-alerts').textContent = stats.info || 0; - document.getElementById('total-alerts-count').textContent = stats.total || 0; - } - - // 从API获取全部预警数据来计算统计 - async updateAlertStatisticsFromAPI() { - try { - // 获取全部预警数据(不分页) - const response = await fetch('/api/alerts?per_page=1000'); // 获取大量数据 - const data = await response.json(); - - if (data.alerts) { - const stats = data.alerts.reduce((acc, alert) => { - acc[alert.level] = (acc[alert.level] || 0) + 1; - acc.total = (acc.total || 0) + 1; - return acc; - }, {}); - - document.getElementById('critical-alerts').textContent = stats.critical || 0; - document.getElementById('warning-alerts').textContent = stats.warning || 0; - document.getElementById('info-alerts').textContent = stats.info || 0; - document.getElementById('total-alerts-count').textContent = stats.total || 0; - - // 更新缓存 - this.cache.set('alerts_stats', { - data: data.alerts, - timestamp: Date.now() - }); - } - } catch (error) { - console.error('获取全部预警统计失败:', error); - } - } - - // 预警批量删除功能 - toggleSelectAllAlerts() { - const selectAllCheckbox = document.getElementById('select-all-alerts'); - const alertCheckboxes = document.querySelectorAll('.alert-checkbox'); - - alertCheckboxes.forEach(checkbox => { - checkbox.checked = selectAllCheckbox.checked; - }); - - this.updateBatchDeleteAlertsButton(); - } - - updateBatchDeleteAlertsButton() { - const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked'); - const batchDeleteBtn = document.getElementById('batch-delete-alerts'); - - if (batchDeleteBtn) { - batchDeleteBtn.disabled = selectedCheckboxes.length === 0; - batchDeleteBtn.textContent = selectedCheckboxes.length > 0 - ? `批量删除 (${selectedCheckboxes.length})` - : '批量删除'; - } - } - - async batchDeleteAlerts() { - const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked'); - const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); - - if (selectedIds.length === 0) { - this.showNotification('请选择要删除的预警', 'warning'); - return; - } - - if (!confirm(`确定要删除选中的 ${selectedIds.length} 个预警吗?此操作不可撤销。`)) { - return; - } - - try { - const response = await fetch('/api/batch-delete/alerts', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ ids: selectedIds }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - - // 清除所有相关缓存 - this.cache.delete('alerts'); - this.cache.delete('alerts_stats'); - - // 立即更新统计数字,避免跳动 - await this.updateStatsAfterDelete(selectedIds.length); - - // 重新加载预警列表 - await this.loadAlerts(); - - // 重置批量删除按钮状态 - this.updateBatchDeleteAlertsButton(); - } else { - this.showNotification(data.error || '批量删除失败', 'error'); - } - } catch (error) { - console.error('批量删除预警失败:', error); - this.showNotification('批量删除预警失败', 'error'); - } - } - - // 删除后更新统计数字(平滑更新) - async updateStatsAfterDelete(deletedCount) { - try { - // 直接调用API获取最新统计,不依赖页面显示数据 - await this.updateAlertStatisticsFromAPI(); - - // 更新最后更新时间,避免智能更新机制干扰 - this.lastUpdateTimes.alerts = Date.now(); - - } catch (error) { - console.error('更新删除后统计失败:', error); - // 如果计算失败,直接刷新 - await this.refreshAlertStats(); - } - } - - // 获取当前显示的预警数据 - getCurrentDisplayedAlerts() { - const alertElements = document.querySelectorAll('.alert-item'); - const alerts = []; - - alertElements.forEach(element => { - const level = element.querySelector('.alert-level')?.textContent?.trim(); - if (level) { - alerts.push({ level: level.toLowerCase() }); - } - }); - - return alerts; - } - - // 计算预警统计 - calculateAlertStats(alerts) { - const stats = { critical: 0, warning: 0, info: 0, total: 0 }; - - alerts.forEach(alert => { - const level = alert.level.toLowerCase(); - if (stats.hasOwnProperty(level)) { - stats[level]++; - } - stats.total++; - }); - - return stats; - } - - // 平滑更新预警统计数字 - smoothUpdateAlertStats(newStats) { - // 获取当前显示的数字 - const currentCritical = parseInt(document.getElementById('critical-alerts')?.textContent || '0'); - const currentWarning = parseInt(document.getElementById('warning-alerts')?.textContent || '0'); - const currentInfo = parseInt(document.getElementById('info-alerts')?.textContent || '0'); - const currentTotal = parseInt(document.getElementById('total-alerts-count')?.textContent || '0'); - - // 平滑过渡到新数字 - this.animateNumberChange('critical-alerts', currentCritical, newStats.critical); - this.animateNumberChange('warning-alerts', currentWarning, newStats.warning); - this.animateNumberChange('info-alerts', currentInfo, newStats.info); - this.animateNumberChange('total-alerts-count', currentTotal, newStats.total); - } - - // 数字变化动画 - animateNumberChange(elementId, from, to) { - const element = document.getElementById(elementId); - if (!element) return; - - const duration = 300; // 300ms动画 - const startTime = Date.now(); - - const animate = () => { - const elapsed = Date.now() - startTime; - const progress = Math.min(elapsed / duration, 1); - - // 使用缓动函数 - const easeOut = 1 - Math.pow(1 - progress, 3); - const currentValue = Math.round(from + (to - from) * easeOut); - - element.textContent = currentValue; - - if (progress < 1) { - requestAnimationFrame(animate); - } - }; - - requestAnimationFrame(animate); - } - - // 解决预警后更新统计数字 - async updateStatsAfterResolve(alertId) { - try { - // 直接调用API获取最新统计,不依赖页面显示数据 - await this.updateAlertStatisticsFromAPI(); - - // 更新最后更新时间,避免智能更新机制干扰 - this.lastUpdateTimes.alerts = Date.now(); - - } catch (error) { - console.error('更新解决后统计失败:', error); - // 如果计算失败,直接刷新 - await this.refreshAlertStats(); - } - } - - async resolveAlert(alertId) { - try { - const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('预警已解决', 'success'); - - // 清除缓存 - this.cache.delete('alerts'); - this.cache.delete('alerts_stats'); - - // 立即更新统计数字 - await this.updateStatsAfterResolve(alertId); - - // 重新加载预警列表 - this.loadAlerts(); - } else { - this.showNotification('解决预警失败', 'error'); - } - } catch (error) { - console.error('解决预警失败:', error); - this.showNotification('解决预警失败', 'error'); - } - } - - // 知识库管理 - async loadKnowledge(page = 1) { - try { - const pageSize = this.getPageSize('knowledge-pagination'); - const response = await fetch(`/api/knowledge?page=${page}&per_page=${pageSize}`); - const data = await response.json(); - - if (data.knowledge) { - this.updateKnowledgeDisplay(data.knowledge); - this.updateKnowledgePagination(data); - } else { - // 兼容旧格式 - this.updateKnowledgeDisplay(data); - } - } catch (error) { - console.error('加载知识库失败:', error); - } - } - - updateKnowledgeDisplay(knowledge) { - const container = document.getElementById('knowledge-list'); - - if (knowledge.length === 0) { - container.innerHTML = '

暂无知识条目

'; - return; - } - - // 添加知识库列表的批量操作头部 - const headerHtml = ` -
-
- - -
-
- -
-
- `; - - const knowledgeHtml = knowledge.map(item => ` -
-
-
- -
-
${item.question}
-

${item.answer}

-
- 分类: ${item.category} - 置信度: ${Math.round(item.confidence_score * 100)}% - 使用次数: ${item.usage_count || 0} - - ${item.is_verified ? '已验证' : '未验证'} - -
-
-
-
-
- ${item.is_verified ? - `` : - `` - } - -
-
-
-
- `).join(''); - - container.innerHTML = headerHtml + knowledgeHtml; - } - - updateKnowledgePagination(data) { - this.createPaginationComponent(data, 'knowledge-pagination', 'loadKnowledge', '条知识'); - } - - async searchKnowledge() { - const query = document.getElementById('knowledge-search').value.trim(); - if (!query) { - this.loadKnowledge(); - return; - } - - try { - const response = await fetch(`/api/knowledge/search?q=${encodeURIComponent(query)}`); - const results = await response.json(); - this.updateKnowledgeDisplay(results); - } catch (error) { - console.error('搜索知识库失败:', error); - } - } - - async addKnowledge() { - const question = document.getElementById('knowledge-question').value.trim(); - const answer = document.getElementById('knowledge-answer').value.trim(); - const category = document.getElementById('knowledge-category').value; - const confidence = parseFloat(document.getElementById('knowledge-confidence').value); - - if (!question || !answer) { - this.showNotification('请填写完整信息', 'warning'); - return; - } - - try { - const response = await fetch('/api/knowledge', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - question, - answer, - category, - confidence_score: confidence - }) - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('知识添加成功', 'success'); - bootstrap.Modal.getInstance(document.getElementById('addKnowledgeModal')).hide(); - document.getElementById('knowledge-form').reset(); - this.loadKnowledge(); - } else { - this.showNotification('添加知识失败', 'error'); - } - } catch (error) { - console.error('添加知识失败:', error); - this.showNotification('添加知识失败', 'error'); - } - } - - async uploadFile() { - const fileInput = document.getElementById('file-input'); - const processMethod = document.getElementById('process-method').value; - const category = document.getElementById('file-category').value; - const confidence = parseFloat(document.getElementById('file-confidence').value); - const manualQuestion = document.getElementById('manual-question').value.trim(); - - if (!fileInput.files[0]) { - this.showNotification('请选择文件', 'warning'); - return; - } - - if (processMethod === 'manual' && !manualQuestion) { - this.showNotification('请指定问题', 'warning'); - return; - } - - // 显示进度条 - const progressDiv = document.getElementById('upload-progress'); - const progressBar = progressDiv.querySelector('.progress-bar'); - const statusText = document.getElementById('upload-status'); - - progressDiv.style.display = 'block'; - progressBar.style.width = '0%'; - statusText.textContent = '正在上传文件...'; - - try { - const formData = new FormData(); - formData.append('file', fileInput.files[0]); - formData.append('process_method', processMethod); - formData.append('category', category); - formData.append('confidence_score', confidence); - if (manualQuestion) { - formData.append('manual_question', manualQuestion); - } - - // 模拟进度更新 - let progress = 0; - const progressInterval = setInterval(() => { - progress += Math.random() * 20; - if (progress > 90) progress = 90; - progressBar.style.width = progress + '%'; - - if (progress < 30) { - statusText.textContent = '正在上传文件...'; - } else if (progress < 60) { - statusText.textContent = '正在解析文件内容...'; - } else if (progress < 90) { - statusText.textContent = '正在生成知识库...'; - } - }, 500); - - const response = await fetch('/api/knowledge/upload', { - method: 'POST', - body: formData - }); - - clearInterval(progressInterval); - progressBar.style.width = '100%'; - statusText.textContent = '处理完成!'; - - const data = await response.json(); - - setTimeout(() => { - progressDiv.style.display = 'none'; - - if (data.success) { - this.showNotification(`文件处理成功,生成了 ${data.knowledge_count || 0} 条知识`, 'success'); - bootstrap.Modal.getInstance(document.getElementById('uploadFileModal')).hide(); - document.getElementById('file-upload-form').reset(); - this.loadKnowledge(); - } else { - this.showNotification(data.error || '文件处理失败', 'error'); - } - }, 1000); - - } catch (error) { - console.error('文件上传失败:', error); - progressDiv.style.display = 'none'; - this.showNotification('文件上传失败', 'error'); - } - } - - async verifyKnowledge(knowledgeId) { - try { - const response = await fetch(`/api/knowledge/verify/${knowledgeId}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - verified_by: 'admin' - }) - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('知识库验证成功', 'success'); - this.loadKnowledge(); - } else { - this.showNotification('知识库验证失败', 'error'); - } - } catch (error) { - console.error('验证知识库失败:', error); - this.showNotification('验证知识库失败', 'error'); - } - } - - async unverifyKnowledge(knowledgeId) { - try { - const response = await fetch(`/api/knowledge/unverify/${knowledgeId}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('取消验证成功', 'success'); - this.loadKnowledge(); - } else { - this.showNotification('取消验证失败', 'error'); - } - } catch (error) { - console.error('取消验证失败:', error); - this.showNotification('取消验证失败', 'error'); - } - } - - async deleteKnowledge(knowledgeId) { - if (!confirm('确定要删除这条知识库条目吗?')) { - return; - } - - try { - const response = await fetch(`/api/knowledge/delete/${knowledgeId}`, { - method: 'DELETE' - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('知识库删除成功', 'success'); - this.loadKnowledge(); - } else { - this.showNotification('知识库删除失败', 'error'); - } - } catch (error) { - console.error('删除知识库失败:', error); - this.showNotification('删除知识库失败', 'error'); - } - } - - // 知识库批量删除功能 - toggleSelectAllKnowledge() { - const selectAllCheckbox = document.getElementById('select-all-knowledge'); - const knowledgeCheckboxes = document.querySelectorAll('.knowledge-checkbox'); - - knowledgeCheckboxes.forEach(checkbox => { - checkbox.checked = selectAllCheckbox.checked; - }); - - this.updateBatchDeleteKnowledgeButton(); - } - - updateBatchDeleteKnowledgeButton() { - const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked'); - const batchDeleteBtn = document.getElementById('batch-delete-knowledge'); - - if (batchDeleteBtn) { - batchDeleteBtn.disabled = selectedCheckboxes.length === 0; - batchDeleteBtn.textContent = selectedCheckboxes.length > 0 - ? `批量删除 (${selectedCheckboxes.length})` - : '批量删除'; - } - } - - async batchDeleteKnowledge() { - const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked'); - const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); - - if (selectedIds.length === 0) { - this.showNotification('请选择要删除的知识库条目', 'warning'); - return; - } - - if (!confirm(`确定要删除选中的 ${selectedIds.length} 个知识库条目吗?此操作不可撤销。`)) { - return; - } - - try { - const response = await fetch('/api/batch-delete/knowledge', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ ids: selectedIds }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - - // 清除缓存并强制刷新 - this.cache.delete('knowledge'); - await this.loadKnowledge(); - - // 重置批量删除按钮状态 - this.updateBatchDeleteKnowledgeButton(); - } else { - this.showNotification(data.error || '批量删除失败', 'error'); - } - } catch (error) { - console.error('批量删除知识库条目失败:', error); - this.showNotification('批量删除知识库条目失败', 'error'); - } - } - - // 工单管理 - async loadWorkOrders(page = 1, forceRefresh = false) { - const cacheKey = `workorders_page_${page}`; - - if (!forceRefresh && this.cache.has(cacheKey)) { - const cachedData = this.cache.get(cacheKey); - this.updateWorkOrdersDisplay(cachedData.workorders); - this.updateWorkOrdersPagination(cachedData); - // 不在这里更新统计,因为分页数据不完整 - return; - } - - try { - const statusFilter = document.getElementById('workorder-status-filter')?.value || 'all'; - const priorityFilter = document.getElementById('workorder-priority-filter')?.value || 'all'; - - let url = '/api/workorders'; - const params = new URLSearchParams(); - params.append('page', page); - const pageSize = this.getPageSize('workorders-pagination'); - params.append('per_page', pageSize.toString()); - if (statusFilter !== 'all') params.append('status', statusFilter); - if (priorityFilter !== 'all') params.append('priority', priorityFilter); - - // 添加强制刷新参数 - if (forceRefresh) { - params.append('_t', Date.now().toString()); - } - - if (params.toString()) url += '?' + params.toString(); - - const response = await fetch(url, { - cache: forceRefresh ? 'no-cache' : 'default', - headers: forceRefresh ? { - 'Cache-Control': 'no-cache', - 'Pragma': 'no-cache' - } : {} - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); - this.updateWorkOrdersDisplay(data.workorders); - this.updateWorkOrdersPagination(data); - // 不在这里更新统计,因为分页数据不完整 - - // 更新缓存 - this.cache.set(cacheKey, data); - - } catch (error) { - console.error('加载工单失败:', error); - this.showNotification('加载工单失败: ' + error.message, 'error'); - } - } - - updateWorkOrdersDisplay(workorders) { - const container = document.getElementById('workorders-list'); - - if (workorders.length === 0) { - container.innerHTML = '

暂无工单

'; - return; - } - - // 添加工单列表的批量操作头部 - const headerHtml = ` -
-
- - -
-
- -
-
- `; - - const workordersHtml = workorders.map(workorder => ` -
-
-
- -
-
${workorder.title}
-

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

-
- ${this.getPriorityText(workorder.priority)} - ${this.getStatusText(workorder.status)} - 分类: ${workorder.category} - 创建时间: ${new Date(workorder.created_at).toLocaleString()} -
-
-
-
-
- - - -
-
-
-
- `).join(''); - - container.innerHTML = headerHtml + workordersHtml; - } - - updateWorkOrdersPagination(data) { - this.createPaginationComponent(data, 'workorders-pagination', 'loadWorkOrders', '个工单'); - } - - updateWorkOrderStatistics(workorders) { - const stats = workorders.reduce((acc, wo) => { - acc.total = (acc.total || 0) + 1; - acc[wo.status] = (acc[wo.status] || 0) + 1; - return acc; - }, {}); - - // 状态映射 - const statusMapping = { - 'open': ['open', '待处理', '新建', 'new'], - 'in_progress': ['in_progress', '处理中', '进行中', 'progress', 'processing'], - 'resolved': ['resolved', '已解决', '已完成'], - 'closed': ['closed', '已关闭', '关闭'] - }; - - // 统计各状态的数量 - const mapped_counts = {'open': 0, 'in_progress': 0, 'resolved': 0, 'closed': 0}; - - for (const [status, count] of Object.entries(stats)) { - if (status === 'total') continue; - - const status_lower = String(status).toLowerCase(); - let mapped = false; - - for (const [mapped_status, possible_values] of Object.entries(statusMapping)) { - if (possible_values.some(v => v.toLowerCase() === status_lower)) { - mapped_counts[mapped_status] += count; - mapped = true; - break; - } - } - - if (!mapped) { - console.warn(`未映射的状态: '${status}' (数量: ${count})`); - } - } - - document.getElementById('workorders-total').textContent = stats.total || 0; - document.getElementById('workorders-open').textContent = mapped_counts['open']; - document.getElementById('workorders-progress').textContent = mapped_counts['in_progress']; - document.getElementById('workorders-resolved').textContent = mapped_counts['resolved']; - - console.log('工单统计更新:', { - total: stats.total, - open: mapped_counts['open'], - in_progress: mapped_counts['in_progress'], - resolved: mapped_counts['resolved'], - closed: mapped_counts['closed'] - }); - } - - // 工单批量删除功能 - toggleSelectAllWorkorders() { - const selectAllCheckbox = document.getElementById('select-all-workorders'); - const workorderCheckboxes = document.querySelectorAll('.workorder-checkbox'); - - workorderCheckboxes.forEach(checkbox => { - checkbox.checked = selectAllCheckbox.checked; - }); - - this.updateBatchDeleteButton(); - } - - updateBatchDeleteButton() { - const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked'); - const batchDeleteBtn = document.getElementById('batch-delete-workorders'); - - if (batchDeleteBtn) { - batchDeleteBtn.disabled = selectedCheckboxes.length === 0; - batchDeleteBtn.textContent = selectedCheckboxes.length > 0 - ? `批量删除 (${selectedCheckboxes.length})` - : '批量删除'; - } - } - - async batchDeleteWorkorders() { - const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked'); - const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value)); - - if (selectedIds.length === 0) { - this.showNotification('请选择要删除的工单', 'warning'); - return; - } - - if (!confirm(`确定要删除选中的 ${selectedIds.length} 个工单吗?此操作不可撤销。`)) { - return; - } - - try { - const response = await fetch('/api/batch-delete/workorders', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ ids: selectedIds }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - - // 清除缓存并强制刷新 - this.cache.delete('workorders'); - await this.loadWorkOrders(true); // 强制刷新 - await this.loadAnalytics(); - - // 重置批量删除按钮状态 - this.updateBatchDeleteButton(); - } else { - this.showNotification(data.error || '批量删除失败', 'error'); - } - } catch (error) { - console.error('批量删除工单失败:', error); - this.showNotification('批量删除工单失败', 'error'); - } - } - - async createWorkOrder() { - const title = document.getElementById('wo-title').value.trim(); - const description = document.getElementById('wo-description').value.trim(); - const category = document.getElementById('wo-category').value; - const priority = document.getElementById('wo-priority').value; - - if (!title || !description) { - this.showNotification('请填写完整信息', 'warning'); - return; - } - - try { - const response = await fetch('/api/workorders', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title, - description, - category, - priority - }) - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('工单创建成功', 'success'); - bootstrap.Modal.getInstance(document.getElementById('createWorkOrderModal')).hide(); - document.getElementById('work-order-form').reset(); - // 立即刷新工单列表和统计 - await this.loadWorkOrders(); - await this.loadAnalytics(); - } else { - this.showNotification('创建工单失败: ' + (data.error || '未知错误'), 'error'); - } - } catch (error) { - console.error('创建工单失败:', error); - this.showNotification('创建工单失败', 'error'); - } - } - - async viewWorkOrderDetails(workorderId) { - try { - const response = await fetch(`/api/workorders/${workorderId}`); - const workorder = await response.json(); - - if (workorder.error) { - this.showNotification('获取工单详情失败', 'error'); - return; - } - - this.showWorkOrderDetailsModal(workorder); - } catch (error) { - console.error('获取工单详情失败:', error); - this.showNotification('获取工单详情失败', 'error'); - } - } - - showWorkOrderDetailsModal(workorder) { - // 创建模态框HTML - const modalHtml = ` - - `; - - // 移除已存在的模态框 - const existingModal = document.getElementById('workOrderDetailsModal'); - if (existingModal) { - existingModal.remove(); - } - - // 添加新的模态框到页面 - document.body.insertAdjacentHTML('beforeend', modalHtml); - - // 显示模态框 - const modal = new bootstrap.Modal(document.getElementById('workOrderDetailsModal')); - modal.show(); - - // 模态框关闭时移除DOM元素 - document.getElementById('workOrderDetailsModal').addEventListener('hidden.bs.modal', function() { - this.remove(); - }); - } - - async updateWorkOrder(workorderId) { - try { - // 获取工单详情 - const response = await fetch(`/api/workorders/${workorderId}`); - const workorder = await response.json(); - - if (workorder.id) { - this.showEditWorkOrderModal(workorder); - } else { - throw new Error(workorder.error || '获取工单详情失败'); - } - } catch (error) { - console.error('获取工单详情失败:', error); - this.showNotification('获取工单详情失败: ' + error.message, 'error'); - } - } - - async deleteWorkOrder(workorderId) { - console.log('deleteWorkOrder called with ID:', workorderId); - - if (!confirm('确定要删除这个工单吗?此操作不可撤销。')) { - console.log('用户取消了删除操作'); - return; - } - - try { - console.log('发送删除请求到:', `/api/workorders/${workorderId}`); - const response = await fetch(`/api/workorders/${workorderId}`, { - method: 'DELETE' - }); - - console.log('删除响应状态:', response.status); - const data = await response.json(); - console.log('删除响应数据:', data); - - if (data.success) { - this.showNotification('工单删除成功', 'success'); - // 立即刷新工单列表和统计 - await this.loadWorkOrders(); - await this.loadAnalytics(); - } else { - this.showNotification('删除工单失败: ' + (data.error || '未知错误'), 'error'); - } - } catch (error) { - console.error('删除工单失败:', error); - this.showNotification('删除工单失败: ' + error.message, 'error'); - } - } - - showEditWorkOrderModal(workorder) { - // 创建编辑工单模态框 - const modalHtml = ` - - `; - - // 移除已存在的模态框 - const existingModal = document.getElementById('editWorkOrderModal'); - if (existingModal) { - existingModal.remove(); - } - - // 添加新模态框到页面 - document.body.insertAdjacentHTML('beforeend', modalHtml); - - // 显示模态框 - const modal = new bootstrap.Modal(document.getElementById('editWorkOrderModal')); - modal.show(); - - // 模态框关闭时清理 - document.getElementById('editWorkOrderModal').addEventListener('hidden.bs.modal', function() { - this.remove(); - }); - } - - async saveWorkOrder(workorderId) { - try { - // 获取表单数据 - const formData = { - title: document.getElementById('editTitle').value, - description: document.getElementById('editDescription').value, - category: document.getElementById('editCategory').value, - priority: document.getElementById('editPriority').value, - status: document.getElementById('editStatus').value, - resolution: document.getElementById('editResolution').value, - satisfaction_score: parseInt(document.getElementById('editSatisfactionScore').value) || null - }; - - // 验证必填字段 - if (!formData.title.trim() || !formData.description.trim()) { - this.showNotification('标题和描述不能为空', 'error'); - return; - } - - // 发送更新请求 - const response = await fetch(`/api/workorders/${workorderId}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(formData) - }); - - const result = await response.json(); - - if (result.success) { - this.showNotification('工单更新成功', 'success'); - // 关闭模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('editWorkOrderModal')); - modal.hide(); - // 刷新工单列表和统计 - await this.loadWorkOrders(); - await this.loadAnalytics(); - } else { - throw new Error(result.error || '更新工单失败'); - } - } catch (error) { - console.error('更新工单失败:', error); - this.showNotification('更新工单失败: ' + error.message, 'error'); - } - } - - // 工单导入功能 - showImportModal() { - // 显示导入模态框 - const modal = new bootstrap.Modal(document.getElementById('importWorkOrderModal')); - modal.show(); - - // 重置表单 - document.getElementById('excel-file-input').value = ''; - document.getElementById('import-progress').classList.add('d-none'); - document.getElementById('import-result').classList.add('d-none'); - } - - async downloadTemplate() { - try { - // 直接请求文件接口,避免浏览器跨源/权限限制 - const resp = await fetch('/api/workorders/import/template/file'); - if (!resp.ok) throw new Error('下载接口返回错误'); - const blob = await resp.blob(); - const blobUrl = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = blobUrl; - a.download = '工单导入模板.xlsx'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(blobUrl); - this.showNotification('模板下载成功', 'success'); - } catch (error) { - console.error('下载模板失败:', error); - this.showNotification('下载模板失败: ' + error.message, 'error'); - } - } - - async importWorkOrders() { - const fileInput = document.getElementById('excel-file-input'); - const file = fileInput.files[0]; - - if (!file) { - this.showNotification('请选择要导入的Excel文件', 'error'); - return; - } - - // 验证文件类型 - if (!file.name.match(/\.(xlsx|xls)$/)) { - this.showNotification('只支持Excel文件(.xlsx, .xls)', 'error'); - return; - } - - // 验证文件大小 - if (file.size > 16 * 1024 * 1024) { - this.showNotification('文件大小不能超过16MB', 'error'); - return; - } - - // 显示进度条 - document.getElementById('import-progress').classList.remove('d-none'); - document.getElementById('import-result').classList.add('d-none'); - - try { - // 创建FormData - const formData = new FormData(); - formData.append('file', file); - - // 发送导入请求 - const response = await fetch('/api/workorders/import', { - method: 'POST', - body: formData - }); - - const result = await response.json(); - - if (result.success) { - // 显示成功消息 - document.getElementById('import-progress').classList.add('d-none'); - document.getElementById('import-result').classList.remove('d-none'); - document.getElementById('import-success-message').textContent = - `成功导入 ${result.imported_count} 个工单`; - - this.showNotification(result.message, 'success'); - - // 刷新工单列表 - this.loadWorkOrders(); - - // 3秒后关闭模态框 - setTimeout(() => { - const modal = bootstrap.Modal.getInstance(document.getElementById('importWorkOrderModal')); - modal.hide(); - }, 3000); - - } else { - throw new Error(result.error || '导入工单失败'); - } - - } catch (error) { - console.error('导入工单失败:', error); - document.getElementById('import-progress').classList.add('d-none'); - this.showNotification('导入工单失败: ' + error.message, 'error'); - } - } - - // 对话历史管理 - async loadConversationHistory(page = 1) { - try { - const pageSize = this.getPageSize('conversations-pagination'); - const response = await fetch(`/api/conversations?page=${page}&per_page=${pageSize}`); - const data = await response.json(); - - if (data.conversations) { - this.renderConversationList(data.conversations || []); - this.updateConversationPagination(data); - this.updateConversationStats(data.stats || {}); - } else { - throw new Error(data.error || '加载对话历史失败'); - } - } catch (error) { - console.error('加载对话历史失败:', error); - this.showNotification('加载对话历史失败: ' + error.message, 'error'); - } - } - - renderConversationList(conversations) { - const container = document.getElementById('conversation-list'); - if (!conversations || conversations.length === 0) { - container.innerHTML = ` -
- -

暂无对话记录

-
- `; - return; - } - - const html = conversations.map(conv => ` -
-
-
-
-
用户: ${conv.user_id || '匿名'}
- ${new Date(conv.timestamp).toLocaleString()} -
-
- - -
-
-
-

用户: ${conv.user_message?.substring(0, 100)}${conv.user_message?.length > 100 ? '...' : ''}

-

助手: ${conv.assistant_response?.substring(0, 100)}${conv.assistant_response?.length > 100 ? '...' : ''}

-
-
- 响应时间: ${conv.response_time || 0}ms - ${conv.work_order_id ? `工单: ${conv.work_order_id}` : ''} -
-
-
- `).join(''); - - container.innerHTML = html; - } - - updateConversationPagination(data) { - this.createPaginationComponent(data, 'conversations-pagination', 'loadConversationHistory', '条对话'); - } - - updateConversationStats(stats) { - document.getElementById('conversation-total').textContent = stats.total || 0; - document.getElementById('conversation-today').textContent = stats.today || 0; - document.getElementById('conversation-avg-response').textContent = `${stats.avg_response_time || 0}ms`; - document.getElementById('conversation-active-users').textContent = stats.active_users || 0; - } - - async refreshConversationHistory() { - // 先尝试触发一次合并迁移(幂等,重复调用也安全) - try { - await fetch('/api/conversations/migrate-merge', { method: 'POST' }); - } catch (e) { /* 忽略迁移失败 */ } - await this.loadConversationHistory(); - this.showNotification('对话历史已刷新', 'success'); - } - - async clearAllConversations() { - if (!confirm('确定要清空所有对话历史吗?此操作不可恢复!')) { - return; - } - - try { - const response = await fetch('/api/conversations/clear', { method: 'DELETE' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('对话历史已清空', 'success'); - await this.loadConversationHistory(); - } else { - throw new Error(data.error || '清空对话历史失败'); - } - } catch (error) { - console.error('清空对话历史失败:', error); - this.showNotification('清空对话历史失败: ' + error.message, 'error'); - } - } - - async deleteConversation(conversationId) { - if (!confirm('确定要删除这条对话记录吗?')) { - return; - } - - try { - const response = await fetch(`/api/conversations/${conversationId}`, { method: 'DELETE' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('对话记录已删除', 'success'); - await this.loadConversationHistory(); - } else { - throw new Error(data.error || '删除对话记录失败'); - } - } catch (error) { - console.error('删除对话记录失败:', error); - this.showNotification('删除对话记录失败: ' + error.message, 'error'); - } - } - - async viewConversation(conversationId) { - try { - const response = await fetch(`/api/conversations/${conversationId}`); - const data = await response.json(); - - if (data.success) { - data.user_id = data.user_id || '匿名'; - this.showConversationModal(data); - } else { - throw new Error(data.error || '获取对话详情失败'); - } - } catch (error) { - console.error('获取对话详情失败:', error); - this.showNotification('获取对话详情失败: ' + error.message, 'error'); - } - } - - showConversationModal(conversation) { - const modalHtml = ` - - `; - - // 移除已存在的模态框 - const existingModal = document.getElementById('conversationModal'); - if (existingModal) { - existingModal.remove(); - } - - // 添加新模态框 - document.body.insertAdjacentHTML('beforeend', modalHtml); - - // 显示模态框 - const modal = new bootstrap.Modal(document.getElementById('conversationModal')); - modal.show(); - } - - async filterConversations() { - const search = document.getElementById('conversation-search').value; - const userFilter = document.getElementById('conversation-user-filter').value; - const dateFilter = document.getElementById('conversation-date-filter').value; - - try { - const params = new URLSearchParams(); - if (search) params.append('search', search); - if (userFilter) params.append('user_id', userFilter); - if (dateFilter) params.append('date_filter', dateFilter); - - const response = await fetch(`/api/conversations?${params.toString()}`); - const data = await response.json(); - - if (data.success) { - this.renderConversationList(data.conversations || []); - this.renderConversationPagination(data.pagination || {}); - } else { - throw new Error(data.error || '筛选对话失败'); - } - } catch (error) { - console.error('筛选对话失败:', error); - this.showNotification('筛选对话失败: ' + error.message, 'error'); - } - } - - // Token监控 - async loadTokenMonitor() { - try { - const response = await fetch('/api/token-monitor/stats'); - const data = await response.json(); - - if (data.success) { - this.updateTokenStats(data); - this.loadTokenChart(); - this.loadTokenRecords(); - } else { - throw new Error(data.error || '加载Token监控数据失败'); - } - } catch (error) { - console.error('加载Token监控数据失败:', error); - this.showNotification('加载Token监控数据失败: ' + error.message, 'error'); - } - } - - updateTokenStats(stats) { - document.getElementById('token-today').textContent = stats.today_tokens || 0; - document.getElementById('token-month').textContent = stats.month_tokens || 0; - document.getElementById('token-cost').textContent = `¥${stats.total_cost || 0}`; - document.getElementById('token-budget').textContent = `¥${stats.budget_limit || 1000}`; - } - - async loadTokenChart() { - try { - const response = await fetch('/api/token-monitor/chart'); - const data = await response.json(); - - if (data.success) { - this.renderTokenChart(data); - } - } catch (error) { - console.error('加载Token图表失败:', error); - } - } - - renderTokenChart(data) { - const ctx = document.getElementById('tokenChart').getContext('2d'); - - if (this.charts.tokenChart) { - this.charts.tokenChart.destroy(); - } - - this.charts.tokenChart = new Chart(ctx, { - type: 'line', - data: { - labels: data.labels || [], - datasets: [{ - label: 'Token消耗', - data: data.tokens || [], - borderColor: '#007bff', - backgroundColor: 'rgba(0, 123, 255, 0.1)', - tension: 0.4 - }, { - label: '成本', - data: data.costs || [], - borderColor: '#28a745', - backgroundColor: 'rgba(40, 167, 69, 0.1)', - tension: 0.4, - yAxisID: 'y1' - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - scales: { - y: { - type: 'linear', - display: true, - position: 'left', - title: { - display: true, - text: 'Token数量' - } - }, - y1: { - type: 'linear', - display: true, - position: 'right', - title: { - display: true, - text: '成本 (元)' - }, - grid: { - drawOnChartArea: false, - }, - } - } - } - }); - } - - async loadTokenRecords() { - try { - const response = await fetch('/api/token-monitor/records'); - const data = await response.json(); - - if (data.success) { - this.renderTokenRecords(data.records || []); - } - } catch (error) { - console.error('加载Token记录失败:', error); - } - } - - renderTokenRecords(records) { - const tbody = document.getElementById('token-records'); - - if (!records || records.length === 0) { - tbody.innerHTML = ` - - 暂无记录 - - `; - return; - } - - const html = records.map(record => ` - - ${new Date(record.timestamp).toLocaleString()} - ${record.user_id || '匿名'} - ${record.model || 'qwen-turbo'} - ${record.input_tokens || 0} - ${record.output_tokens || 0} - ${record.total_tokens || 0} - ¥${record.cost || 0} - ${record.response_time || 0}ms - - `).join(''); - - tbody.innerHTML = html; - } - - async saveTokenSettings() { - const dailyThreshold = document.getElementById('daily-threshold').value; - const monthlyBudget = document.getElementById('monthly-budget').value; - const enableAlerts = document.getElementById('enable-alerts').checked; - - try { - const response = await fetch('/api/token-monitor/settings', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - daily_threshold: parseInt(dailyThreshold), - monthly_budget: parseFloat(monthlyBudget), - enable_alerts: enableAlerts - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('Token设置已保存', 'success'); - } else { - throw new Error(data.error || '保存Token设置失败'); - } - } catch (error) { - console.error('保存Token设置失败:', error); - this.showNotification('保存Token设置失败: ' + error.message, 'error'); - } - } - - async updateTokenChart(period) { - // 更新按钮状态 - document.querySelectorAll('#tokenChart').forEach(btn => { - btn.classList.remove('active'); - }); - event.target.classList.add('active'); - - try { - const response = await fetch(`/api/token-monitor/chart?period=${period}`); - const data = await response.json(); - - if (data.success) { - this.renderTokenChart(data); - } - } catch (error) { - console.error('更新Token图表失败:', error); - } - } - - async exportTokenData() { - try { - const response = await fetch('/api/token-monitor/export'); - const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'token_usage_data.xlsx'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(url); - this.showNotification('Token数据导出成功', 'success'); - } catch (error) { - console.error('导出Token数据失败:', error); - this.showNotification('导出Token数据失败: ' + error.message, 'error'); - } - } - - async refreshTokenData() { - await this.loadTokenMonitor(); - this.showNotification('Token数据已刷新', 'success'); - } - - // AI监控 - async loadAIMonitor() { - try { - const response = await fetch('/api/ai-monitor/stats'); - const data = await response.json(); - - if (data.success) { - this.updateAIStats(data); - this.loadModelComparisonChart(); - this.loadErrorDistributionChart(); - this.loadErrorLog(); - } else { - throw new Error(data.error || '加载AI监控数据失败'); - } - } catch (error) { - console.error('加载AI监控数据失败:', error); - this.showNotification('加载AI监控数据失败: ' + error.message, 'error'); - } - } - - updateAIStats(stats) { - document.getElementById('ai-success-rate').textContent = `${stats.success_rate || 0}%`; - document.getElementById('ai-response-time').textContent = `${stats.avg_response_time || 0}ms`; - document.getElementById('ai-error-rate').textContent = `${stats.error_rate || 0}%`; - document.getElementById('ai-total-calls').textContent = stats.total_calls || 0; - } - - async loadModelComparisonChart() { - try { - const response = await fetch('/api/ai-monitor/model-comparison'); - const data = await response.json(); - - if (data.success) { - this.renderModelComparisonChart(data); - } - } catch (error) { - console.error('加载模型对比图表失败:', error); - } - } - - renderModelComparisonChart(data) { - const ctx = document.getElementById('modelComparisonChart').getContext('2d'); - - if (this.charts.modelComparisonChart) { - this.charts.modelComparisonChart.destroy(); - } - - this.charts.modelComparisonChart = new Chart(ctx, { - type: 'bar', - data: { - labels: data.models || [], - datasets: [{ - label: '成功率 (%)', - data: data.success_rates || [], - backgroundColor: 'rgba(40, 167, 69, 0.8)' - }, { - label: '平均响应时间 (ms)', - data: data.response_times || [], - backgroundColor: 'rgba(255, 193, 7, 0.8)', - yAxisID: 'y1' - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - scales: { - y: { - type: 'linear', - display: true, - position: 'left', - title: { - display: true, - text: '成功率 (%)' - } - }, - y1: { - type: 'linear', - display: true, - position: 'right', - title: { - display: true, - text: '响应时间 (ms)' - }, - grid: { - drawOnChartArea: false, - }, - } - } - } - }); - } - - async loadErrorDistributionChart() { - try { - const response = await fetch('/api/ai-monitor/error-distribution'); - const data = await response.json(); - - if (data.success) { - this.renderErrorDistributionChart(data); - } - } catch (error) { - console.error('加载错误分布图表失败:', error); - } - } - - renderErrorDistributionChart(data) { - const ctx = document.getElementById('errorDistributionChart').getContext('2d'); - - if (this.charts.errorDistributionChart) { - this.charts.errorDistributionChart.destroy(); - } - - this.charts.errorDistributionChart = new Chart(ctx, { - type: 'doughnut', - data: { - labels: data.error_types || [], - datasets: [{ - data: data.counts || [], - backgroundColor: [ - '#dc3545', - '#fd7e14', - '#ffc107', - '#17a2b8', - '#6c757d' - ] - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'bottom' - } - } - } - }); - } - - async loadErrorLog() { - try { - const response = await fetch('/api/ai-monitor/error-log'); - const data = await response.json(); - - if (data.success) { - this.renderErrorLog(data.errors || []); - } - } catch (error) { - console.error('加载错误日志失败:', error); - } - } - - renderErrorLog(errors) { - const tbody = document.getElementById('error-log'); - - if (!errors || errors.length === 0) { - tbody.innerHTML = ` - - 暂无错误记录 - - `; - return; - } - - const html = errors.map(error => ` - - ${new Date(error.timestamp).toLocaleString()} - ${error.error_type || '未知'} - ${error.error_message || ''} - ${error.model || 'qwen-turbo'} - ${error.user_id || '匿名'} - - - - - `).join(''); - - tbody.innerHTML = html; - } - - async refreshErrorLog() { - await this.loadErrorLog(); - this.showNotification('错误日志已刷新', 'success'); - } - - async clearErrorLog() { - if (!confirm('确定要清空错误日志吗?')) { - return; - } - - try { - const response = await fetch('/api/ai-monitor/error-log', { method: 'DELETE' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('错误日志已清空', 'success'); - await this.loadErrorLog(); - } else { - throw new Error(data.error || '清空错误日志失败'); - } - } catch (error) { - console.error('清空错误日志失败:', error); - this.showNotification('清空错误日志失败: ' + error.message, 'error'); - } - } - - // 系统优化 - async loadSystemOptimizer() { - try { - const response = await fetch('/api/system-optimizer/status'); - const data = await response.json(); - - if (data.success) { - this.updateSystemStats(data); - this.loadSecuritySettings(); - this.loadTrafficSettings(); - this.loadCostSettings(); - } else { - throw new Error(data.error || '加载系统优化数据失败'); - } - } catch (error) { - console.error('加载系统优化数据失败:', error); - this.showNotification('加载系统优化数据失败: ' + error.message, 'error'); - } - } - - updateSystemStats(stats) { - document.getElementById('cpu-usage').textContent = `${stats.cpu_usage || 0}%`; - document.getElementById('memory-usage-percent').textContent = `${stats.memory_usage || 0}%`; - document.getElementById('disk-usage').textContent = `${stats.disk_usage || 0}%`; - document.getElementById('network-latency').textContent = `${stats.network_latency || 0}ms`; - - // 更新健康指标 - this.updateHealthIndicator('system-health-indicator', stats.system_health || 95); - this.updateHealthIndicator('database-health-indicator', stats.database_health || 98); - this.updateHealthIndicator('api-health-indicator', stats.api_health || 92); - this.updateHealthIndicator('cache-health-indicator', stats.cache_health || 99); - - document.getElementById('system-health-score').textContent = `${stats.system_health || 95}%`; - document.getElementById('database-health-score').textContent = `${stats.database_health || 98}%`; - document.getElementById('api-health-score').textContent = `${stats.api_health || 92}%`; - document.getElementById('cache-health-score').textContent = `${stats.cache_health || 99}%`; - } - - updateHealthIndicator(elementId, score) { - const element = document.getElementById(elementId); - if (!element) return; - - element.className = 'health-dot'; - if (score >= 95) element.classList.add('excellent'); - else if (score >= 85) element.classList.add('good'); - else if (score >= 70) element.classList.add('fair'); - else if (score >= 50) element.classList.add('poor'); - else element.classList.add('critical'); - } - - async optimizeCPU() { - try { - const response = await fetch('/api/system-optimizer/optimize-cpu', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message || 'CPU优化完成', 'success'); - this.updateOptimizationProgress('cpu-optimization', data.progress || 100); - // 刷新状态并回落进度条 - await this.loadSystemOptimizer(); - setTimeout(() => this.updateOptimizationProgress('cpu-optimization', 0), 1500); - } else { - throw new Error(data.error || 'CPU优化失败'); - } - } catch (error) { - console.error('CPU优化失败:', error); - this.showNotification('CPU优化失败: ' + error.message, 'error'); - } - } - - async optimizeMemory() { - try { - const response = await fetch('/api/system-optimizer/optimize-memory', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message || '内存优化完成', 'success'); - this.updateOptimizationProgress('memory-optimization', data.progress || 100); - await this.loadSystemOptimizer(); - setTimeout(() => this.updateOptimizationProgress('memory-optimization', 0), 1500); - } else { - throw new Error(data.error || '内存优化失败'); - } - } catch (error) { - console.error('内存优化失败:', error); - this.showNotification('内存优化失败: ' + error.message, 'error'); - } - } - - async optimizeDisk() { - try { - const response = await fetch('/api/system-optimizer/optimize-disk', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message || '磁盘优化完成', 'success'); - this.updateOptimizationProgress('disk-optimization', data.progress || 100); - await this.loadSystemOptimizer(); - setTimeout(() => this.updateOptimizationProgress('disk-optimization', 0), 1500); - } else { - throw new Error(data.error || '磁盘优化失败'); - } - } catch (error) { - console.error('磁盘优化失败:', error); - this.showNotification('磁盘优化失败: ' + error.message, 'error'); - } - } - - updateOptimizationProgress(elementId, progress) { - const element = document.getElementById(elementId); - if (element) { - element.style.width = `${progress}%`; - } - } - - async saveSecuritySettings() { - const settings = { - input_validation: document.getElementById('input-validation').checked, - rate_limiting: document.getElementById('rate-limiting').checked, - sql_injection_protection: document.getElementById('sql-injection-protection').checked, - xss_protection: document.getElementById('xss-protection').checked - }; - - try { - const response = await fetch('/api/system-optimizer/security-settings', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(settings) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('安全设置已保存', 'success'); - } else { - throw new Error(data.error || '保存安全设置失败'); - } - } catch (error) { - console.error('保存安全设置失败:', error); - this.showNotification('保存安全设置失败: ' + error.message, 'error'); - } - } - - async saveTrafficSettings() { - const settings = { - request_limit: parseInt(document.getElementById('request-limit').value), - concurrent_limit: parseInt(document.getElementById('concurrent-limit').value), - ip_whitelist: document.getElementById('ip-whitelist').value.split('\n').filter(ip => ip.trim()) - }; - - try { - const response = await fetch('/api/system-optimizer/traffic-settings', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(settings) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('流量设置已保存', 'success'); - } else { - throw new Error(data.error || '保存流量设置失败'); - } - } catch (error) { - console.error('保存流量设置失败:', error); - this.showNotification('保存流量设置失败: ' + error.message, 'error'); - } - } - - async saveCostSettings() { - const settings = { - monthly_budget_limit: parseFloat(document.getElementById('monthly-budget-limit').value), - per_call_cost_limit: parseFloat(document.getElementById('per-call-cost-limit').value), - auto_cost_control: document.getElementById('auto-cost-control').checked - }; - - try { - const response = await fetch('/api/system-optimizer/cost-settings', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(settings) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('成本设置已保存', 'success'); - } else { - throw new Error(data.error || '保存成本设置失败'); - } - } catch (error) { - console.error('保存成本设置失败:', error); - this.showNotification('保存成本设置失败: ' + error.message, 'error'); - } - } - - async runHealthCheck() { - try { - const response = await fetch('/api/system-optimizer/health-check', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('健康检查完成', 'success'); - this.updateSystemStats(data); - } else { - throw new Error(data.error || '健康检查失败'); - } - } catch (error) { - console.error('健康检查失败:', error); - this.showNotification('健康检查失败: ' + error.message, 'error'); - } - } - - async refreshSystemStatus() { - await this.loadSystemOptimizer(); - this.showNotification('系统状态已刷新', 'success'); - } - - async clearCache() { - try { - const response = await fetch('/api/system-optimizer/clear-cache', { method: 'POST' }); - const data = await response.json(); - if (data.success) { - this.showNotification(data.message || '缓存已清理', 'success'); - await this.loadSystemOptimizer(); - } else { - throw new Error(data.error || '清理缓存失败'); - } - } catch (error) { - console.error('清理缓存失败:', error); - this.showNotification('清理缓存失败: ' + error.message, 'error'); - } - } - - async optimizeAll() { - try { - const response = await fetch('/api/system-optimizer/optimize-all', { method: 'POST' }); - const data = await response.json(); - if (data.success) { - this.showNotification(data.message || '一键优化完成', 'success'); - await this.loadSystemOptimizer(); - ['cpu-optimization','memory-optimization','disk-optimization'].forEach(id => this.updateOptimizationProgress(id, 100)); - setTimeout(() => ['cpu-optimization','memory-optimization','disk-optimization'].forEach(id => this.updateOptimizationProgress(id, 0)), 1500); - } else { - throw new Error(data.error || '一键优化失败'); - } - } catch (error) { - console.error('一键优化失败:', error); - this.showNotification('一键优化失败: ' + error.message, 'error'); - } - } - - async loadSecuritySettings() { - try { - const response = await fetch('/api/system-optimizer/security-settings'); - const data = await response.json(); - - if (data.success) { - document.getElementById('input-validation').checked = data.input_validation || false; - document.getElementById('rate-limiting').checked = data.rate_limiting || false; - document.getElementById('sql-injection-protection').checked = data.sql_injection_protection || false; - document.getElementById('xss-protection').checked = data.xss_protection || false; - } - } catch (error) { - console.error('加载安全设置失败:', error); - } - } - - async loadTrafficSettings() { - try { - const response = await fetch('/api/system-optimizer/traffic-settings'); - const data = await response.json(); - - if (data.success) { - document.getElementById('request-limit').value = data.request_limit || 100; - document.getElementById('concurrent-limit').value = data.concurrent_limit || 50; - document.getElementById('ip-whitelist').value = (data.ip_whitelist || []).join('\n'); - } - } catch (error) { - console.error('加载流量设置失败:', error); - } - } - - async loadCostSettings() { - try { - const response = await fetch('/api/system-optimizer/cost-settings'); - const data = await response.json(); - - if (data.success) { - document.getElementById('monthly-budget-limit').value = data.monthly_budget_limit || 1000; - document.getElementById('per-call-cost-limit').value = data.per_call_cost_limit || 0.1; - document.getElementById('auto-cost-control').checked = data.auto_cost_control || false; - } - } catch (error) { - console.error('加载成本设置失败:', error); - } - } - - // 数据分析 - async loadAnalytics() { - try { - const response = await fetch('/api/analytics'); - const analytics = await response.json(); - this.updateAnalyticsDisplay(analytics); - this.updateStatisticsCards(analytics); // 添加统计卡片更新 - this.initializeCharts(); - } catch (error) { - console.error('加载分析数据失败:', error); - } - } - - // 初始化图表 - initializeCharts() { - if (!this.charts) { - this.charts = {}; - } - this.updateCharts(); - } - - // 清理连接 - cleanupConnections() { - // 关闭WebSocket连接 - if (this.websocket) { - this.websocket.close(); - this.websocket = null; - } - - // 清理所有定时器 - Object.values(this.refreshIntervals).forEach(interval => { - clearInterval(interval); - }); - this.refreshIntervals = {}; - } - - // 销毁所有图表 - destroyAllCharts() { - if (!this.charts) return; - - Object.keys(this.charts).forEach(chartId => { - if (this.charts[chartId]) { - try { - this.charts[chartId].destroy(); - } catch (e) { - console.warn(`Error destroying chart ${chartId}:`, e); - } - this.charts[chartId] = null; - } - }); - - // 清理charts对象 - this.charts = {}; - } - - // 安全的图表创建方法 - createChart(canvasId, chartConfig) { - const canvas = document.getElementById(canvasId); - if (!canvas) { - console.error(`Canvas element '${canvasId}' not found`); - return null; - } - - // 确保charts对象存在 - if (!this.charts) { - this.charts = {}; - } - - // 销毁现有图表 - if (this.charts[canvasId]) { - try { - this.charts[canvasId].destroy(); - } catch (e) { - console.warn(`Error destroying chart ${canvasId}:`, e); - } - this.charts[canvasId] = null; - } - - try { - const ctx = canvas.getContext('2d'); - this.charts[canvasId] = new Chart(ctx, chartConfig); - return this.charts[canvasId]; - } catch (e) { - console.error(`Error creating chart ${canvasId}:`, e); - return null; - } - } - - // 更新所有图表 - async updateCharts() { - try { - const timeRange = document.getElementById('timeRange').value; - const chartType = document.getElementById('chartType').value; - const dataDimension = document.getElementById('dataDimension').value; - - // 获取数据 - const response = await fetch(`/api/analytics?timeRange=${timeRange}&dimension=${dataDimension}`); - const data = await response.json(); - - // 更新统计卡片 - this.updateStatisticsCards(data); - - // 更新图表 - this.updateMainChart(data, chartType); - this.updateDistributionChart(data); - this.updateTrendChart(data); - this.updatePriorityChart(data); - - // 更新分析报告 - this.updateAnalyticsReport(data); - - } catch (error) { - console.error('更新图表失败:', error); - this.showNotification('更新图表失败: ' + error.message, 'error'); - } - } - - // 更新统计卡片 - updateStatisticsCards(data) { - // 更新工单统计 - const total = data.workorders?.total || 0; - const open = data.workorders?.open || 0; - const inProgress = data.workorders?.in_progress || 0; - const resolved = data.workorders?.resolved || 0; - const avgSatisfaction = data.satisfaction?.average || 0; - - // 更新工单统计数字(使用正确的元素ID) - if (document.getElementById('workorders-total')) { - document.getElementById('workorders-total').textContent = total; - } - if (document.getElementById('workorders-open')) { - document.getElementById('workorders-open').textContent = open; - } - if (document.getElementById('workorders-progress')) { - document.getElementById('workorders-progress').textContent = inProgress; - } - if (document.getElementById('workorders-resolved')) { - document.getElementById('workorders-resolved').textContent = resolved; - } - - // 同时更新其他可能的元素ID - if (document.getElementById('totalWorkorders')) { - document.getElementById('totalWorkorders').textContent = total; - } - if (document.getElementById('openWorkorders')) { - document.getElementById('openWorkorders').textContent = open; - } - if (document.getElementById('resolvedWorkorders')) { - document.getElementById('resolvedWorkorders').textContent = resolved; - } - document.getElementById('avgSatisfaction').textContent = avgSatisfaction.toFixed(1); - - // 更新预警统计 - const alertTotal = data.alerts?.total || 0; - const alertActive = data.alerts?.active || 0; - const alertCritical = data.alerts?.by_level?.critical || 0; - const alertWarning = data.alerts?.by_level?.warning || 0; - const alertError = data.alerts?.by_level?.error || 0; - - // 更新预警统计显示 - if (document.getElementById('critical-alerts')) { - document.getElementById('critical-alerts').textContent = alertCritical; - } - if (document.getElementById('warning-alerts')) { - document.getElementById('warning-alerts').textContent = alertWarning; - } - if (document.getElementById('error-alerts')) { - document.getElementById('error-alerts').textContent = alertError; - } - if (document.getElementById('total-alerts-count')) { - document.getElementById('total-alerts-count').textContent = alertTotal; - } - - // 更新性能统计 - const performanceScore = data.performance?.score || 0; - const performanceTrend = data.performance?.trend || 'stable'; - - if (document.getElementById('performance-score')) { - document.getElementById('performance-score').textContent = performanceScore.toFixed(1); - } - if (document.getElementById('performance-trend')) { - document.getElementById('performance-trend').textContent = this.getPerformanceTrendText(performanceTrend); - } - - // 更新满意度统计 - const satisfactionAvg = data.satisfaction?.average || 0; - const satisfactionCount = data.satisfaction?.count || 0; - - if (document.getElementById('satisfaction-avg')) { - document.getElementById('satisfaction-avg').textContent = satisfactionAvg.toFixed(1); - } - if (document.getElementById('satisfaction-count')) { - document.getElementById('satisfaction-count').textContent = satisfactionCount; - } - - // 更新进度条 - if (total > 0) { - document.getElementById('openProgress').style.width = `${(open / total) * 100}%`; - document.getElementById('resolvedProgress').style.width = `${(resolved / total) * 100}%`; - document.getElementById('satisfactionProgress').style.width = `${(avgSatisfaction / 5) * 100}%`; - } - } - - // 更新主图表 - updateMainChart(data, chartType) { - const chartData = this.prepareChartData(data, chartType); - - const chartConfig = { - type: chartType, - data: chartData, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: '数据分析趋势' - }, - legend: { - display: true, - position: 'top' - } - }, - scales: chartType === 'pie' || chartType === 'doughnut' ? {} : { - x: { - display: true, - title: { - display: true, - text: '时间' - } - }, - y: { - display: true, - title: { - display: true, - text: '数量' - } - } - } - } - }; - - this.createChart('mainChart', chartConfig); - } - - // 更新分布图表 - updateDistributionChart(data) { - const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; - - let labels, values, title, backgroundColor; - - if (currentDimension === 'alerts') { - // 预警级别分布 - const alertLevels = data.alerts?.by_level || {}; - labels = Object.keys(alertLevels); - values = Object.values(alertLevels); - title = '预警级别分布'; - backgroundColor = [ - '#FF6384', // critical - 红色 - '#FFCE56', // warning - 黄色 - '#36A2EB', // error - 蓝色 - '#4BC0C0', // info - 青色 - '#9966FF' // 其他 - ]; - } else if (currentDimension === 'performance') { - // 性能指标分布 - const performanceMetrics = data.performance?.by_level || {}; - labels = Object.keys(performanceMetrics); - values = Object.values(performanceMetrics); - title = '性能指标分布'; - backgroundColor = [ - '#28a745', // 优秀 - 绿色 - '#ffc107', // 良好 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 差 - 红色 - ]; - } else if (currentDimension === 'satisfaction') { - // 满意度分布 - const satisfactionLevels = data.satisfaction?.by_level || {}; - labels = Object.keys(satisfactionLevels); - values = Object.values(satisfactionLevels); - title = '满意度分布'; - backgroundColor = [ - '#28a745', // 非常满意 - 绿色 - '#ffc107', // 满意 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 不满意 - 红色 - ]; - } else { - // 工单分类分布 - const categories = data.workorders?.by_category || {}; - labels = Object.keys(categories); - values = Object.values(categories); - title = '工单分类分布'; - backgroundColor = [ - '#FF6384', - '#36A2EB', - '#FFCE56', - '#4BC0C0', - '#9966FF', - '#FF9F40' - ]; - } - - const chartConfig = { - type: 'doughnut', - data: { - labels: labels, - datasets: [{ - data: values, - backgroundColor: backgroundColor - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: title - }, - legend: { - display: true, - position: 'bottom' - } - } - } - }; - - this.createChart('distributionChart', chartConfig); - } - - // 更新趋势图表 - updateTrendChart(data) { - const ctx = document.getElementById('trendChart').getContext('2d'); - - if (this.charts.trendChart) { - this.charts.trendChart.destroy(); - } - - const trendData = data.trend || []; - const labels = trendData.map(item => item.date); - const workorders = trendData.map(item => item.workorders); - const alerts = trendData.map(item => item.alerts); - - this.charts.trendChart = new Chart(ctx, { - type: 'line', - data: { - labels: labels, - datasets: [{ - label: '工单数量', - data: workorders, - borderColor: '#36A2EB', - backgroundColor: 'rgba(54, 162, 235, 0.1)', - tension: 0.4 - }, { - label: '预警数量', - data: alerts, - borderColor: '#FF6384', - backgroundColor: 'rgba(255, 99, 132, 0.1)', - tension: 0.4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: '时间趋势分析' - } - }, - scales: { - x: { - display: true, - title: { - display: true, - text: '日期' - } - }, - y: { - display: true, - title: { - display: true, - text: '数量' - } - } - } - } - }); - } - - // 更新优先级图表 - updatePriorityChart(data) { - const ctx = document.getElementById('priorityChart').getContext('2d'); - - if (this.charts.priorityChart) { - this.charts.priorityChart.destroy(); - } - - const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; - - let labels, values, title, backgroundColor, label; - - if (currentDimension === 'alerts') { - // 预警严重程度分布 - const alertSeverities = data.alerts?.by_severity || {}; - labels = Object.keys(alertSeverities).map(s => this.getSeverityText(s)); - values = Object.values(alertSeverities); - title = '预警严重程度分布'; - label = '预警数量'; - backgroundColor = [ - '#28a745', // low - 绿色 - '#ffc107', // medium - 黄色 - '#fd7e14', // high - 橙色 - '#dc3545' // critical - 红色 - ]; - } else if (currentDimension === 'performance') { - // 性能指标分布 - const performanceMetrics = data.performance?.by_metric || {}; - labels = Object.keys(performanceMetrics); - values = Object.values(performanceMetrics); - title = '性能指标分布'; - label = '性能值'; - backgroundColor = [ - '#28a745', // 优秀 - 绿色 - '#ffc107', // 良好 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 差 - 红色 - ]; - } else if (currentDimension === 'satisfaction') { - // 满意度分布 - const satisfactionLevels = data.satisfaction?.by_level || {}; - labels = Object.keys(satisfactionLevels).map(s => this.getSatisfactionText(s)); - values = Object.values(satisfactionLevels); - title = '满意度分布'; - label = '满意度数量'; - backgroundColor = [ - '#28a745', // 非常满意 - 绿色 - '#ffc107', // 满意 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 不满意 - 红色 - ]; - } else { - // 工单优先级分布 - const priorities = data.workorders?.by_priority || {}; - labels = Object.keys(priorities).map(p => this.getPriorityText(p)); - values = Object.values(priorities); - title = '工单优先级分布'; - label = '工单数量'; - backgroundColor = [ - '#28a745', // 低 - 绿色 - '#ffc107', // 中 - 黄色 - '#fd7e14', // 高 - 橙色 - '#dc3545' // 紧急 - 红色 - ]; - } - - this.charts.priorityChart = new Chart(ctx, { - type: 'bar', - data: { - labels: labels, - datasets: [{ - label: label, - data: values, - backgroundColor: backgroundColor - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - title: { - display: true, - text: title - } - }, - scales: { - y: { - beginAtZero: true - } - } - } - }); - } - - // 准备图表数据 - prepareChartData(data, chartType) { - const trendData = data.trend || []; - const labels = trendData.map(item => item.date); - const workorders = trendData.map(item => item.workorders); - const alerts = trendData.map(item => item.alerts); - const performance = trendData.map(item => item.performance || 0); - const satisfaction = trendData.map(item => item.satisfaction || 0); - - if (chartType === 'pie' || chartType === 'doughnut') { - // 根据数据维度选择显示内容 - const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; - - if (currentDimension === 'alerts') { - const alertLevels = data.alerts?.by_level || {}; - return { - labels: Object.keys(alertLevels), - datasets: [{ - data: Object.values(alertLevels), - backgroundColor: [ - '#FF6384', // critical - 红色 - '#FFCE56', // warning - 黄色 - '#36A2EB', // error - 蓝色 - '#4BC0C0', // info - 青色 - '#9966FF' // 其他 - ] - }] - }; - } else if (currentDimension === 'performance') { - // 性能指标分布 - const performanceMetrics = data.performance || {}; - return { - labels: Object.keys(performanceMetrics), - datasets: [{ - data: Object.values(performanceMetrics), - backgroundColor: [ - '#28a745', // 优秀 - 绿色 - '#ffc107', // 良好 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 差 - 红色 - ] - }] - }; - } else if (currentDimension === 'satisfaction') { - // 满意度分布 - const satisfactionLevels = data.satisfaction?.by_level || {}; - return { - labels: Object.keys(satisfactionLevels), - datasets: [{ - data: Object.values(satisfactionLevels), - backgroundColor: [ - '#28a745', // 非常满意 - 绿色 - '#ffc107', // 满意 - 黄色 - '#fd7e14', // 一般 - 橙色 - '#dc3545' // 不满意 - 红色 - ] - }] - }; - } else { - const categories = data.workorders?.by_category || {}; - return { - labels: Object.keys(categories), - datasets: [{ - data: Object.values(categories), - backgroundColor: [ - '#FF6384', - '#36A2EB', - '#FFCE56', - '#4BC0C0', - '#9966FF', - '#FF9F40' - ] - }] - }; - } - } else { - // 线图和柱状图根据数据维度显示不同内容 - const currentDimension = document.getElementById('dataDimension')?.value || 'workorders'; - const datasets = []; - - if (currentDimension === 'performance') { - // 性能指标图表 - datasets.push({ - label: '性能指标', - data: performance, - borderColor: '#28a745', - backgroundColor: 'rgba(40, 167, 69, 0.1)', - tension: chartType === 'line' ? 0.4 : 0 - }); - } else if (currentDimension === 'satisfaction') { - // 满意度图表 - datasets.push({ - label: '满意度评分', - data: satisfaction, - borderColor: '#ffc107', - backgroundColor: 'rgba(255, 193, 7, 0.1)', - tension: chartType === 'line' ? 0.4 : 0 - }); - } else { - // 默认显示工单和预警数据 - datasets.push({ - label: '工单数量', - data: workorders, - borderColor: '#36A2EB', - backgroundColor: 'rgba(54, 162, 235, 0.1)', - tension: chartType === 'line' ? 0.4 : 0 - }); - - // 如果有预警数据,添加预警数据集 - if (alerts.some(alert => alert > 0)) { - datasets.push({ - label: '预警数量', - data: alerts, - borderColor: '#FF6384', - backgroundColor: 'rgba(255, 99, 132, 0.1)', - tension: chartType === 'line' ? 0.4 : 0 - }); - } - } - - return { - labels: labels, - datasets: datasets - }; - } - } - - // 导出图表 - exportChart(chartId) { - if (this.charts[chartId]) { - const link = document.createElement('a'); - link.download = `${chartId}_chart.png`; - link.href = this.charts[chartId].toBase64Image(); - link.click(); - } - } - - // 全屏图表 - fullscreenChart(chartId) { - // 这里可以实现全屏显示功能 - this.showNotification('全屏功能开发中', 'info'); - } - - // 导出报告 - async exportReport() { - try { - const response = await fetch('/api/analytics/export'); - const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = 'analytics_report.xlsx'; - link.click(); - window.URL.revokeObjectURL(url); - } catch (error) { - console.error('导出报告失败:', error); - this.showNotification('导出报告失败: ' + error.message, 'error'); - } - } - - // 打印报告 - printReport() { - window.print(); - } - - // Agent执行历史相关功能 - async refreshAgentHistory() { - try { - const response = await fetch('/api/agent/action-history?limit=20'); - const data = await response.json(); - - if (data.success) { - this.updateAgentExecutionHistory(data.history); - this.showNotification(`已加载 ${data.count} 条执行历史`, 'success'); - } else { - throw new Error(data.error || '获取执行历史失败'); - } - } catch (error) { - console.error('刷新Agent历史失败:', error); - this.showNotification('刷新Agent历史失败: ' + error.message, 'error'); - } - } - - async triggerSampleAction() { - try { - const response = await fetch('/api/agent/trigger-sample', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - } - }); - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - // 刷新执行历史 - await this.refreshAgentHistory(); - } else { - throw new Error(data.error || '触发示例动作失败'); - } - } catch (error) { - console.error('触发示例动作失败:', error); - this.showNotification('触发示例动作失败: ' + error.message, 'error'); - } - } - - async clearAgentHistory() { - if (!confirm('确定要清空Agent执行历史吗?此操作不可恢复。')) { - return; - } - - try { - const response = await fetch('/api/agent/clear-history', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - } - }); - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - // 清空显示 - this.updateAgentExecutionHistory([]); - } else { - throw new Error(data.error || '清空历史失败'); - } - } catch (error) { - console.error('清空Agent历史失败:', error); - this.showNotification('清空Agent历史失败: ' + error.message, 'error'); - } - } - - updateAgentExecutionHistory(history) { - const container = document.getElementById('agent-execution-history'); - - if (!history || history.length === 0) { - container.innerHTML = ` -
- -

暂无执行历史

-
- `; - return; - } - - const historyHtml = history.map(record => { - const startTime = new Date(record.start_time * 1000).toLocaleString(); - const endTime = new Date(record.end_time * 1000).toLocaleString(); - const duration = Math.round((record.end_time - record.start_time) * 100) / 100; - - const priorityColor = { - 5: 'danger', - 4: 'warning', - 3: 'info', - 2: 'secondary', - 1: 'light' - }[record.priority] || 'secondary'; - - const confidenceColor = record.confidence >= 0.8 ? 'success' : - record.confidence >= 0.5 ? 'warning' : 'danger'; - - return ` -
-
-
-
-
${record.description}
-
- 优先级 ${record.priority} - 置信度 ${(record.confidence * 100).toFixed(0)}% - ${record.success ? '成功' : '失败'} -
- - 开始: ${startTime} | - 耗时: ${duration}秒 - -
-
- ${record.action_type} -
-
- ${record.result && record.result.message ? ` -
- 结果: ${record.result.message} -
- ` : ''} -
-
- `; - }).join(''); - - container.innerHTML = historyHtml; - } - - // 更新分析报告 - updateAnalyticsReport(data) { - const reportContainer = document.getElementById('analytics-report'); - - if (!reportContainer) return; - - const summary = data.summary || {}; - const workorders = data.workorders || {}; - const satisfaction = data.satisfaction || {}; - const alerts = data.alerts || {}; - const performance = data.performance || {}; - - const reportHtml = ` -
-
-
工单统计概览
-
- - - - - - - - - - - - - - - - - - - - - -
总工单数${workorders.total || 0}
待处理${workorders.open || 0}
处理中${workorders.in_progress || 0}
已解决${workorders.resolved || 0}
已关闭${workorders.closed || 0}
-
-
-
-
满意度分析
-
- - - - - - - - - - - - - - - - - - - - - -
平均满意度${satisfaction.average || 0}/5.0
5星评价${satisfaction.distribution?.['5'] || 0} 个
4星评价${satisfaction.distribution?.['4'] || 0} 个
3星评价${satisfaction.distribution?.['3'] || 0} 个
2星及以下${(satisfaction.distribution?.['2'] || 0) + (satisfaction.distribution?.['1'] || 0)} 个
-
-
-
- -
-
-
预警统计
-
- - - - - - - - - - - - - -
总预警数${alerts.total || 0}
活跃预警${alerts.active || 0}
已解决${alerts.resolved || 0}
-
-
-
-
性能指标
-
- - - - - - - - - - - - - - - - - -
响应时间${performance.response_time || 0} 秒
系统可用性${performance.uptime || 0}%
错误率${performance.error_rate || 0}%
吞吐量${performance.throughput || 0} 请求/小时
-
-
-
- -
-
-
关键指标总结
-
-
-
-
-
${summary.resolution_rate || 0}%
-

解决率

-
-
-
-
-
-
-
${summary.avg_satisfaction || 0}
-

平均满意度

-
-
-
-
-
-
-
${summary.active_alerts || 0}
-

活跃预警

-
-
-
-
-
-
-
${summary.total_workorders || 0}
-

总工单数

-
-
-
-
-
-
- `; - - reportContainer.innerHTML = reportHtml; - } - - updateAnalyticsDisplay(analytics) { - // 更新分析报告 - const reportContainer = document.getElementById('analytics-report'); - if (analytics.summary) { - reportContainer.innerHTML = ` -
-
-
性能指标
-
    -
  • 总工单数: ${analytics.summary.total_orders || 0}
  • -
  • 解决率: ${Math.round((analytics.summary.resolution_rate || 0) * 100)}%
  • -
  • 平均解决时间: ${analytics.summary.avg_resolution_time_hours || 0}小时
  • -
  • 平均满意度: ${analytics.summary.avg_satisfaction || 0}
  • -
-
-
-
趋势分析
-
    -
  • 工单趋势: ${analytics.summary.trends?.orders_trend ? '上升' : '下降'}
  • -
  • 满意度趋势: ${analytics.summary.trends?.satisfaction_trend ? '上升' : '下降'}
  • -
  • 解决时间趋势: ${analytics.summary.trends?.resolution_time_trend ? '上升' : '下降'}
  • -
-
-
- `; - } - - // 更新类别分布图 - if (analytics.category_distribution && this.charts.category) { - const labels = Object.keys(analytics.category_distribution); - const data = Object.values(analytics.category_distribution); - - this.charts.category.data.labels = labels; - this.charts.category.data.datasets[0].data = data; - this.charts.category.update(); - } - } - - // 系统设置 - async loadSettings() { - try { - const response = await fetch('/api/settings'); - const settings = await response.json(); - this.updateSettingsDisplay(settings); - } catch (error) { - console.error('加载设置失败:', error); - } - } - - updateSettingsDisplay(settings) { - if (settings.api_timeout !== undefined) document.getElementById('api-timeout').value = settings.api_timeout; - if (settings.max_history !== undefined) document.getElementById('max-history').value = settings.max_history; - if (settings.refresh_interval !== undefined) document.getElementById('refresh-interval').value = settings.refresh_interval; - if (settings.auto_monitoring !== undefined) document.getElementById('auto-monitoring').checked = settings.auto_monitoring; - if (settings.agent_mode !== undefined) document.getElementById('agent-mode').checked = settings.agent_mode; - // 新增:API与模型、端口、日志级别(如页面存在对应输入框则填充) - const map = [ - ['api-provider','api_provider'], - ['api-base-url','api_base_url'], - ['api-key','api_key'], - ['model-name','model_name'], - ['model-temperature','model_temperature'], - ['model-max-tokens','model_max_tokens'], - ['server-port','server_port'], - ['websocket-port','websocket_port'], - ['log-level','log_level'] - ]; - map.forEach(([id, key]) => { - const el = document.getElementById(id); - if (el && settings[key] !== undefined) el.value = settings[key]; - }); - - // 更新温度滑块显示值 - const tempSlider = document.getElementById('model-temperature'); - const tempValue = document.getElementById('temperature-value'); - if (tempSlider && tempValue) { - tempSlider.addEventListener('input', function() { - tempValue.textContent = this.value; - }); - tempValue.textContent = tempSlider.value; - } - - // 更新服务状态显示 - this.updateServiceStatus(settings); - } - - async saveSystemSettings() { - const settings = { - api_timeout: parseInt(document.getElementById('api-timeout').value), - max_history: parseInt(document.getElementById('max-history').value), - refresh_interval: parseInt(document.getElementById('refresh-interval').value), - auto_monitoring: document.getElementById('auto-monitoring').checked, - agent_mode: document.getElementById('agent-mode').checked, - api_provider: document.getElementById('api-provider')?.value || '', - api_base_url: document.getElementById('api-base-url')?.value || '', - api_key: document.getElementById('api-key')?.value || '', - model_name: document.getElementById('model-name')?.value || '', - model_temperature: parseFloat(document.getElementById('model-temperature')?.value || 0.7), - model_max_tokens: parseInt(document.getElementById('model-max-tokens')?.value || 1000), - server_port: parseInt(document.getElementById('server-port')?.value || 5000), - websocket_port: parseInt(document.getElementById('websocket-port')?.value || 8765), - log_level: document.getElementById('log-level')?.value || 'INFO' - }; - - try { - const response = await fetch('/api/settings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(settings) - }); - - const data = await response.json(); - if (data.success) { - this.showNotification('设置保存成功', 'success'); - } else { - this.showNotification('保存设置失败', 'error'); - } - } catch (error) { - console.error('保存设置失败:', error); - this.showNotification('保存设置失败', 'error'); - } - } - - // 更新服务状态显示 - updateServiceStatus(settings) { - // 更新仪表板服务状态卡片 - if (settings.current_server_port !== undefined) { - const webPortEl = document.getElementById('web-port-status'); - if (webPortEl) webPortEl.textContent = settings.current_server_port; - } - if (settings.current_websocket_port !== undefined) { - const wsPortEl = document.getElementById('ws-port-status'); - if (wsPortEl) wsPortEl.textContent = settings.current_websocket_port; - } - if (settings.log_level !== undefined) { - const logLevelEl = document.getElementById('log-level-status'); - if (logLevelEl) logLevelEl.textContent = settings.log_level; - } - if (settings.uptime_seconds !== undefined) { - const uptimeEl = document.getElementById('uptime-status'); - if (uptimeEl) { - const hours = Math.floor(settings.uptime_seconds / 3600); - const minutes = Math.floor((settings.uptime_seconds % 3600) / 60); - uptimeEl.textContent = `${hours}小时${minutes}分钟`; - } - } - - // 更新系统设置页面的当前端口显示 - const currentPortEl = document.getElementById('current-server-port'); - if (currentPortEl && settings.current_server_port !== undefined) { - currentPortEl.textContent = settings.current_server_port; - } - } - - // 刷新服务状态 - async refreshServiceStatus() { - try { - const response = await fetch('/api/settings'); - const settings = await response.json(); - this.updateServiceStatus(settings); - this.showNotification('服务状态已刷新', 'success'); - } catch (error) { - console.error('刷新服务状态失败:', error); - this.showNotification('刷新服务状态失败', 'error'); - } - } - - // 测试API连接 - async testApiConnection() { - try { - const apiProvider = document.getElementById('api-provider').value; - const apiBaseUrl = document.getElementById('api-base-url').value; - const apiKey = document.getElementById('api-key').value; - const modelName = document.getElementById('model-name').value; - - const response = await fetch('/api/test/connection', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - api_provider: apiProvider, - api_base_url: apiBaseUrl, - api_key: apiKey, - model_name: modelName - }) - }); - - const result = await response.json(); - if (result.success) { - this.showNotification(`API连接测试成功: ${result.message}`, 'success'); - } else { - this.showNotification(`API连接测试失败: ${result.error}`, 'error'); - } - } catch (error) { - console.error('API连接测试失败:', error); - this.showNotification('API连接测试失败', 'error'); - } - } - - // 测试模型回答 - async testModelResponse() { - try { - const testMessage = prompt('请输入测试消息:', '你好,请简单介绍一下你自己'); - if (!testMessage) return; - - const response = await fetch('/api/test/model', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - test_message: testMessage - }) - }); - - const result = await response.json(); - if (result.success) { - const message = `模型回答测试成功:\n\n问题: ${result.test_message}\n\n回答: ${result.response}\n\n响应时间: ${result.response_time}`; - alert(message); - this.showNotification('模型回答测试成功', 'success'); - } else { - this.showNotification(`模型回答测试失败: ${result.error}`, 'error'); - } - } catch (error) { - console.error('模型回答测试失败:', error); - this.showNotification('模型回答测试失败', 'error'); - } - } - - async loadSystemInfo() { - try { - const response = await fetch('/api/system/info'); - const info = await response.json(); - this.updateSystemInfoDisplay(info); - } catch (error) { - console.error('加载系统信息失败:', error); - } - } - - updateSystemInfoDisplay(info) { - const container = document.getElementById('system-info'); - container.innerHTML = ` -
- 系统版本: ${info.version || '1.0.0'} -
-
- Python版本: ${info.python_version || '未知'} -
-
- 数据库: ${info.database || 'SQLite'} -
-
- 运行时间: ${info.uptime || '未知'} -
-
- 内存使用: ${info.memory_usage || '0'} MB -
- `; - } - - // 工具函数 - getLevelText(level) { - const levelMap = { - 'critical': '严重', - 'error': '错误', - 'warning': '警告', - 'info': '信息' - }; - return levelMap[level] || level; - } - - getTypeText(type) { - const typeMap = { - 'performance': '性能', - 'quality': '质量', - 'volume': '量级', - 'system': '系统', - 'business': '业务' - }; - return typeMap[type] || type; - } - - getAlertColor(level) { - const colorMap = { - 'critical': 'danger', - 'error': 'danger', - 'warning': 'warning', - 'info': 'info' - }; - return colorMap[level] || 'secondary'; - } - - getPriorityText(priority) { - const priorityMap = { - 'low': '低', - 'medium': '中', - 'high': '高', - 'urgent': '紧急' - }; - return priorityMap[priority] || priority; - } - - getSeverityText(severity) { - const severityMap = { - 'low': '低', - 'medium': '中', - 'high': '高', - 'critical': '严重' - }; - return severityMap[severity] || severity; - } - - getSatisfactionText(level) { - const satisfactionMap = { - 'very_satisfied': '非常满意', - 'satisfied': '满意', - 'neutral': '一般', - 'dissatisfied': '不满意' - }; - return satisfactionMap[level] || level; - } - - getPerformanceTrendText(trend) { - const trendMap = { - 'up': '上升', - 'down': '下降', - 'stable': '稳定' - }; - return trendMap[trend] || trend; - } - - getPriorityColor(priority) { - const colorMap = { - 'low': 'secondary', - 'medium': 'primary', - 'high': 'warning', - 'urgent': 'danger' - }; - return colorMap[priority] || 'secondary'; - } - - getStatusText(status) { - const statusMap = { - 'open': '待处理', - 'in_progress': '处理中', - 'resolved': '已解决', - 'closed': '已关闭' - }; - return statusMap[status] || status; - } - - // 统计数字预览功能 - async showStatPreview(type, status) { - try { - let title = ''; - let data = []; - let apiUrl = ''; - - switch (type) { - case 'workorder': - title = this.getWorkorderPreviewTitle(status); - apiUrl = status === 'all' ? '/api/workorders' : `/api/workorders/by-status/${status}`; - break; - case 'alert': - title = this.getAlertPreviewTitle(status); - apiUrl = `/api/alerts/by-level/${status}`; - break; - case 'knowledge': - title = this.getKnowledgePreviewTitle(status); - apiUrl = `/api/knowledge/by-status/${status}`; - break; - default: - return; - } - - // 显示加载状态 - this.showLoadingModal(title); - - const response = await fetch(apiUrl); - const result = await response.json(); - - // 处理不同的API响应结构 - if (result.success !== false) { - if (result.success === true) { - // 新API结构: {success: true, data: {workorders: [...]}} - data = result.data[type + 's'] || result.data.knowledge || []; - } else if (result.workorders) { - // 旧API结构: {workorders: [...], page: 1, ...} - data = result.workorders || []; - } else if (result.alerts) { - // 预警API结构 - data = result.alerts || []; - } else if (result.knowledge) { - // 知识库API结构 - data = result.knowledge || []; - } else { - data = []; - } - this.showPreviewModal(title, type, data); - } else { - const errorMsg = result.error || result.message || '未知错误'; - this.showNotification('获取数据失败: ' + errorMsg, 'error'); - } - } catch (error) { - console.error('预览失败:', error); - this.showNotification('预览失败: ' + error.message, 'error'); - } - } - - getWorkorderPreviewTitle(status) { - const titles = { - 'all': '所有工单', - 'open': '待处理工单', - 'in_progress': '处理中工单', - 'resolved': '已解决工单', - 'closed': '已关闭工单' - }; - return titles[status] || '工单列表'; - } - - getAlertPreviewTitle(level) { - const titles = { - 'critical': '严重预警', - 'warning': '警告预警', - 'info': '信息预警' - }; - return titles[level] || '预警列表'; - } - - getKnowledgePreviewTitle(status) { - const titles = { - 'verified': '已验证知识', - 'unverified': '未验证知识' - }; - return titles[status] || '知识库条目'; - } - - showLoadingModal(title) { - const modalHtml = ` - - `; - - // 移除已存在的模态框和遮罩 - this.removeExistingModal(); - - document.body.insertAdjacentHTML('beforeend', modalHtml); - const modalElement = document.getElementById('statPreviewModal'); - const modal = new bootstrap.Modal(modalElement, { - backdrop: true, - keyboard: true - }); - - // 添加事件监听器确保正确清理 - modalElement.addEventListener('hidden.bs.modal', () => { - this.cleanupModal(); - }); - - modal.show(); - } - - showPreviewModal(title, type, data) { - let contentHtml = ''; - - if (data.length === 0) { - contentHtml = ` -
- -
暂无数据
-

当前条件下没有找到相关记录

-
- `; - } else { - switch (type) { - case 'workorder': - contentHtml = this.generateWorkorderPreviewHtml(data); - break; - case 'alert': - contentHtml = this.generateAlertPreviewHtml(data); - break; - case 'knowledge': - contentHtml = this.generateKnowledgePreviewHtml(data); - break; - } - } - - const modalHtml = ` - - `; - - // 移除已存在的模态框和遮罩 - this.removeExistingModal(); - - document.body.insertAdjacentHTML('beforeend', modalHtml); - const modalElement = document.getElementById('statPreviewModal'); - const modal = new bootstrap.Modal(modalElement, { - backdrop: true, - keyboard: true - }); - - // 添加事件监听器确保正确清理 - modalElement.addEventListener('hidden.bs.modal', () => { - this.cleanupModal(); - }); - - modal.show(); - } - - generateWorkorderPreviewHtml(workorders) { - return ` -
- - - - - - - - - - - - - ${workorders.map(wo => ` - - - - - - - - - `).join('')} - -
工单ID标题状态优先级创建时间操作
${wo.order_id || wo.id} -
- ${wo.title} -
-
- - ${this.getStatusText(wo.status)} - - - - ${this.getPriorityText(wo.priority)} - - ${new Date(wo.created_at).toLocaleString()} - -
-
- `; - } - - generateAlertPreviewHtml(alerts) { - return ` -
- - - - - - - - - - - - - ${alerts.map(alert => ` - - - - - - - - - `).join('')} - -
预警ID消息级别类型创建时间操作
${alert.id} -
- ${alert.message} -
-
- - ${this.getLevelText(alert.level)} - - ${this.getTypeText(alert.alert_type)}${new Date(alert.created_at).toLocaleString()} - -
-
- `; - } - - generateKnowledgePreviewHtml(knowledge) { - return ` -
- - - - - - - - - - - - - ${knowledge.map(item => ` - - - - - - - - - `).join('')} - -
ID标题分类验证状态创建时间操作
${item.id} -
- ${item.title} -
-
${item.category || '未分类'} - - ${item.is_verified ? '已验证' : '未验证'} - - ${new Date(item.created_at).toLocaleString()} -
- ${item.is_verified ? - `` : - `` - } - -
-
-
- `; - } - - getAlertLevelColor(level) { - const colorMap = { - 'critical': 'danger', - 'warning': 'warning', - 'info': 'info' - }; - return colorMap[level] || 'secondary'; - } - - goToFullView(type, status) { - // 关闭预览模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('statPreviewModal')); - if (modal) { - modal.hide(); - } - - // 切换到对应的标签页 - switch (type) { - case 'workorder': - this.switchTab('workorders'); - // 设置筛选器 - if (status !== 'all') { - setTimeout(() => { - const filter = document.getElementById('workorder-status-filter'); - if (filter) { - filter.value = status; - this.loadWorkOrders(); - } - }, 100); - } - break; - case 'alert': - this.switchTab('alerts'); - // 设置筛选器 - setTimeout(() => { - const filter = document.getElementById('alert-filter'); - if (filter) { - filter.value = status; - this.updateAlertsDisplay(); - } - }, 100); - break; - case 'knowledge': - this.switchTab('knowledge'); - break; - } - } - - // 模态框清理方法 - removeExistingModal() { - const existingModal = document.getElementById('statPreviewModal'); - if (existingModal) { - // 获取模态框实例并销毁 - const modalInstance = bootstrap.Modal.getInstance(existingModal); - if (modalInstance) { - modalInstance.dispose(); - } - existingModal.remove(); - } - - // 清理可能残留的遮罩 - const backdrops = document.querySelectorAll('.modal-backdrop'); - backdrops.forEach(backdrop => backdrop.remove()); - - // 恢复body的滚动 - document.body.classList.remove('modal-open'); - document.body.style.overflow = ''; - document.body.style.paddingRight = ''; - } - - cleanupModal() { - // 延迟清理,确保动画完成 - setTimeout(() => { - this.removeExistingModal(); - }, 300); - } - - getStatusColor(status) { - const colorMap = { - 'open': 'warning', - 'in_progress': 'info', - 'resolved': 'success', - 'closed': 'secondary' - }; - return colorMap[status] || 'secondary'; - } - - formatTime(timestamp) { - const date = new Date(timestamp); - const now = new Date(); - const diff = now - date; - - if (diff < 60000) { // 1分钟内 - return '刚刚'; - } else if (diff < 3600000) { // 1小时内 - return `${Math.floor(diff / 60000)}分钟前`; - } else if (diff < 86400000) { // 1天内 - return `${Math.floor(diff / 3600000)}小时前`; - } else { - return date.toLocaleDateString(); - } - } - - showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = `notification alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show`; - notification.innerHTML = ` - ${message} - - `; - - document.body.appendChild(notification); - - setTimeout(() => { - if (notification.parentNode) { - notification.parentNode.removeChild(notification); - } - }, 3000); - } - - getSimilarityExplanation(percent) { - if (percent >= 95) { - return "语义高度相似,AI建议与人工描述基本一致,建议自动审批"; - } else if (percent >= 90) { - return "语义较为相似,AI建议与人工描述大体一致,建议人工审核"; - } else if (percent >= 80) { - return "语义部分相似,AI建议与人工描述有一定差异,需要人工判断"; - } else if (percent >= 60) { - return "语义相似度较低,AI建议与人工描述差异较大,建议使用人工描述"; - } else { - return "语义差异很大,AI建议与人工描述差异很大,优先使用人工描述"; - } - } - - getSimilarityMessage(percent, approved, useHumanResolution = false) { - if (useHumanResolution) { - return `人工描述已保存!语义相似度: ${percent}%,AI准确率低于90%,将使用人工描述入库`; - } else if (approved) { - return `人工描述已保存!语义相似度: ${percent}%,已自动审批入库`; - } else if (percent >= 90) { - return `人工描述已保存!语义相似度: ${percent}%,建议人工审核后审批`; - } else if (percent >= 80) { - return `人工描述已保存!语义相似度: ${percent}%,需要人工判断是否审批`; - } else { - return `人工描述已保存!语义相似度: ${percent}%,建议使用人工描述入库`; - } - } - - showCreateWorkOrderModal() { - const modal = new bootstrap.Modal(document.getElementById('createWorkOrderModal')); - modal.show(); - } - - // 新增Agent对话功能 - async sendAgentMessage() { - const messageInput = document.getElementById('agent-message-input'); - const message = messageInput.value.trim(); - - if (!message) { - this.showNotification('请输入消息', 'warning'); - return; - } - - try { - // 显示发送状态 - const sendBtn = document.getElementById('send-agent-message'); - const originalText = sendBtn.innerHTML; - sendBtn.innerHTML = '发送中...'; - sendBtn.disabled = true; - - const response = await fetch('/api/agent/chat', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - message: message, - context: { - user_id: 'admin', - session_id: `agent_session_${Date.now()}` - } - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('Agent响应成功', 'success'); - // 清空输入框 - messageInput.value = ''; - // 刷新执行历史 - await this.loadAgentExecutionHistory(); - } else { - this.showNotification('Agent响应失败: ' + (data.error || '未知错误'), 'error'); - } - } catch (error) { - console.error('发送Agent消息失败:', error); - this.showNotification('发送Agent消息失败: ' + error.message, 'error'); - } finally { - // 恢复按钮状态 - const sendBtn = document.getElementById('send-agent-message'); - sendBtn.innerHTML = '发送'; - sendBtn.disabled = false; - } - } - - // 清空Agent对话 - clearAgentChat() { - document.getElementById('agent-message-input').value = ''; - this.showNotification('对话已清空', 'info'); - } - - // 加载Agent执行历史 - async loadAgentExecutionHistory() { - try { - const response = await fetch('/api/agent/action-history?limit=10'); - const data = await response.json(); - - if (data.success) { - this.updateExecutionHistory(data.history); - } - } catch (error) { - console.error('加载Agent执行历史失败:', error); - } - } - - // 触发示例动作 - async triggerSampleAction() { - try { - const response = await fetch('/api/agent/trigger-sample', { - method: 'POST' - }); - const data = await response.json(); - - if (data.success) { - this.showNotification('示例动作执行成功', 'success'); - await this.loadAgentExecutionHistory(); - } else { - this.showNotification('示例动作执行失败: ' + (data.error || '未知错误'), 'error'); - } - } catch (error) { - console.error('触发示例动作失败:', error); - this.showNotification('触发示例动作失败: ' + error.message, 'error'); - } - } - - // 清空Agent历史 - async clearAgentHistory() { - if (!confirm('确定要清空Agent执行历史吗?')) { - return; - } - - try { - const response = await fetch('/api/agent/clear-history', { - method: 'POST' - }); - const data = await response.json(); - - if (data.success) { - this.showNotification('Agent历史已清空', 'success'); - await this.loadAgentExecutionHistory(); - } else { - this.showNotification('清空Agent历史失败: ' + (data.error || '未知错误'), 'error'); - } - } catch (error) { - console.error('清空Agent历史失败:', error); - this.showNotification('清空Agent历史失败: ' + error.message, 'error'); - } - } -} - -// 飞书同步管理器 -class FeishuSyncManager { - constructor() { - this.loadConfig(); - this.refreshStatus(); - } - - async loadConfig() { - try { - const response = await fetch('/api/feishu-sync/config'); - const data = await response.json(); - - if (data.success) { - const config = data.config; - document.getElementById('appId').value = config.feishu.app_id || ''; - document.getElementById('appSecret').value = ''; - document.getElementById('appToken').value = config.feishu.app_token || ''; - document.getElementById('tableId').value = config.feishu.table_id || ''; - - // 显示配置状态 - const statusBadge = config.feishu.status === 'active' ? - '已配置' : - '未配置'; - - // 可以在这里添加状态显示 - } - } catch (error) { - console.error('加载配置失败:', error); - } - } - - async saveConfig() { - const config = { - app_id: document.getElementById('appId').value, - app_secret: document.getElementById('appSecret').value, - app_token: document.getElementById('appToken').value, - table_id: document.getElementById('tableId').value - }; - - if (!config.app_id || !config.app_secret || !config.app_token || !config.table_id) { - this.showNotification('请填写完整的配置信息', 'error'); - return; - } - - try { - const response = await fetch('/api/feishu-sync/config', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(config) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('配置保存成功', 'success'); - } else { - this.showNotification('配置保存失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('配置保存失败: ' + error.message, 'error'); - } - } - - async testConnection() { - try { - this.showNotification('正在测试连接...', 'info'); - - const response = await fetch('/api/feishu-sync/test-connection'); - const data = await response.json(); - - if (data.success) { - this.showNotification('飞书连接正常', 'success'); - } else { - this.showNotification('连接失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('连接测试失败: ' + error.message, 'error'); - } - } - - async syncFromFeishu() { - try { - const limit = document.getElementById('syncLimit').value; - this.showNotification('开始从飞书同步数据...', 'info'); - this.showProgress(true); - - const response = await fetch('/api/feishu-sync/sync-from-feishu', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - generate_ai_suggestions: false, - limit: parseInt(limit) - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - this.addSyncLog(data.message); - this.refreshStatus(); - } else { - this.showNotification('同步失败: ' + data.error, 'error'); - this.addSyncLog('同步失败: ' + data.error); - } - } catch (error) { - this.showNotification('同步失败: ' + error.message, 'error'); - this.addSyncLog('同步失败: ' + error.message); - } finally { - this.showProgress(false); - } - } - - async syncWithAI() { - try { - const limit = document.getElementById('syncLimit').value; - this.showNotification('开始同步数据并生成AI建议...', 'info'); - this.showProgress(true); - - const response = await fetch('/api/feishu-sync/sync-from-feishu', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - generate_ai_suggestions: true, - limit: parseInt(limit) - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - this.addSyncLog(data.message); - this.refreshStatus(); - } else { - this.showNotification('同步失败: ' + data.error, 'error'); - this.addSyncLog('同步失败: ' + data.error); - } - } catch (error) { - this.showNotification('同步失败: ' + error.message, 'error'); - this.addSyncLog('同步失败: ' + error.message); - } finally { - this.showProgress(false); - } - } - - // 打开字段映射管理页面 - openFieldMapping() { - const section = document.getElementById('fieldMappingSection'); - if (section.style.display === 'none') { - section.style.display = 'block'; - // 自动加载映射状态 - this.loadMappingStatus(); - } else { - section.style.display = 'none'; - } - } - - async previewFeishuData() { - try { - this.showNotification('正在获取飞书数据预览...', 'info'); - - const response = await fetch('/api/feishu-sync/preview-feishu-data'); - const data = await response.json(); - - if (data.success) { - this.displayPreviewData(data.preview_data); - this.showNotification(`获取到 ${data.total_count} 条预览数据`, 'success'); - } else { - this.showNotification('获取预览数据失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('获取预览数据失败: ' + error.message, 'error'); - } - } - - displayPreviewData(data) { - const tbody = document.querySelector('#previewTable tbody'); - tbody.innerHTML = ''; - - data.forEach(item => { - const row = document.createElement('tr'); - row.innerHTML = ` - ${item.record_id} - ${item.fields['TR Number'] || '-'} - ${item.fields['TR Description'] || '-'} - ${item.fields['Type of problem'] || '-'} - ${item.fields['Source'] || '-'} - ${item.fields['TR (Priority/Status)'] || '-'} - - - - `; - tbody.appendChild(row); - }); - - document.getElementById('previewSection').style.display = 'block'; - } - - async refreshStatus() { - try { - const response = await fetch('/api/feishu-sync/status'); - const data = await response.json(); - - if (data.success) { - const status = data.status; - document.getElementById('totalLocalWorkorders').textContent = status.total_local_workorders || 0; - document.getElementById('syncedWorkorders').textContent = status.synced_workorders || 0; - document.getElementById('unsyncedWorkorders').textContent = status.unsynced_workorders || 0; - } - } catch (error) { - console.error('刷新状态失败:', error); - } - } - - showProgress(show) { - const progress = document.getElementById('syncProgress'); - if (show) { - progress.style.display = 'block'; - const bar = progress.querySelector('.progress-bar'); - bar.style.width = '100%'; - } else { - setTimeout(() => { - progress.style.display = 'none'; - const bar = progress.querySelector('.progress-bar'); - bar.style.width = '0%'; - }, 1000); - } - } - - addSyncLog(message) { - const log = document.getElementById('syncLog'); - const timestamp = new Date().toLocaleString(); - const logEntry = document.createElement('div'); - logEntry.innerHTML = `[${timestamp}] ${message}`; - - if (log.querySelector('.text-muted')) { - log.innerHTML = ''; - } - - log.appendChild(logEntry); - log.scrollTop = log.scrollHeight; - } - - async exportConfig() { - try { - const response = await fetch('/api/feishu-sync/config/export'); - const data = await response.json(); - - if (data.success) { - // 创建下载链接 - const blob = new Blob([data.config], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `feishu_config_${new Date().toISOString().split('T')[0]}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - - this.showNotification('配置导出成功', 'success'); - } else { - this.showNotification('配置导出失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('配置导出失败: ' + error.message, 'error'); - } - } - - showImportModal() { - const modal = new bootstrap.Modal(document.getElementById('importConfigModal')); - modal.show(); - } - - async importConfig() { - try { - const configJson = document.getElementById('configJson').value.trim(); - - if (!configJson) { - this.showNotification('请输入配置JSON数据', 'warning'); - return; - } - - const response = await fetch('/api/feishu-sync/config/import', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ config: configJson }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('配置导入成功', 'success'); - this.loadConfig(); - this.refreshStatus(); - - // 关闭模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('importConfigModal')); - modal.hide(); - document.getElementById('configJson').value = ''; - } else { - this.showNotification('配置导入失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('配置导入失败: ' + error.message, 'error'); - } - } - - async resetConfig() { - if (confirm('确定要重置所有配置吗?此操作不可撤销!')) { - try { - const response = await fetch('/api/feishu-sync/config/reset', { - method: 'POST' - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('配置重置成功', 'success'); - this.loadConfig(); - this.refreshStatus(); - } else { - this.showNotification('配置重置失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('配置重置失败: ' + error.message, 'error'); - } - } - } - - async createWorkorder(recordId) { - if (confirm(`确定要从飞书记录 ${recordId} 创建工单吗?`)) { - try { - this.showNotification('正在创建工单...', 'info'); - - const response = await fetch('/api/feishu-sync/create-workorder', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - record_id: recordId - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification(data.message, 'success'); - // 刷新工单列表(如果用户在工单页面) - if (typeof window.refreshWorkOrders === 'function') { - window.refreshWorkOrders(); - } - } else { - this.showNotification('创建工单失败: ' + data.message, 'error'); - } - } catch (error) { - this.showNotification('创建工单失败: ' + error.message, 'error'); - } - } - } - - // 字段映射管理方法 - async discoverFields() { - try { - this.showNotification('正在发现字段...', 'info'); - - const response = await fetch('/api/feishu-sync/field-mapping/discover', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ limit: 5 }) - }); - - const data = await response.json(); - - if (data.success) { - this.displayDiscoveryResults(data.discovery_report); - this.showNotification('字段发现完成', 'success'); - } else { - this.showNotification('字段发现失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('字段发现失败: ' + error.message, 'error'); - } - } - - displayDiscoveryResults(report) { - const container = document.getElementById('fieldMappingContent'); - let html = ''; - - // 已映射字段 - if (report.mapped_fields && Object.keys(report.mapped_fields).length > 0) { - html += '
已映射字段
'; - for (const [feishuField, localField] of Object.entries(report.mapped_fields)) { - html += `
- ${feishuField}${localField} -
`; - } - html += '
'; - } - - // 未映射字段和建议 - if (report.unmapped_fields && report.unmapped_fields.length > 0) { - html += '
未映射字段
'; - for (const field of report.unmapped_fields) { - html += `
- ${field}`; - - const suggestions = report.suggested_mappings[field] || []; - if (suggestions.length > 0) { - html += '
建议映射:'; - suggestions.slice(0, 2).forEach(suggestion => { - html += `
- ${suggestion.local_field} - (${suggestion.reason}) - -
`; - }); - html += '
'; - } - - html += '
'; - } - html += '
'; - } - - container.innerHTML = html; - } - - async applySuggestion(feishuField, localField) { - if (confirm(`确定要将 "${feishuField}" 映射到 "${localField}" 吗?`)) { - try { - const response = await fetch('/api/feishu-sync/field-mapping/add', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - feishu_field: feishuField, - local_field: localField, - priority: 3 - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('映射添加成功!', 'success'); - this.discoverFields(); // 重新发现字段 - } else { - this.showNotification('添加映射失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('请求失败: ' + error.message, 'error'); - } - } - } - - async loadMappingStatus() { - try { - const response = await fetch('/api/feishu-sync/field-mapping/status'); - const data = await response.json(); - - if (data.success) { - this.displayMappingStatus(data.status); - } else { - this.showNotification('获取映射状态失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('请求失败: ' + error.message, 'error'); - } - } - - displayMappingStatus(status) { - const container = document.getElementById('fieldMappingContent'); - let html = ''; - - html += `
-
-
-
-
${status.total_mappings}
-

直接映射

-
-
-
-
-
-
-
${status.total_aliases}
-

别名映射

-
-
-
-
-
-
-
${status.total_patterns}
-

模式匹配

-
-
-
-
-
-
-
- ${status.auto_mapping_enabled ? '启用' : '禁用'} -
-

自动映射

-
-
-
-
`; - - // 显示当前映射 - if (status.field_mapping && Object.keys(status.field_mapping).length > 0) { - html += '
当前字段映射:
'; - for (const [feishuField, localField] of Object.entries(status.field_mapping)) { - html += `
-
- ${feishuField}${localField} - -
-
`; - } - html += '
'; - } - - container.innerHTML = html; - } - - async removeMapping(feishuField) { - if (confirm(`确定要删除映射 "${feishuField}" 吗?`)) { - try { - const response = await fetch('/api/feishu-sync/field-mapping/remove', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - feishu_field: feishuField - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('映射删除成功!', 'success'); - this.loadMappingStatus(); // 刷新状态 - } else { - this.showNotification('删除映射失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('请求失败: ' + error.message, 'error'); - } - } - } - - showAddMappingModal() { - // 简单的添加映射功能 - const feishuField = prompt('请输入飞书字段名:'); - if (!feishuField) return; - - const localField = prompt('请输入本地字段名 (如: order_id, description, category):'); - if (!localField) return; - - this.addFieldMapping(feishuField, localField); - } - - async addFieldMapping(feishuField, localField) { - try { - const response = await fetch('/api/feishu-sync/field-mapping/add', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - feishu_field: feishuField, - local_field: localField, - priority: 3 - }) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('映射添加成功!', 'success'); - this.loadMappingStatus(); // 刷新状态 - } else { - this.showNotification('添加映射失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('请求失败: ' + error.message, 'error'); - } - } - - async checkPermissions() { - try { - this.showNotification('正在检查飞书权限...', 'info'); - - const response = await fetch('/api/feishu-sync/check-permissions'); - const data = await response.json(); - - if (data.success) { - this.displayPermissionCheck(data.permission_check, data.summary); - this.showNotification('权限检查完成', 'success'); - } else { - this.showNotification('权限检查失败: ' + data.error, 'error'); - } - } catch (error) { - this.showNotification('权限检查失败: ' + error.message, 'error'); - } - } - - displayPermissionCheck(permissionCheck, summary) { - const container = document.getElementById('fieldMappingContent'); - - let html = '
飞书权限检查结果
'; - - // 整体状态 - const statusClass = permissionCheck.success ? 'success' : 'danger'; - const statusIcon = permissionCheck.success ? 'check-circle' : 'exclamation-triangle'; - html += `
- - 整体状态: ${permissionCheck.success ? '正常' : '异常'} -
`; - - // 检查项目 - html += '
检查项目:
'; - for (const [checkName, checkResult] of Object.entries(permissionCheck.checks)) { - const statusClass = checkResult.status === 'success' ? 'success' : - checkResult.status === 'warning' ? 'warning' : 'danger'; - const statusIcon = checkResult.status === 'success' ? 'check-circle' : - checkResult.status === 'warning' ? 'exclamation-triangle' : 'times-circle'; - - html += `
- - ${checkName}: ${checkResult.message} -
`; - } - - // 修复建议 - if (permissionCheck.recommendations && permissionCheck.recommendations.length > 0) { - html += '
修复建议:
    '; - permissionCheck.recommendations.forEach(rec => { - html += `
  • ${rec}
  • `; - }); - html += '
'; - } - - // 错误信息 - if (permissionCheck.errors && permissionCheck.errors.length > 0) { - html += '
错误信息:
    '; - permissionCheck.errors.forEach(error => { - html += `
  • ${error}
  • `; - }); - html += '
'; - } - - html += '
'; - - container.innerHTML = html; - - // 显示字段映射管理区域 - const section = document.getElementById('fieldMappingSection'); - section.style.display = 'block'; - } - - showNotification(message, type = 'info') { - const container = document.getElementById('notificationContainer'); - const alert = document.createElement('div'); - alert.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show`; - alert.innerHTML = ` - ${message} - - `; - - container.appendChild(alert); - - setTimeout(() => { - if (alert.parentNode) { - alert.parentNode.removeChild(alert); - } - }, 5000); - } -} - -// 初始化应用 -let dashboard; -let feishuSync; -document.addEventListener('DOMContentLoaded', () => { - dashboard = new TSPDashboard(); - feishuSync = new FeishuSyncManager(); -}); +// test diff --git a/src/web/templates/dashboard.html b/src/web/templates/dashboard.html index 63eee7a..b207884 100644 --- a/src/web/templates/dashboard.html +++ b/src/web/templates/dashboard.html @@ -572,12 +572,12 @@ - +
- +
- +
- +
快速操作
@@ -601,7 +601,7 @@
- +
会话信息
@@ -611,7 +611,7 @@
- +
@@ -625,7 +625,7 @@
- +
@@ -633,10 +633,10 @@

请点击"开始对话"按钮开始聊天

- +
-
- +
- +
- +
对话记忆
@@ -2230,13 +2230,13 @@ 请先下载模板文件,按照模板格式填写工单信息,然后上传Excel文件进行导入。
- +
支持 .xlsx 和 .xls 格式,文件大小不超过16MB
- +
Excel文件列名说明: @@ -2301,7 +2301,7 @@
- +
@@ -2311,7 +2311,7 @@ 正在导入工单...
- +
diff --git a/src/web/websocket_server.py b/src/web/websocket_server.py index 6ae17ef..425cec8 100644 --- a/src/web/websocket_server.py +++ b/src/web/websocket_server.py @@ -213,18 +213,22 @@ class WebSocketServer: async def handle_client(self, websocket: WebSocketServerProtocol, path: str): """处理客户端连接""" - # 检查连接头 - headers = websocket.request_headers - connection = headers.get("Connection", "").lower() - - # 处理不同的连接头格式 - if "upgrade" not in connection and "keep-alive" in connection: - logger.warning(f"收到非标准连接头: {connection}") - # 对于keep-alive连接头,我们仍然接受连接 - elif "upgrade" not in connection: - logger.warning(f"连接头不包含upgrade: {connection}") - await websocket.close(code=1002, reason="Invalid connection header") - return + # 检查连接头(如果可用) + try: + if hasattr(websocket, 'request_headers'): + headers = websocket.request_headers + connection = headers.get("Connection", "").lower() + + # 处理不同的连接头格式 + if "upgrade" not in connection and "keep-alive" in connection: + logger.warning(f"收到非标准连接头: {connection}") + # 对于keep-alive连接头,我们仍然接受连接 + elif "upgrade" not in connection: + logger.warning(f"连接头不包含upgrade: {connection}") + # 在websockets 15.x中,连接已经在serve时验证,所以这里只记录警告 + except AttributeError: + # websockets 15.x版本可能没有request_headers属性,跳过检查 + pass await self.register_client(websocket) @@ -243,19 +247,15 @@ class WebSocketServer: logger.info(f"启动WebSocket服务器: ws://{self.host}:{self.port}") # 添加CORS支持 - async def handle_client_with_cors(websocket: WebSocketServerProtocol, path: str): - # 设置CORS头 - if websocket.request_headers.get("Origin"): - # 允许跨域连接 - pass - await self.handle_client(websocket, path) + async def handle_client_with_cors(websocket: WebSocketServerProtocol, path: str = None): + # CORS处理:websockets库默认允许所有来源连接 + # 如果需要限制,可以在serve时使用additional_headers参数 + await self.handle_client(websocket, path or "") async with websockets.serve( handle_client_with_cors, self.host, - self.port, - # 添加额外的服务器选项 - process_request=self._process_request + self.port ): await asyncio.Future() # 保持服务器运行 diff --git a/start_dashboard.py b/start_dashboard.py index d82f5c5..b19ef58 100644 --- a/start_dashboard.py +++ b/start_dashboard.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -启动TSP智能助手综合管理平台 +TSP智能助手综合管理平台 """ import sys diff --git a/test_git_push.bat b/test_git_push.bat new file mode 100644 index 0000000..84dba13 --- /dev/null +++ b/test_git_push.bat @@ -0,0 +1,79 @@ +@echo off +chcp 65001 >nul +setlocal enabledelayedexpansion + +echo ======================================== +echo GitͲԽű +echo ======================================== +echo. + +:: Git״̬ +echo [1] Git״̬... +git status --porcelain >nul 2>&1 +if %errorlevel% neq 0 ( + echo Gitδʼ + pause + exit /b 1 +) +echo Git״̬ +echo. + +:: ȡǰ֧ +echo [2] ȡǰ֧... +set current_branch= +for /f "tokens=*" %%b in ('git branch --show-current 2^>nul') do set current_branch=%%b + +if "!current_branch!"=="" ( + echo ޷ȡǰ֧ʹĬϷ֧main + set current_branch=main +) else ( + echo ǰ֧: !current_branch! +) +echo. + +:: Զֿ̲ +echo [3] Զֿ̲... +git remote -v +if %errorlevel% neq 0 ( + echo δԶֿ̲ + pause + exit /b 1 +) +echo. + +:: +echo [4] ͵Զ... +echo ֧: !current_branch! +echo. + +git push origin !current_branch! 2>&1 +if %errorlevel% equ 0 ( + echo. + echo ͳɹ +) else ( + echo. + echo ʧ + echo. + echo ܵԭ: + echo 1. Զ̷֧ڣ... + git push -u origin !current_branch! 2>&1 + if %errorlevel% equ 0 ( + echo ͳɹΣ + ) else ( + echo Ȼʧ + echo. + echo : + echo - + echo - ֤ãSSHԿToken + echo - Զַֿ̲ + pause + exit /b 1 + ) +) + +echo. +echo ======================================== +echo +echo ======================================== +pause + diff --git a/tsp_assistant.db b/tsp_assistant.db index 8488fad..5d14bcb 100644 Binary files a/tsp_assistant.db and b/tsp_assistant.db differ