docs: update README and CLAUDE.md to v2.2.0

- Added documentation for audit tracking (IP address, invocation method).
- Updated database model descriptions for enhanced WorkOrder and Conversation fields.
- Documented the new UnifiedConfig system.
- Reflected enhanced logging transparency for knowledge base parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhaojie
2026-02-11 00:08:09 +08:00
parent 2026007045
commit c3560b43fd
218 changed files with 3354 additions and 5096 deletions

View File

@@ -1,306 +0,0 @@
#!/bin/bash
# TSP智能助手部署脚本
# 支持多环境部署、版本管理、自动备份
set -e # 遇到错误立即退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查依赖
check_dependencies() {
log_info "检查系统依赖..."
# 检查Python
if ! command -v python3 &> /dev/null; then
log_error "Python3 未安装"
exit 1
fi
# 检查pip
if ! command -v pip3 &> /dev/null; then
log_error "pip3 未安装"
exit 1
fi
# 检查Git
if ! command -v git &> /dev/null; then
log_error "Git 未安装"
exit 1
fi
log_info "依赖检查完成"
}
# 创建虚拟环境
setup_venv() {
local venv_path=$1
log_info "创建虚拟环境: $venv_path"
if [ ! -d "$venv_path" ]; then
python3 -m venv "$venv_path"
fi
source "$venv_path/bin/activate"
pip install --upgrade pip
log_info "虚拟环境设置完成"
}
# 安装依赖
install_dependencies() {
log_info "安装Python依赖..."
pip install -r requirements.txt
log_info "依赖安装完成"
}
# 数据库迁移
run_migrations() {
log_info "运行数据库迁移..."
# 检查数据库文件
if [ ! -f "tsp_assistant.db" ]; then
log_info "初始化数据库..."
python init_database.py
fi
log_info "数据库迁移完成"
}
# 创建systemd服务文件
create_systemd_service() {
local service_name=$1
local app_path=$2
local service_file="/etc/systemd/system/${service_name}.service"
log_info "创建systemd服务文件: $service_file"
sudo tee "$service_file" > /dev/null <<EOF
[Unit]
Description=TSP智能助手服务
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=$app_path
Environment=PATH=$app_path/venv/bin
ExecStart=$app_path/venv/bin/python start_dashboard.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable "$service_name"
log_info "systemd服务创建完成"
}
# 创建nginx配置
create_nginx_config() {
local domain=$1
local app_port=$2
local config_file="/etc/nginx/sites-available/tsp_assistant"
log_info "创建nginx配置: $config_file"
sudo tee "$config_file" > /dev/null <<EOF
server {
listen 80;
server_name $domain;
location / {
proxy_pass http://127.0.0.1:$app_port;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
location /static {
alias $app_path/src/web/static;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
EOF
# 启用站点
sudo ln -sf "$config_file" /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
log_info "nginx配置完成"
}
# 主部署函数
deploy() {
local environment=${1:-production}
local domain=${2:-localhost}
local app_port=${3:-5000}
log_info "开始部署TSP智能助手到 $environment 环境"
# 设置部署路径
case $environment in
development)
DEPLOY_PATH="./dev_deploy"
SERVICE_NAME=""
;;
staging)
DEPLOY_PATH="/opt/tsp_assistant_staging"
SERVICE_NAME="tsp_assistant_staging"
;;
production)
DEPLOY_PATH="/opt/tsp_assistant"
SERVICE_NAME="tsp_assistant"
;;
*)
log_error "未知环境: $environment"
exit 1
;;
esac
# 检查依赖
check_dependencies
# 创建部署目录
log_info "创建部署目录: $DEPLOY_PATH"
sudo mkdir -p "$DEPLOY_PATH"
sudo chown $USER:$USER "$DEPLOY_PATH"
# 复制文件
log_info "复制应用文件..."
cp -r . "$DEPLOY_PATH/"
cd "$DEPLOY_PATH"
# 设置虚拟环境
setup_venv "venv"
# 安装依赖
install_dependencies
# 运行迁移
run_migrations
# 创建服务文件(非开发环境)
if [ "$environment" != "development" ] && [ -n "$SERVICE_NAME" ]; then
create_systemd_service "$SERVICE_NAME" "$DEPLOY_PATH"
create_nginx_config "$domain" "$app_port"
fi
log_info "部署完成!"
if [ "$environment" != "development" ]; then
log_info "启动服务..."
sudo systemctl start "$SERVICE_NAME"
sudo systemctl status "$SERVICE_NAME"
else
log_info "开发环境部署完成,使用以下命令启动:"
log_info "cd $DEPLOY_PATH && source venv/bin/activate && python start_dashboard.py"
fi
}
# 回滚函数
rollback() {
local backup_name=$1
if [ -z "$backup_name" ]; then
log_error "请指定备份名称"
exit 1
fi
log_info "回滚到备份: $backup_name"
# 停止服务
sudo systemctl stop tsp_assistant
# 恢复备份
if [ -d "backups/$backup_name" ]; then
sudo rm -rf /opt/tsp_assistant
sudo cp -r "backups/$backup_name" /opt/tsp_assistant
sudo chown -R www-data:www-data /opt/tsp_assistant
# 重启服务
sudo systemctl start tsp_assistant
log_info "回滚完成"
else
log_error "备份不存在: $backup_name"
exit 1
fi
}
# 版本检查
check_version() {
log_info "检查版本信息..."
if [ -f "version.json" ]; then
local version=$(python3 -c "import json; print(json.load(open('version.json'))['version'])" 2>/dev/null || echo "unknown")
log_info "当前版本: $version"
else
log_warn "版本文件不存在"
fi
}
# 创建部署包
create_deployment_package() {
local package_name="tsp_assistant_$(date +%Y%m%d_%H%M%S).tar.gz"
log_info "创建部署包: $package_name"
# 排除不需要的文件
tar --exclude='.git' \
--exclude='__pycache__' \
--exclude='*.pyc' \
--exclude='.env' \
--exclude='logs/*' \
--exclude='backups/*' \
--exclude='dev_deploy' \
-czf "$package_name" .
log_info "部署包创建完成: $package_name"
echo "$package_name"
}
# 主函数
main() {
case ${1:-deploy} in
deploy)
check_version
deploy "$2" "$3" "$4"
;;
rollback)
rollback "$2"
;;
package)
create_deployment_package
;;
*)
echo "用法: $0 {deploy|rollback|package} [environment] [domain] [port]"
echo "环境: development, staging, production"
echo ""
echo "命令说明:"
echo " deploy - 部署到指定环境"
echo " rollback - 回滚到指定备份"
echo " package - 创建部署包"
exit 1
;;
esac
}
main "$@"

View File

@@ -1,204 +0,0 @@
#!/bin/bash
# TSP智能助手Docker部署脚本
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查Docker和Docker Compose
check_dependencies() {
log_info "检查依赖..."
if ! command -v docker &> /dev/null; then
log_error "Docker未安装请先安装Docker"
exit 1
fi
if ! command -v docker-compose &> /dev/null; then
log_error "Docker Compose未安装请先安装Docker Compose"
exit 1
fi
log_success "依赖检查通过"
}
# 创建必要的目录
create_directories() {
log_info "创建必要的目录..."
mkdir -p logs/nginx
mkdir -p monitoring/grafana/provisioning/datasources
mkdir -p monitoring/grafana/provisioning/dashboards
mkdir -p ssl
mkdir -p data
mkdir -p backups
mkdir -p uploads
mkdir -p config
log_success "目录创建完成"
}
# 构建镜像
build_images() {
log_info "构建Docker镜像..."
# 构建主应用镜像
docker-compose build --no-cache tsp-assistant
log_success "镜像构建完成"
}
# 启动服务
start_services() {
log_info "启动服务..."
# 启动基础服务MySQL, Redis
docker-compose up -d mysql redis
# 等待数据库启动
log_info "等待数据库启动..."
sleep 30
# 启动主应用
docker-compose up -d tsp-assistant
# 启动其他服务
docker-compose up -d nginx prometheus grafana
log_success "服务启动完成"
}
# 检查服务状态
check_services() {
log_info "检查服务状态..."
sleep 10
# 检查主应用
if curl -f http://localhost:5000/api/health &> /dev/null; then
log_success "TSP助手服务正常"
else
log_warning "TSP助手服务可能未完全启动"
fi
# 检查Nginx
if curl -f http://localhost/health &> /dev/null; then
log_success "Nginx服务正常"
else
log_warning "Nginx服务可能未完全启动"
fi
# 检查Prometheus
if curl -f http://localhost:9090 &> /dev/null; then
log_success "Prometheus服务正常"
else
log_warning "Prometheus服务可能未完全启动"
fi
# 检查Grafana
if curl -f http://localhost:3000 &> /dev/null; then
log_success "Grafana服务正常"
else
log_warning "Grafana服务可能未完全启动"
fi
}
# 显示服务信息
show_info() {
log_info "服务访问信息:"
echo " TSP助手: http://localhost:5000"
echo " Nginx代理: http://localhost"
echo " Prometheus: http://localhost:9090"
echo " Grafana: http://localhost:3000 (admin/admin123456)"
echo " MySQL: localhost:3306 (root/root123456)"
echo " Redis: localhost:6379 (密码: redis123456)"
echo ""
log_info "查看日志命令:"
echo " docker-compose logs -f tsp-assistant"
echo " docker-compose logs -f mysql"
echo " docker-compose logs -f redis"
echo " docker-compose logs -f nginx"
}
# 停止服务
stop_services() {
log_info "停止服务..."
docker-compose down
log_success "服务已停止"
}
# 清理资源
cleanup() {
log_info "清理Docker资源..."
docker system prune -f
log_success "清理完成"
}
# 主函数
main() {
case "${1:-start}" in
"start")
check_dependencies
create_directories
build_images
start_services
check_services
show_info
;;
"stop")
stop_services
;;
"restart")
stop_services
sleep 5
start_services
check_services
show_info
;;
"cleanup")
stop_services
cleanup
;;
"logs")
docker-compose logs -f "${2:-tsp-assistant}"
;;
"status")
docker-compose ps
;;
*)
echo "用法: $0 {start|stop|restart|cleanup|logs|status}"
echo " start - 启动所有服务"
echo " stop - 停止所有服务"
echo " restart - 重启所有服务"
echo " cleanup - 清理Docker资源"
echo " logs - 查看日志 (可选指定服务名)"
echo " status - 查看服务状态"
exit 1
;;
esac
}
# 执行主函数
main "$@"

View File

@@ -1,277 +0,0 @@
#!/bin/bash
# TSP智能助手监控脚本
# 配置变量
APP_NAME="tsp_assistant"
SERVICE_NAME="tsp_assistant"
HEALTH_URL="http://localhost:5000/api/health"
LOG_FILE="./logs/monitor.log"
ALERT_EMAIL="admin@example.com"
ALERT_PHONE="13800138000"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] INFO${NC} $1" | tee -a "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARN${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR${NC} $1" | tee -a "$LOG_FILE"
}
# 发送告警
send_alert() {
local message=$1
local level=$2
log_error "告警: $message"
# 发送邮件告警
if command -v mail &> /dev/null; then
echo "$message" | mail -s "[$level] TSP助手告警" "$ALERT_EMAIL"
fi
# 发送短信告警(需要配置短信服务)
# curl -X POST "https://api.sms.com/send" \
# -d "phone=$ALERT_PHONE" \
# -d "message=$message"
}
# 检查服务状态
check_service_status() {
if systemctl is-active --quiet "$SERVICE_NAME"; then
return 0
else
return 1
fi
}
# 检查健康状态
check_health() {
local response_code
response_code=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null)
if [ "$response_code" = "200" ]; then
return 0
else
return 1
fi
}
# 检查响应时间
check_response_time() {
local response_time
response_time=$(curl -s -o /dev/null -w "%{time_total}" "$HEALTH_URL" 2>/dev/null)
# 响应时间超过5秒认为异常
if (( $(echo "$response_time > 5.0" | bc -l) )); then
return 1
else
return 0
fi
}
# 检查系统资源
check_system_resources() {
local cpu_usage
local memory_usage
local disk_usage
# CPU使用率
cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | awk -F'%' '{print $1}')
# 内存使用率
memory_usage=$(free | grep Mem | awk '{printf "%.2f", $3/$2 * 100.0}')
# 磁盘使用率
disk_usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
# 检查阈值
if (( $(echo "$cpu_usage > 80" | bc -l) )); then
send_alert "CPU使用率过高: ${cpu_usage}%" "HIGH"
fi
if (( $(echo "$memory_usage > 80" | bc -l) )); then
send_alert "内存使用率过高: ${memory_usage}%" "HIGH"
fi
if [ "$disk_usage" -gt 80 ]; then
send_alert "磁盘使用率过高: ${disk_usage}%" "HIGH"
fi
log_info "系统资源 - CPU: ${cpu_usage}%, 内存: ${memory_usage}%, 磁盘: ${disk_usage}%"
}
# 检查日志错误
check_log_errors() {
local log_file="./logs/tsp_assistant.log"
local error_count
if [ -f "$log_file" ]; then
# 检查最近5分钟的错误日志
error_count=$(tail -n 100 "$log_file" | grep -c "ERROR" 2>/dev/null || echo "0")
if [ "$error_count" -gt 10 ]; then
send_alert "最近5分钟错误日志过多: $error_count" "MEDIUM"
fi
fi
}
# 检查数据库连接
check_database() {
local db_file="./tsp_assistant.db"
if [ -f "$db_file" ]; then
# 检查数据库文件大小
local db_size
db_size=$(du -h "$db_file" | cut -f1)
log_info "数据库大小: $db_size"
# 检查数据库是否可读
if ! sqlite3 "$db_file" "SELECT 1;" > /dev/null 2>&1; then
send_alert "数据库连接失败" "CRITICAL"
return 1
fi
fi
return 0
}
# 自动重启服务
restart_service() {
log_warn "尝试重启服务..."
sudo systemctl restart "$SERVICE_NAME"
sleep 10
if check_service_status && check_health; then
log_info "服务重启成功"
return 0
else
log_error "服务重启失败"
return 1
fi
}
# 主监控循环
monitor_loop() {
local consecutive_failures=0
local max_failures=3
while true; do
log_info "开始监控检查..."
# 检查服务状态
if ! check_service_status; then
log_error "服务未运行"
send_alert "TSP助手服务未运行" "CRITICAL"
consecutive_failures=$((consecutive_failures + 1))
else
# 检查健康状态
if ! check_health; then
log_error "健康检查失败"
send_alert "TSP助手健康检查失败" "HIGH"
consecutive_failures=$((consecutive_failures + 1))
else
# 检查响应时间
if ! check_response_time; then
log_warn "响应时间过长"
send_alert "TSP助手响应时间过长" "MEDIUM"
fi
consecutive_failures=0
fi
fi
# 检查系统资源
check_system_resources
# 检查日志错误
check_log_errors
# 检查数据库
check_database
# 连续失败处理
if [ "$consecutive_failures" -ge "$max_failures" ]; then
log_error "连续失败次数达到阈值,尝试重启服务"
if restart_service; then
consecutive_failures=0
else
send_alert "TSP助手服务重启失败需要人工干预" "CRITICAL"
fi
fi
# 等待下次检查
sleep 60
done
}
# 一次性检查
single_check() {
log_info "执行一次性健康检查..."
if check_service_status; then
log_info "✓ 服务运行正常"
else
log_error "✗ 服务未运行"
exit 1
fi
if check_health; then
log_info "✓ 健康检查通过"
else
log_error "✗ 健康检查失败"
exit 1
fi
if check_response_time; then
log_info "✓ 响应时间正常"
else
log_warn "⚠ 响应时间过长"
fi
check_system_resources
check_log_errors
check_database
log_info "健康检查完成"
}
# 主函数
main() {
# 创建日志目录
mkdir -p logs
case ${1:-monitor} in
monitor)
log_info "启动TSP助手监控服务..."
monitor_loop
;;
check)
single_check
;;
restart)
restart_service
;;
*)
echo "用法: $0 {monitor|check|restart}"
echo " monitor - 持续监控模式"
echo " check - 一次性健康检查"
echo " restart - 重启服务"
exit 1
;;
esac
}
# 执行主函数
main "$@"

View File

@@ -1,285 +0,0 @@
@echo off
REM TSP智能助手快速更新脚本 (Windows)
REM 支持热更新和完整更新
setlocal enabledelayedexpansion
REM 颜色定义
set "GREEN=[32m"
set "YELLOW=[33m"
set "RED=[31m"
set "NC=[0m"
REM 配置变量
set "APP_NAME=tsp_assistant"
set "DEPLOY_PATH=."
set "BACKUP_PATH=.\backups"
set "HEALTH_URL=http://localhost:5000/api/health"
REM 解析参数
set "ACTION=%1"
set "SOURCE_PATH=%2"
set "ENVIRONMENT=%3"
if "%ACTION%"=="" (
echo 用法: %0 {check^|hot-update^|full-update^|auto-update^|rollback} [源路径] [环境]
echo.
echo 命令说明:
echo check - 检查更新可用性
echo hot-update - 热更新(不重启服务)
echo full-update - 完整更新(重启服务)
echo auto-update - 自动更新(智能选择)
echo rollback - 回滚到指定备份
echo.
echo 环境: development, staging, production
exit /b 1
)
if "%ENVIRONMENT%"=="" set "ENVIRONMENT=production"
if "%SOURCE_PATH%"=="" set "SOURCE_PATH=."
REM 日志函数
:log_info
echo %GREEN%[INFO]%NC% %~1
goto :eof
:log_warn
echo %YELLOW%[WARN]%NC% %~1
goto :eof
:log_error
echo %RED%[ERROR]%NC% %~1
goto :eof
REM 检查更新可用性
:check_update
call :log_info "检查更新可用性..."
if not exist "%SOURCE_PATH%\version.json" (
call :log_error "源路径中未找到版本文件"
exit /b 1
)
REM 比较版本
for /f "tokens=2 delims=:" %%a in ('findstr "version" "%SOURCE_PATH%\version.json"') do (
set "NEW_VERSION=%%a"
set "NEW_VERSION=!NEW_VERSION: =!"
set "NEW_VERSION=!NEW_VERSION:"=!"
set "NEW_VERSION=!NEW_VERSION:,=!"
)
if exist "version.json" (
for /f "tokens=2 delims=:" %%a in ('findstr "version" "version.json"') do (
set "CURRENT_VERSION=%%a"
set "CURRENT_VERSION=!CURRENT_VERSION: =!"
set "CURRENT_VERSION=!CURRENT_VERSION:"=!"
set "CURRENT_VERSION=!CURRENT_VERSION:,=!"
)
) else (
set "CURRENT_VERSION=unknown"
)
if "!NEW_VERSION!"=="!CURRENT_VERSION!" (
call :log_info "没有更新可用 (当前版本: !CURRENT_VERSION!)"
) else (
call :log_info "发现更新: !CURRENT_VERSION! -> !NEW_VERSION!"
)
goto :eof
REM 创建备份
:create_backup
set "TIMESTAMP=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
set "TIMESTAMP=!TIMESTAMP: =0!"
set "BACKUP_NAME=%APP_NAME%_backup_!TIMESTAMP!"
call :log_info "创建备份: !BACKUP_NAME!"
if not exist "%BACKUP_PATH%" mkdir "%BACKUP_PATH%"
mkdir "%BACKUP_PATH%\!BACKUP_NAME!"
REM 备份应用文件
if exist "%DEPLOY_PATH%" (
call :log_info "备份应用文件..."
xcopy "%DEPLOY_PATH%\*" "%BACKUP_PATH%\!BACKUP_NAME!\" /E /I /Y
)
REM 备份数据库
if exist "%DEPLOY_PATH%\tsp_assistant.db" (
call :log_info "备份数据库..."
mkdir "%BACKUP_PATH%\!BACKUP_NAME!\database"
copy "%DEPLOY_PATH%\tsp_assistant.db" "%BACKUP_PATH%\!BACKUP_NAME!\database\"
)
call :log_info "备份完成: !BACKUP_NAME!"
echo !BACKUP_NAME!
goto :eof
REM 热更新
:hot_update
call :log_info "开始热更新..."
REM 支持热更新的文件列表
set "HOT_UPDATE_FILES=src\web\static\js\dashboard.js src\web\static\css\style.css src\web\templates\dashboard.html src\web\app.py"
set "UPDATED_COUNT=0"
for %%f in (%HOT_UPDATE_FILES%) do (
if exist "%SOURCE_PATH%\%%f" (
call :log_info "更新文件: %%f"
if not exist "%DEPLOY_PATH%\%%f" mkdir "%DEPLOY_PATH%\%%f" 2>nul
copy "%SOURCE_PATH%\%%f" "%DEPLOY_PATH%\%%f" /Y >nul
set /a UPDATED_COUNT+=1
)
)
if !UPDATED_COUNT! gtr 0 (
call :log_info "热更新完成,更新了 !UPDATED_COUNT! 个文件"
) else (
call :log_info "没有文件需要热更新"
)
goto :eof
REM 完整更新
:full_update
call :log_info "开始完整更新..."
REM 创建备份
call :create_backup
set "BACKUP_NAME=!BACKUP_NAME!"
REM 停止服务(如果运行中)
call :log_info "停止服务..."
taskkill /f /im python.exe 2>nul || echo 服务未运行
REM 更新文件
call :log_info "更新应用文件..."
if exist "%DEPLOY_PATH%" rmdir /s /q "%DEPLOY_PATH%"
mkdir "%DEPLOY_PATH%"
xcopy "%SOURCE_PATH%\*" "%DEPLOY_PATH%\" /E /I /Y
REM 安装依赖
call :log_info "安装依赖..."
cd "%DEPLOY_PATH%"
if exist "requirements.txt" (
pip install -r requirements.txt
)
REM 运行数据库迁移
call :log_info "运行数据库迁移..."
if exist "init_database.py" (
python init_database.py
)
REM 启动服务
call :log_info "启动服务..."
start /b python start_dashboard.py
REM 等待服务启动
call :log_info "等待服务启动..."
timeout /t 15 /nobreak >nul
REM 健康检查
call :log_info "执行健康检查..."
set "RETRY_COUNT=0"
set "MAX_RETRIES=10"
:health_check_loop
if !RETRY_COUNT! geq !MAX_RETRIES! (
call :log_error "健康检查失败,开始回滚..."
call :rollback !BACKUP_NAME!
exit /b 1
)
curl -f "%HEALTH_URL%" >nul 2>&1
if !errorlevel! equ 0 (
call :log_info "健康检查通过!"
call :log_info "更新成功!"
call :log_info "备份名称: !BACKUP_NAME!"
exit /b 0
) else (
call :log_warn "健康检查失败,重试中... (!RETRY_COUNT!/!MAX_RETRIES!)"
set /a RETRY_COUNT+=1
timeout /t 5 /nobreak >nul
goto :health_check_loop
)
REM 回滚
:rollback
set "BACKUP_NAME=%1"
if "%BACKUP_NAME%"=="" (
call :log_error "请指定备份名称"
exit /b 1
)
call :log_info "开始回滚到备份: !BACKUP_NAME!"
if not exist "%BACKUP_PATH%\!BACKUP_NAME!" (
call :log_error "备份不存在: !BACKUP_NAME!"
exit /b 1
)
REM 停止服务
call :log_info "停止服务..."
taskkill /f /im python.exe 2>nul || echo 服务未运行
REM 恢复文件
call :log_info "恢复文件..."
if exist "%DEPLOY_PATH%" rmdir /s /q "%DEPLOY_PATH%"
mkdir "%DEPLOY_PATH%"
xcopy "%BACKUP_PATH%\!BACKUP_NAME!\*" "%DEPLOY_PATH%\" /E /I /Y
REM 恢复数据库
if exist "%BACKUP_PATH%\!BACKUP_NAME!\database\tsp_assistant.db" (
call :log_info "恢复数据库..."
copy "%BACKUP_PATH%\!BACKUP_NAME!\database\tsp_assistant.db" "%DEPLOY_PATH%\"
)
REM 启动服务
call :log_info "启动服务..."
cd "%DEPLOY_PATH%"
start /b python start_dashboard.py
REM 等待服务启动
timeout /t 15 /nobreak >nul
REM 健康检查
curl -f "%HEALTH_URL%" >nul 2>&1
if !errorlevel! equ 0 (
call :log_info "回滚成功!"
) else (
call :log_error "回滚后健康检查失败"
exit /b 1
)
goto :eof
REM 自动更新
:auto_update
call :log_info "开始自动更新..."
REM 尝试热更新
call :hot_update
if !errorlevel! equ 0 (
call :log_info "热更新成功"
exit /b 0
)
REM 热更新失败,进行完整更新
call :log_info "热更新失败,进行完整更新..."
call :full_update
goto :eof
REM 主逻辑
if "%ACTION%"=="check" (
call :check_update
) else if "%ACTION%"=="hot-update" (
call :hot_update
) else if "%ACTION%"=="full-update" (
call :full_update
) else if "%ACTION%"=="auto-update" (
call :auto_update
) else if "%ACTION%"=="rollback" (
call :rollback "%SOURCE_PATH%"
) else (
call :log_error "未知操作: %ACTION%"
exit /b 1
)
endlocal

View File

@@ -1,477 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TSP智能助手更新管理器
支持热更新、版本管理、回滚等功能
"""
import os
import sys
import json
import shutil
import subprocess
import time
import requests
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple
class UpdateManager:
"""更新管理器"""
def __init__(self, config_file: str = "update_config.json"):
self.config_file = config_file
self.config = self._load_config()
self.version_manager = None
# 初始化版本管理器
try:
from version import VersionManager
self.version_manager = VersionManager()
except ImportError:
print("警告: 版本管理器不可用")
def _load_config(self) -> Dict:
"""加载更新配置"""
default_config = {
"app_name": "tsp_assistant",
"deploy_path": "/opt/tsp_assistant",
"backup_path": "./backups",
"service_name": "tsp_assistant",
"health_url": "http://localhost:5000/api/health",
"update_timeout": 300,
"rollback_enabled": True,
"auto_backup": True,
"hot_update_enabled": True,
"environments": {
"development": {
"path": "./dev_deploy",
"service_name": "",
"auto_restart": False
},
"staging": {
"path": "/opt/tsp_assistant_staging",
"service_name": "tsp_assistant_staging",
"auto_restart": True
},
"production": {
"path": "/opt/tsp_assistant",
"service_name": "tsp_assistant",
"auto_restart": True
}
}
}
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# 合并默认配置
default_config.update(config)
except Exception as e:
print(f"加载配置文件失败: {e}")
return default_config
def _save_config(self):
"""保存配置"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
except Exception as e:
print(f"保存配置文件失败: {e}")
def check_update_available(self, source_path: str) -> Tuple[bool, str, str]:
"""检查是否有更新可用"""
if not self.version_manager:
return False, "unknown", "unknown"
current_version = self.version_manager.get_version()
# 检查源路径的版本
try:
source_version_file = os.path.join(source_path, "version.json")
if os.path.exists(source_version_file):
with open(source_version_file, 'r', encoding='utf-8') as f:
source_info = json.load(f)
source_version = source_info.get("version", "unknown")
else:
return False, current_version, "unknown"
except Exception as e:
print(f"检查源版本失败: {e}")
return False, current_version, "unknown"
# 比较版本
if source_version != current_version:
return True, current_version, source_version
return False, current_version, source_version
def create_backup(self, environment: str = "production") -> str:
"""创建备份"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_name = f"{self.config['app_name']}_backup_{timestamp}"
backup_path = os.path.join(self.config["backup_path"], backup_name)
print(f"创建备份: {backup_name}")
# 创建备份目录
os.makedirs(backup_path, exist_ok=True)
# 获取部署路径
env_config = self.config["environments"].get(environment, {})
deploy_path = env_config.get("path", self.config["deploy_path"])
# 备份应用文件
if os.path.exists(deploy_path):
print("备份应用文件...")
shutil.copytree(deploy_path, os.path.join(backup_path, "app"))
# 备份数据库
db_file = os.path.join(deploy_path, "tsp_assistant.db")
if os.path.exists(db_file):
print("备份数据库...")
os.makedirs(os.path.join(backup_path, "database"), exist_ok=True)
shutil.copy2(db_file, os.path.join(backup_path, "database", "tsp_assistant.db"))
# 保存备份信息
backup_info = {
"backup_name": backup_name,
"backup_path": backup_path,
"timestamp": timestamp,
"environment": environment,
"version": self.version_manager.get_version() if self.version_manager else "unknown",
"git_commit": self._get_git_commit(deploy_path)
}
with open(os.path.join(backup_path, "backup_info.json"), 'w', encoding='utf-8') as f:
json.dump(backup_info, f, indent=2, ensure_ascii=False)
print(f"备份完成: {backup_name}")
return backup_name
def _get_git_commit(self, path: str) -> str:
"""获取Git提交哈希"""
try:
result = subprocess.run(['git', 'rev-parse', 'HEAD'],
cwd=path, capture_output=True, text=True)
return result.stdout.strip()[:8] if result.returncode == 0 else "unknown"
except:
return "unknown"
def hot_update(self, source_path: str, environment: str = "production") -> bool:
"""热更新(不重启服务)"""
if not self.config["hot_update_enabled"]:
print("热更新未启用")
return False
print("开始热更新...")
env_config = self.config["environments"].get(environment, {})
deploy_path = env_config.get("path", self.config["deploy_path"])
# 检查哪些文件可以热更新
hot_update_files = [
"src/web/static/js/dashboard.js",
"src/web/static/css/style.css",
"src/web/templates/dashboard.html",
"src/web/app.py",
"src/knowledge_base/knowledge_manager.py",
"src/dialogue/realtime_chat.py"
]
updated_files = []
for file_path in hot_update_files:
source_file = os.path.join(source_path, file_path)
target_file = os.path.join(deploy_path, file_path)
if os.path.exists(source_file):
# 检查文件是否有变化
if not os.path.exists(target_file) or not self._files_equal(source_file, target_file):
print(f"更新文件: {file_path}")
os.makedirs(os.path.dirname(target_file), exist_ok=True)
shutil.copy2(source_file, target_file)
updated_files.append(file_path)
if updated_files:
print(f"热更新完成,更新了 {len(updated_files)} 个文件")
return True
else:
print("没有文件需要热更新")
return False
def _files_equal(self, file1: str, file2: str) -> bool:
"""比较两个文件是否相等"""
try:
with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
return f1.read() == f2.read()
except:
return False
def full_update(self, source_path: str, environment: str = "production",
create_backup: bool = True) -> bool:
"""完整更新(重启服务)"""
print("开始完整更新...")
env_config = self.config["environments"].get(environment, {})
deploy_path = env_config.get("path", self.config["deploy_path"])
service_name = env_config.get("service_name", self.config["service_name"])
auto_restart = env_config.get("auto_restart", True)
# 创建备份
backup_name = None
if create_backup and self.config["auto_backup"]:
backup_name = self.create_backup(environment)
try:
# 停止服务
if auto_restart and service_name:
print(f"停止服务: {service_name}")
subprocess.run(['sudo', 'systemctl', 'stop', service_name], check=True)
# 更新文件
print("更新应用文件...")
if os.path.exists(deploy_path):
shutil.rmtree(deploy_path)
os.makedirs(deploy_path, exist_ok=True)
shutil.copytree(source_path, deploy_path, dirs_exist_ok=True)
# 设置权限
subprocess.run(['sudo', 'chown', '-R', 'www-data:www-data', deploy_path], check=True)
# 安装依赖
print("安装依赖...")
requirements_file = os.path.join(deploy_path, "requirements.txt")
if os.path.exists(requirements_file):
subprocess.run(['sudo', '-u', 'www-data', 'python', '-m', 'pip', 'install', '-r', requirements_file],
cwd=deploy_path, check=True)
# 运行数据库迁移
print("运行数据库迁移...")
init_script = os.path.join(deploy_path, "init_database.py")
if os.path.exists(init_script):
subprocess.run(['sudo', '-u', 'www-data', 'python', init_script],
cwd=deploy_path, check=True)
# 启动服务
if auto_restart and service_name:
print(f"启动服务: {service_name}")
subprocess.run(['sudo', 'systemctl', 'start', service_name], check=True)
# 等待服务启动
print("等待服务启动...")
time.sleep(15)
# 健康检查
if self._health_check():
print("更新成功!")
return True
else:
print("健康检查失败,开始回滚...")
if backup_name:
self.rollback(backup_name, environment)
return False
else:
print("更新完成(未重启服务)")
return True
except Exception as e:
print(f"更新失败: {e}")
if backup_name:
print("开始回滚...")
self.rollback(backup_name, environment)
return False
def _health_check(self) -> bool:
"""健康检查"""
health_url = self.config["health_url"]
max_retries = 10
retry_count = 0
while retry_count < max_retries:
try:
response = requests.get(health_url, timeout=5)
if response.status_code == 200:
return True
except:
pass
retry_count += 1
print(f"健康检查失败,重试中... ({retry_count}/{max_retries})")
time.sleep(5)
return False
def rollback(self, backup_name: str, environment: str = "production") -> bool:
"""回滚到指定备份"""
print(f"开始回滚到备份: {backup_name}")
env_config = self.config["environments"].get(environment, {})
deploy_path = env_config.get("path", self.config["deploy_path"])
service_name = env_config.get("service_name", self.config["service_name"])
auto_restart = env_config.get("auto_restart", True)
backup_path = os.path.join(self.config["backup_path"], backup_name)
if not os.path.exists(backup_path):
print(f"备份不存在: {backup_name}")
return False
try:
# 停止服务
if auto_restart and service_name:
print(f"停止服务: {service_name}")
subprocess.run(['sudo', 'systemctl', 'stop', service_name], check=True)
# 恢复文件
print("恢复文件...")
app_backup_path = os.path.join(backup_path, "app")
if os.path.exists(app_backup_path):
if os.path.exists(deploy_path):
shutil.rmtree(deploy_path)
shutil.copytree(app_backup_path, deploy_path)
# 恢复数据库
db_backup_path = os.path.join(backup_path, "database", "tsp_assistant.db")
if os.path.exists(db_backup_path):
print("恢复数据库...")
shutil.copy2(db_backup_path, os.path.join(deploy_path, "tsp_assistant.db"))
# 设置权限
subprocess.run(['sudo', 'chown', '-R', 'www-data:www-data', deploy_path], check=True)
# 启动服务
if auto_restart and service_name:
print(f"启动服务: {service_name}")
subprocess.run(['sudo', 'systemctl', 'start', service_name], check=True)
# 等待服务启动
time.sleep(15)
# 健康检查
if self._health_check():
print("回滚成功!")
return True
else:
print("回滚后健康检查失败")
return False
else:
print("回滚完成(未重启服务)")
return True
except Exception as e:
print(f"回滚失败: {e}")
return False
def list_backups(self) -> List[Dict]:
"""列出所有备份"""
backups = []
backup_dir = self.config["backup_path"]
if os.path.exists(backup_dir):
for item in os.listdir(backup_dir):
backup_path = os.path.join(backup_dir, item)
if os.path.isdir(backup_path):
info_file = os.path.join(backup_path, "backup_info.json")
if os.path.exists(info_file):
try:
with open(info_file, 'r', encoding='utf-8') as f:
backup_info = json.load(f)
backups.append(backup_info)
except:
pass
return sorted(backups, key=lambda x: x.get("timestamp", ""), reverse=True)
def auto_update(self, source_path: str, environment: str = "production") -> bool:
"""自动更新(智能选择热更新或完整更新)"""
print("开始自动更新...")
# 检查是否有更新
has_update, current_version, new_version = self.check_update_available(source_path)
if not has_update:
print("没有更新可用")
return True
print(f"发现更新: {current_version} -> {new_version}")
# 尝试热更新
if self.hot_update(source_path, environment):
print("热更新成功")
return True
# 热更新失败,进行完整更新
print("热更新失败,进行完整更新...")
return self.full_update(source_path, environment)
def main():
"""命令行接口"""
import argparse
parser = argparse.ArgumentParser(description='TSP智能助手更新管理器')
parser.add_argument('action', choices=['check', 'hot-update', 'full-update', 'auto-update', 'rollback', 'list-backups'],
help='要执行的操作')
parser.add_argument('--source', help='源路径')
parser.add_argument('--environment', choices=['development', 'staging', 'production'],
default='production', help='目标环境')
parser.add_argument('--backup', help='备份名称(用于回滚)')
parser.add_argument('--no-backup', action='store_true', help='跳过备份')
args = parser.parse_args()
um = UpdateManager()
if args.action == 'check':
if not args.source:
print("错误: 需要指定源路径")
sys.exit(1)
has_update, current, new = um.check_update_available(args.source)
if has_update:
print(f"有更新可用: {current} -> {new}")
else:
print(f"没有更新可用 (当前版本: {current})")
elif args.action == 'hot-update':
if not args.source:
print("错误: 需要指定源路径")
sys.exit(1)
success = um.hot_update(args.source, args.environment)
sys.exit(0 if success else 1)
elif args.action == 'full-update':
if not args.source:
print("错误: 需要指定源路径")
sys.exit(1)
success = um.full_update(args.source, args.environment, not args.no_backup)
sys.exit(0 if success else 1)
elif args.action == 'auto-update':
if not args.source:
print("错误: 需要指定源路径")
sys.exit(1)
success = um.auto_update(args.source, args.environment)
sys.exit(0 if success else 1)
elif args.action == 'rollback':
if not args.backup:
print("错误: 需要指定备份名称")
sys.exit(1)
success = um.rollback(args.backup, args.environment)
sys.exit(0 if success else 1)
elif args.action == 'list-backups':
backups = um.list_backups()
if backups:
print("可用备份:")
for backup in backups:
print(f" {backup['backup_name']} - {backup['timestamp']} - {backup.get('version', 'unknown')}")
else:
print("没有找到备份")
if __name__ == "__main__":
main()

View File

@@ -1,273 +0,0 @@
#!/bin/bash
# TSP智能助手升级脚本
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# 配置变量
APP_NAME="tsp_assistant"
BACKUP_DIR="./backups"
DEPLOY_PATH="/opt/tsp_assistant"
SERVICE_NAME="tsp_assistant"
HEALTH_URL="http://localhost:5000/api/health"
# 检查参数
if [ $# -lt 1 ]; then
echo "用法: $0 <新版本路径> [选项]"
echo "选项:"
echo " --force 强制升级,跳过确认"
echo " --no-backup 跳过备份"
echo " --rollback 回滚到指定备份"
exit 1
fi
NEW_VERSION_PATH=$1
FORCE_UPGRADE=false
SKIP_BACKUP=false
ROLLBACK_MODE=false
# 解析参数
while [[ $# -gt 1 ]]; do
case $2 in
--force)
FORCE_UPGRADE=true
;;
--no-backup)
SKIP_BACKUP=true
;;
--rollback)
ROLLBACK_MODE=true
;;
*)
log_error "未知选项: $2"
exit 1
;;
esac
shift
done
# 回滚功能
rollback() {
local backup_name=$1
if [ -z "$backup_name" ]; then
log_error "请指定备份名称"
exit 1
fi
log_step "开始回滚到备份: $backup_name"
# 检查备份是否存在
if [ ! -d "$BACKUP_DIR/$backup_name" ]; then
log_error "备份不存在: $backup_name"
log_info "可用备份列表:"
ls -la "$BACKUP_DIR" | grep backup
exit 1
fi
# 停止服务
log_info "停止服务..."
sudo systemctl stop "$SERVICE_NAME" || true
# 恢复文件
log_info "恢复文件..."
sudo rm -rf "$DEPLOY_PATH"
sudo cp -r "$BACKUP_DIR/$backup_name" "$DEPLOY_PATH"
sudo chown -R www-data:www-data "$DEPLOY_PATH"
# 恢复数据库
if [ -f "$BACKUP_DIR/$backup_name/database/tsp_assistant.db" ]; then
log_info "恢复数据库..."
sudo cp "$BACKUP_DIR/$backup_name/database/tsp_assistant.db" "$DEPLOY_PATH/"
fi
# 启动服务
log_info "启动服务..."
sudo systemctl start "$SERVICE_NAME"
# 等待服务启动
sleep 10
# 健康检查
if curl -f "$HEALTH_URL" > /dev/null 2>&1; then
log_info "回滚成功!"
else
log_error "回滚后健康检查失败"
exit 1
fi
}
# 创建备份
create_backup() {
local timestamp=$(date +"%Y%m%d_%H%M%S")
local backup_name="${APP_NAME}_backup_${timestamp}"
local backup_path="$BACKUP_DIR/$backup_name"
log_step "创建备份: $backup_name"
# 创建备份目录
mkdir -p "$backup_path"
# 备份应用文件
if [ -d "$DEPLOY_PATH" ]; then
log_info "备份应用文件..."
cp -r "$DEPLOY_PATH"/* "$backup_path/"
fi
# 备份数据库
if [ -f "$DEPLOY_PATH/tsp_assistant.db" ]; then
log_info "备份数据库..."
mkdir -p "$backup_path/database"
cp "$DEPLOY_PATH/tsp_assistant.db" "$backup_path/database/"
fi
# 保存备份信息
cat > "$backup_path/backup_info.json" << EOF
{
"backup_name": "$backup_name",
"backup_path": "$backup_path",
"timestamp": "$timestamp",
"version": "$(cd "$DEPLOY_PATH" && python version.py version 2>/dev/null || echo "unknown")",
"git_commit": "$(cd "$DEPLOY_PATH" && git rev-parse HEAD 2>/dev/null | cut -c1-8 || echo "unknown")"
}
EOF
log_info "备份完成: $backup_name"
echo "$backup_name"
}
# 升级功能
upgrade() {
local new_version_path=$1
log_step "开始升级TSP智能助手"
# 检查新版本路径
if [ ! -d "$new_version_path" ]; then
log_error "新版本路径不存在: $new_version_path"
exit 1
fi
# 检查当前版本
if [ -d "$DEPLOY_PATH" ]; then
local current_version=$(cd "$DEPLOY_PATH" && python version.py version 2>/dev/null || echo "unknown")
log_info "当前版本: $current_version"
else
log_warn "当前部署路径不存在: $DEPLOY_PATH"
fi
# 检查新版本
local new_version=$(cd "$new_version_path" && python version.py version 2>/dev/null || echo "unknown")
log_info "新版本: $new_version"
# 确认升级
if [ "$FORCE_UPGRADE" = false ]; then
echo -n "确认升级到版本 $new_version? (y/N): "
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
log_info "升级取消"
exit 0
fi
fi
# 创建备份
local backup_name=""
if [ "$SKIP_BACKUP" = false ]; then
backup_name=$(create_backup)
fi
# 停止服务
log_step "停止服务..."
sudo systemctl stop "$SERVICE_NAME" || true
# 升级文件
log_step "升级应用文件..."
sudo rm -rf "$DEPLOY_PATH"
sudo mkdir -p "$DEPLOY_PATH"
sudo cp -r "$new_version_path"/* "$DEPLOY_PATH/"
sudo chown -R www-data:www-data "$DEPLOY_PATH"
# 安装依赖
log_step "安装依赖..."
cd "$DEPLOY_PATH"
sudo -u www-data python -m pip install -r requirements.txt
# 运行数据库迁移
log_step "运行数据库迁移..."
sudo -u www-data python init_database.py || true
# 启动服务
log_step "启动服务..."
sudo systemctl start "$SERVICE_NAME"
# 等待服务启动
log_info "等待服务启动..."
sleep 15
# 健康检查
log_step "执行健康检查..."
local retry_count=0
local max_retries=10
while [ $retry_count -lt $max_retries ]; do
if curl -f "$HEALTH_URL" > /dev/null 2>&1; then
log_info "健康检查通过!"
break
else
log_warn "健康检查失败,重试中... ($((retry_count + 1))/$max_retries)"
retry_count=$((retry_count + 1))
sleep 5
fi
done
if [ $retry_count -eq $max_retries ]; then
log_error "健康检查失败,开始回滚..."
if [ -n "$backup_name" ]; then
rollback "$backup_name"
else
log_error "没有备份可回滚"
exit 1
fi
else
log_info "升级成功!"
log_info "新版本: $new_version"
if [ -n "$backup_name" ]; then
log_info "备份名称: $backup_name"
fi
fi
}
# 主函数
main() {
if [ "$ROLLBACK_MODE" = true ]; then
rollback "$NEW_VERSION_PATH"
else
upgrade "$NEW_VERSION_PATH"
fi
}
# 执行主函数
main