feat: 初始化考培练系统项目

- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
111
2026-01-24 19:33:28 +08:00
commit 998211c483
1197 changed files with 228429 additions and 0 deletions

152
deploy/scripts/auto_update.sh Executable file
View File

@@ -0,0 +1,152 @@
#!/bin/bash
# 考培练系统自动更新脚本
# 作者: AI Assistant
# 日期: 2025-09-25
set -e
# 配置变量
PROJECT_DIR="/root/aiedu"
LOG_FILE="/var/log/kaopeilian_update.log"
BACKUP_DIR="/root/aiedu/backups/updates"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 创建备份目录
mkdir -p "$BACKUP_DIR"
log "=== 开始检查代码更新 ==="
cd "$PROJECT_DIR"
# 获取当前提交哈希
LOCAL_COMMIT=$(git rev-parse HEAD)
log "本地提交: $LOCAL_COMMIT"
# 拉取最新代码
git fetch origin production
# 获取远程提交哈希
REMOTE_COMMIT=$(git rev-parse origin/production)
log "远程提交: $REMOTE_COMMIT"
# 检查是否有更新
if [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
log "代码已是最新版本,无需更新"
exit 0
fi
log "发现代码更新,开始部署流程..."
# 创建备份
BACKUP_NAME="backup_$(date '+%Y%m%d_%H%M%S')"
log "创建备份: $BACKUP_NAME"
# 备份当前代码
git stash push -m "Auto backup before update $BACKUP_NAME"
# 备份数据库如果MySQL容器在运行
if docker ps | grep -q kaopeilian-mysql; then
docker exec kaopeilian-mysql mysqldump -u root -p'Kaopeilian2025!@#' kaopeilian > "$BACKUP_DIR/${BACKUP_NAME}_database.sql"
log "数据库备份完成"
fi
# 拉取最新代码
log "拉取最新代码..."
git pull origin production
# 检查是否需要重新构建
NEED_REBUILD=false
# 检查Docker文件变化
if git diff --name-only "$LOCAL_COMMIT" "$REMOTE_COMMIT" | grep -E "(Dockerfile|docker-compose\.yml|requirements\.txt|package\.json)"; then
NEED_REBUILD=true
log "检测到构建文件变化,需要重新构建镜像"
fi
# 检查前端文件变化并构建
if git diff --name-only "$LOCAL_COMMIT" "$REMOTE_COMMIT" | grep -E "kaopeilian-frontend/(src/|package\.json|vite\.config\.ts)"; then
log "检测到前端代码变化,开始构建前端..."
cd "$PROJECT_DIR/kaopeilian-frontend"
# 确保依赖已安装
if [ ! -d "node_modules" ]; then
log "安装前端依赖..."
npm install
fi
# 构建前端
log "构建前端应用..."
npm run build
if [ $? -eq 0 ]; then
log "前端构建成功"
NEED_REBUILD=true
else
log "⚠️ 前端构建失败,但继续部署流程"
fi
cd "$PROJECT_DIR"
fi
# 停止服务
log "停止当前服务..."
docker compose down
# 重新构建(如果需要)
if [ "$NEED_REBUILD" = true ]; then
log "重新构建Docker镜像..."
docker compose build --no-cache
else
log "使用现有镜像启动服务..."
fi
# 启动服务
log "启动更新后的服务..."
docker compose up -d
# 等待服务启动
sleep 60
# 健康检查
log "执行健康检查..."
HEALTH_CHECK_URL="https://aiedu.ireborn.com.cn/health"
# 尝试多次健康检查
RETRY_COUNT=0
MAX_RETRIES=5
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -f -s -k "$HEALTH_CHECK_URL" > /dev/null; then
log "✅ 健康检查通过,更新成功完成"
# 清理旧的备份保留最近5个
cd "$BACKUP_DIR"
ls -t backup_*_database.sql 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true
log "=== 自动更新完成 ==="
exit 0
else
RETRY_COUNT=$((RETRY_COUNT + 1))
log "健康检查失败,重试 $RETRY_COUNT/$MAX_RETRIES"
sleep 10
fi
done
log "❌ 健康检查失败,开始回滚..."
# 回滚代码
cd "$PROJECT_DIR"
git reset --hard "$LOCAL_COMMIT"
# 重新启动服务
docker compose down
docker compose up -d
log "回滚完成,请检查服务状态"
exit 1

223
deploy/scripts/check-config.sh Executable file
View File

@@ -0,0 +1,223 @@
#!/bin/bash
# 配置一致性检查脚本
# 用于快速验证系统各组件配置是否一致
echo "🔧 配置一致性检查脚本"
echo "========================"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 检查结果统计
PASS_COUNT=0
FAIL_COUNT=0
# 检查函数
check_service() {
local service_name=$1
local url=$2
local expected_response=$3
echo -n "检查 $service_name ... "
if command -v curl >/dev/null 2>&1; then
response=$(curl -s --connect-timeout 5 "$url" 2>/dev/null)
if [[ $? -eq 0 && "$response" == *"$expected_response"* ]]; then
echo -e "${GREEN}✅ 通过${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 失败${NC}"
echo " 期望包含: $expected_response"
echo " 实际响应: $response"
((FAIL_COUNT++))
fi
else
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
fi
}
# 检查Docker服务
check_docker_service() {
local service_name=$1
echo -n "检查 Docker 服务 $service_name ... "
if command -v docker-compose >/dev/null 2>&1; then
if docker-compose ps | grep -q "$service_name.*Up"; then
echo -e "${GREEN}✅ 运行中${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 未运行${NC}"
((FAIL_COUNT++))
fi
else
echo -e "${YELLOW}⚠️ 跳过 (docker-compose 未安装)${NC}"
fi
}
# 检查配置文件
check_config_file() {
local file_path=$1
local description=$2
echo -n "检查 $description ... "
if [[ -f "$file_path" ]]; then
echo -e "${GREEN}✅ 存在${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 缺失${NC}"
echo " 文件路径: $file_path"
((FAIL_COUNT++))
fi
}
# 检查端口占用
check_port() {
local port=$1
local service_name=$2
echo -n "检查端口 $port ($service_name) ... "
if command -v lsof >/dev/null 2>&1; then
if lsof -i :$port >/dev/null 2>&1; then
echo -e "${GREEN}✅ 已占用${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 未占用${NC}"
((FAIL_COUNT++))
fi
elif command -v netstat >/dev/null 2>&1; then
if netstat -an | grep -q ":$port "; then
echo -e "${GREEN}✅ 已占用${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 未占用${NC}"
((FAIL_COUNT++))
fi
else
echo -e "${YELLOW}⚠️ 跳过 (lsof/netstat 未安装)${NC}"
fi
}
echo "1. 检查基础服务状态"
echo "-------------------"
# 检查Docker服务
check_docker_service "mysql"
check_docker_service "redis"
# 检查端口占用
check_port 3306 "MySQL"
check_port 6379 "Redis"
check_port 8000 "后端API"
check_port 3001 "前端开发服务器"
echo ""
echo "2. 检查服务健康状态"
echo "-------------------"
# 检查后端健康状态
check_service "后端API健康检查" "http://localhost:8000/health" "healthy"
# 检查前端服务
check_service "前端服务" "http://localhost:3001" "考培练系统"
echo ""
echo "3. 检查配置文件"
echo "---------------"
# 检查关键配置文件
check_config_file "kaopeilian-backend/app/config/settings.py" "后端配置文件"
check_config_file "kaopeilian-frontend/src/api/config.ts" "前端API配置"
check_config_file "docker-compose.yml" "Docker配置文件"
check_config_file "配置一致性检查清单.md" "配置检查清单"
echo ""
echo "4. 检查认证功能"
echo "---------------"
# 检查登录API
echo -n "检查登录API ... "
if command -v curl >/dev/null 2>&1; then
login_response=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "TestPass123!"}' 2>/dev/null)
if [[ "$login_response" == *"access_token"* ]]; then
echo -e "${GREEN}✅ 正常工作${NC}"
((PASS_COUNT++))
# 提取token测试认证
if command -v jq >/dev/null 2>&1; then
token=$(echo "$login_response" | jq -r '.data.access_token' 2>/dev/null)
if [[ "$token" != "null" && "$token" != "" ]]; then
echo -n "检查token认证 ... "
auth_response=$(curl -s -H "Authorization: Bearer $token" \
http://localhost:8000/api/v1/auth/me 2>/dev/null)
if [[ "$auth_response" == *"testuser"* ]]; then
echo -e "${GREEN}✅ 正常工作${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 失败${NC}"
((FAIL_COUNT++))
fi
fi
else
echo " (跳过token测试 - jq未安装)"
fi
else
echo -e "${RED}❌ 失败${NC}"
echo " 响应: $login_response"
((FAIL_COUNT++))
fi
else
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
fi
echo ""
echo "5. 检查CORS配置"
echo "---------------"
echo -n "检查CORS预检请求 ... "
if command -v curl >/dev/null 2>&1; then
cors_response=$(curl -s -X OPTIONS http://localhost:8000/api/v1/auth/login \
-H "Origin: http://localhost:3001" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" 2>/dev/null)
if [[ $? -eq 0 ]]; then
echo -e "${GREEN}✅ 正常响应${NC}"
((PASS_COUNT++))
else
echo -e "${RED}❌ 失败${NC}"
((FAIL_COUNT++))
fi
else
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
fi
echo ""
echo "========================"
echo "📊 检查结果统计"
echo "------------------------"
echo -e "通过: ${GREEN}$PASS_COUNT${NC}"
echo -e "失败: ${RED}$FAIL_COUNT${NC}"
if [[ $FAIL_COUNT -eq 0 ]]; then
echo -e "\n🎉 ${GREEN}所有检查都通过了!系统配置正常。${NC}"
exit 0
else
echo -e "\n⚠ ${YELLOW}发现 $FAIL_COUNT 个问题,请检查配置。${NC}"
echo ""
echo "💡 解决建议:"
echo "1. 确保Docker服务已启动: docker-compose up -d"
echo "2. 检查端口是否被占用或服务未启动"
echo "3. 参考 '配置一致性检查清单.md' 核对配置"
echo "4. 确保数据库密码、CORS域名等配置一致"
exit 1
fi

View File

@@ -0,0 +1,91 @@
#!/bin/bash
# 环境状态检查脚本
echo "=== 考培练系统环境状态检查 ==="
echo "检查时间: $(date)"
echo ""
# 检查前端环境
echo "🌐 前端环境检查:"
if curl -s -f http://localhost:3001 > /dev/null; then
echo "✅ 前端服务运行正常 (http://localhost:3001)"
# 尝试获取环境信息
if command -v jq &> /dev/null; then
echo "📊 前端环境信息:"
curl -s http://localhost:3001/api/env 2>/dev/null | jq . || echo "无法获取环境信息"
fi
else
echo "❌ 前端服务不可访问"
fi
echo ""
# 检查后端环境
echo "🚀 后端环境检查:"
if curl -s -f http://localhost:8000/health > /dev/null; then
echo "✅ 后端服务运行正常 (http://localhost:8000)"
# 获取健康检查信息
if command -v jq &> /dev/null; then
echo "📊 后端环境信息:"
curl -s http://localhost:8000/health | jq .
else
echo "健康检查响应:"
curl -s http://localhost:8000/health
fi
else
echo "❌ 后端服务不可访问"
fi
echo ""
# 检查数据库连接
echo "🗄️ 数据库连接检查:"
if curl -s -f http://localhost:8000/health/db > /dev/null 2>&1; then
echo "✅ 数据库连接正常"
if command -v jq &> /dev/null; then
curl -s http://localhost:8000/health/db | jq .
fi
else
echo "❌ 数据库连接异常"
fi
echo ""
# 检查Redis连接
echo "🔴 Redis连接检查:"
if curl -s -f http://localhost:8000/health/redis > /dev/null 2>&1; then
echo "✅ Redis连接正常"
if command -v jq &> /dev/null; then
curl -s http://localhost:8000/health/redis | jq .
fi
else
echo "❌ Redis连接异常"
fi
echo ""
# 检查Docker容器状态
echo "🐳 Docker容器状态:"
if command -v docker &> /dev/null; then
echo "开发环境容器:"
docker-compose -f docker-compose.dev.yml ps 2>/dev/null || echo "无法获取开发环境容器状态"
echo ""
echo "生产环境容器:"
docker-compose ps 2>/dev/null || echo "无法获取生产环境容器状态"
else
echo "Docker未安装或不可访问"
fi
echo ""
# 检查端口占用
echo "🔌 端口占用检查:"
ports=(3001 8000 3306 6379)
for port in "${ports[@]}"; do
if lsof -i :$port > /dev/null 2>&1; then
echo "✅ 端口 $port 已占用"
lsof -i :$port | head -2 | tail -1 | awk '{print " 进程:", $2, "命令:", $1}'
else
echo "❌ 端口 $port 未占用"
fi
done
echo ""
echo "=== 环境检查完成 ==="

View File

@@ -0,0 +1,62 @@
#!/bin/bash
# Docker 清理脚本
# 用于清理未使用的Docker资源
echo "🧹 开始清理Docker资源..."
# 1. 清理停止的容器
echo "📋 清理停止的容器..."
stopped_containers=$(docker ps -a --filter "status=exited" -q)
if [ ! -z "$stopped_containers" ]; then
echo "发现停止的容器: $stopped_containers"
docker rm $stopped_containers
echo "✅ 已清理停止的容器"
else
echo "✅ 没有停止的容器需要清理"
fi
# 2. 清理悬空镜像
echo "📋 清理悬空镜像..."
dangling_images=$(docker images --filter "dangling=true" -q)
if [ ! -z "$dangling_images" ]; then
echo "发现悬空镜像: $dangling_images"
docker rmi $dangling_images
echo "✅ 已清理悬空镜像"
else
echo "✅ 没有悬空镜像需要清理"
fi
# 3. 清理未使用的卷
echo "📋 清理未使用的卷..."
unused_volumes=$(docker volume ls --filter "dangling=true" -q)
if [ ! -z "$unused_volumes" ]; then
echo "发现未使用的卷: $unused_volumes"
docker volume rm $unused_volumes
echo "✅ 已清理未使用的卷"
else
echo "✅ 没有未使用的卷需要清理"
fi
# 4. 清理未使用的网络
echo "📋 清理未使用的网络..."
unused_networks=$(docker network ls --filter "dangling=true" -q)
if [ ! -z "$unused_networks" ]; then
echo "发现未使用的网络: $unused_networks"
docker network rm $unused_networks
echo "✅ 已清理未使用的网络"
else
echo "✅ 没有未使用的网络需要清理"
fi
# 5. 显示当前状态
echo "📊 当前Docker状态:"
echo "运行中的容器:"
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
echo -e "\nDocker卷:"
docker volume ls
echo -e "\nDocker网络:"
docker network ls
echo "🎉 Docker清理完成"

113
deploy/scripts/deploy.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
# 考陪练系统部署脚本
# 用于部署前端、后端服务并申请SSL证书
set -e
echo "=== 考陪练系统部署开始 ==="
# 检查Docker是否运行
if ! docker info > /dev/null 2>&1; then
echo "❌ Docker未运行请先启动Docker服务"
exit 1
fi
# 检查Docker Compose是否安装
if ! command -v docker-compose > /dev/null 2>&1; then
echo "❌ Docker Compose未安装"
exit 1
fi
echo "✅ Docker环境检查通过"
# 创建必要的目录
echo "📁 创建必要的目录..."
mkdir -p /root/aiedu/kaopeilian-backend/logs
mkdir -p /root/aiedu/kaopeilian-backend/uploads
mkdir -p /root/aiedu/nginx/conf.d
# 停止现有容器
echo "🛑 停止现有容器..."
docker-compose down || true
# 构建镜像
echo "🔨 构建Docker镜像..."
docker-compose build --no-cache
# 启动服务除了nginx因为还没有SSL证书
echo "🚀 启动后端和前端服务..."
docker-compose up -d kaopeilian-redis kaopeilian-backend kaopeilian-frontend
# 等待服务启动
echo "⏳ 等待服务启动..."
sleep 30
# 检查服务健康状态
echo "🔍 检查服务健康状态..."
if curl -f http://localhost:8000/health > /dev/null 2>&1; then
echo "✅ 后端服务健康检查通过"
else
echo "❌ 后端服务健康检查失败"
docker-compose logs kaopeilian-backend
exit 1
fi
if curl -f http://localhost:3001/ > /dev/null 2>&1; then
echo "✅ 前端服务健康检查通过"
else
echo "❌ 前端服务健康检查失败"
docker-compose logs kaopeilian-frontend
exit 1
fi
echo "=== 基础服务部署完成 ==="
echo "前端访问地址: http://localhost:3001"
echo "后端API地址: http://localhost:8000"
echo ""
echo "下一步将申请SSL证书..."
# 申请SSL证书
echo "🔐 申请SSL证书..."
if command -v certbot > /dev/null 2>&1; then
# 停止nginx容器如果运行
docker-compose stop kaopeilian-nginx || true
# 使用standalone模式申请证书
certbot certonly \
--standalone \
--non-interactive \
--agree-tos \
--email admin@ireborn.com.cn \
--domains aiedu.ireborn.com.cn \
--pre-hook "docker-compose stop kaopeilian-nginx" \
--post-hook "docker-compose up -d kaopeilian-nginx"
if [ $? -eq 0 ]; then
echo "✅ SSL证书申请成功"
# 启动nginx服务
echo "🚀 启动Nginx反向代理服务..."
docker-compose up -d kaopeilian-nginx
echo "=== 部署完成 ==="
echo "HTTPS访问地址: https://aiedu.ireborn.com.cn"
echo "前端访问地址: https://aiedu.ireborn.com.cn"
echo "后端API地址: https://aiedu.ireborn.com.cn/api"
else
echo "❌ SSL证书申请失败请检查域名配置"
echo "HTTP访问地址: http://aiedu.ireborn.com.cn"
fi
else
echo "⚠️ certbot未安装跳过SSL证书申请"
echo "请手动安装certbot并申请SSL证书"
fi
echo ""
echo "=== 部署完成 ==="
echo "查看服务状态: docker-compose ps"
echo "查看日志: docker-compose logs [服务名]"
echo "停止服务: docker-compose down"

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# 系统诊断脚本
echo "=== 系统诊断开始 ==="
echo "时间: $(date)"
echo ""
echo "=== Docker服务状态 ==="
systemctl is-active docker || echo "Docker服务未运行"
echo ""
echo "=== Docker版本 ==="
docker --version 2>/dev/null || echo "Docker命令不可用"
echo ""
echo "=== 容器状态 ==="
docker ps -a 2>/dev/null || echo "无法获取容器状态"
echo ""
echo "=== 网络连接测试 ==="
curl -I --connect-timeout 3 http://localhost 2>/dev/null || echo "本地80端口不可访问"
curl -I --connect-timeout 3 https://aiedu.ireborn.com.cn 2>/dev/null || echo "HTTPS不可访问"
echo ""
echo "=== 端口占用检查 ==="
netstat -tlnp | grep -E ":(80|443|8000|3306|6379|9000)" || echo "关键端口未监听"
echo ""
echo "=== 服务状态 ==="
systemctl is-active kaopeilian.service || echo "kaopeilian服务未运行"
systemctl is-active kaopeilian-webhook.service || echo "webhook服务未运行"
echo ""
echo "=== 诊断完成 ==="

View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Dify服务器网络诊断脚本
# 请在Dify服务器(47.112.29.0)上运行此脚本
echo "=== Dify服务器网络诊断 ==="
echo ""
# 1. DNS解析测试
echo "1. DNS解析测试"
echo " hl.ireborn.com.cn: $(nslookup hl.ireborn.com.cn 2>/dev/null | grep Address | tail -1 || echo '解析失败')"
echo " yy.ireborn.com.cn: $(nslookup yy.ireborn.com.cn 2>/dev/null | grep Address | tail -1 || echo '解析失败')"
echo ""
# 2. 网络连通性测试
echo "2. 网络连通性测试 (ping)"
ping -c 3 120.79.247.16 2>&1 | tail -3
echo ""
# 3. 端口连通性测试
echo "3. 端口连通性测试:"
echo " HTTP (80): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/80' 2>&1 && echo '可连接' || echo '不可连接')"
echo " HTTPS (443): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/443' 2>&1 && echo '可连接' || echo '不可连接')"
echo " API (8000): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/8000' 2>&1 && echo '可连接' || echo '不可连接')"
echo ""
# 4. HTTPS请求测试
echo "4. HTTPS请求测试"
echo " 使用域名:"
curl -s -o /dev/null -w "HTTP状态码: %{http_code}, 连接时间: %{time_connect}s\n" \
--connect-timeout 10 \
https://hl.ireborn.com.cn/health 2>&1 || echo " 请求失败"
echo " 使用IP地址"
curl -s -o /dev/null -w "HTTP状态码: %{http_code}, 连接时间: %{time_connect}s\n" \
--connect-timeout 10 \
-H "Host: hl.ireborn.com.cn" \
https://120.79.247.16/health 2>&1 || echo " 请求失败"
echo ""
# 5. SQL执行器API测试
echo "5. SQL执行器API测试"
curl -s -X POST https://hl.ireborn.com.cn/api/v1/sql/execute-simple \
-H "Content-Type: application/json" \
-H "X-API-Key: dify-2025-kaopeilian" \
-d '{"sql":"SELECT 1 as test"}' \
--connect-timeout 10 2>&1 | head -1 || echo " 请求失败"
echo ""
echo "=== 诊断完成 ==="
echo ""
echo "如果以上测试有失败项,请检查:"
echo "1. 阿里云安全组是否允许来自47.112.29.0的入站流量"
echo "2. 服务器防火墙规则"
echo "3. VPC网络配置"

View File

@@ -0,0 +1,50 @@
#!/bin/bash
# 强制重启所有服务
echo "=== 强制重启考培练系统服务 ==="
echo "时间: $(date)"
# 1. 停止所有相关进程
echo "1. 停止相关进程..."
pkill -f "docker-compose"
pkill -f "webhook_handler"
# 2. 清理Docker
echo "2. 清理Docker容器和网络..."
cd /root/aiedu
docker compose down --remove-orphans 2>/dev/null || true
docker system prune -f 2>/dev/null || true
# 3. 重启Docker服务
echo "3. 重启Docker服务..."
systemctl restart docker
sleep 15
# 4. 启动服务
echo "4. 启动考培练系统..."
cd /root/aiedu
docker compose up -d
# 5. 启动webhook服务
echo "5. 启动Webhook服务..."
systemctl restart kaopeilian-webhook.service
# 6. 等待服务启动
echo "6. 等待服务启动..."
sleep 30
# 7. 检查状态
echo "7. 检查服务状态..."
echo "Docker容器:"
docker ps --format "table {{.Names}}\t{{.Status}}" 2>/dev/null || echo "Docker命令失败"
echo ""
echo "端口监听:"
netstat -tlnp | grep -E ":(80|443|8000|3306|6379|9000)" || echo "端口检查失败"
echo ""
echo "网站测试:"
curl -I --connect-timeout 5 https://aiedu.ireborn.com.cn 2>/dev/null || echo "网站访问失败"
echo ""
echo "=== 重启完成 ==="

View File

@@ -0,0 +1,60 @@
#!/bin/bash
# 快速测试陪练功能API
echo "============================================================"
echo "陪练功能快速测试"
echo "============================================================"
# 获取token
echo -e "\n1. 登录获取token..."
TOKEN=$(curl -s -X POST "http://localhost:8000/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
| python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['token']['access_token'] if data['code']==200 else '')")
if [ -z "$TOKEN" ]; then
echo "❌ 登录失败"
exit 1
fi
echo "✅ 登录成功"
# 测试场景列表
echo -e "\n2. 测试场景列表..."
SCENES=$(curl -s "http://localhost:8000/api/v1/practice/scenes" \
-H "Authorization: Bearer $TOKEN")
COUNT=$(echo $SCENES | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['total'] if data['code']==200 else 0)")
if [ "$COUNT" -gt 0 ]; then
echo "✅ 成功获取 $COUNT 个场景"
else
echo "❌ 获取场景失败"
exit 1
fi
# 测试场景详情
echo -e "\n3. 测试场景详情..."
DETAIL=$(curl -s "http://localhost:8000/api/v1/practice/scenes/1" \
-H "Authorization: Bearer $TOKEN")
NAME=$(echo $DETAIL | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['name'] if data['code']==200 else '')")
if [ -n "$NAME" ]; then
echo "✅ 成功获取场景: $NAME"
else
echo "❌ 获取场景详情失败"
exit 1
fi
echo -e "\n============================================================"
echo "✅ 陪练功能API测试通过"
echo "============================================================"
echo ""
echo "📌 提示:"
echo " - 场景列表: http://localhost:3001/trainee/ai-practice-center"
echo " - 后端API: http://localhost:8000/docs"
echo " - 运行完整测试: python3 test_practice_api.py"
echo ""

View File

@@ -0,0 +1,84 @@
#!/bin/bash
# 快速测试成绩报告和错题本功能
# 使用方法:./quick_test_score_mistakes.sh
set -e
echo "=========================================="
echo "成绩报告与错题本功能快速测试"
echo "=========================================="
# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 1. 检查MySQL容器
echo -e "\n${YELLOW}1. 检查MySQL容器状态...${NC}"
if docker ps | grep -q kaopeilian-mysql-dev; then
echo -e "${GREEN}✓ MySQL容器正在运行${NC}"
else
echo -e "${RED}✗ MySQL容器未运行请先启动${NC}"
echo "启动命令: docker-compose -f docker-compose.dev.yml up -d mysql-dev"
exit 1
fi
# 2. 检查数据库字段
echo -e "\n${YELLOW}2. 验证数据库字段...${NC}"
echo "检查exams表的round字段..."
docker exec -i kaopeilian-mysql-dev mysql -u root -pnj861021 kaopeilian -e "
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='kaopeilian'
AND TABLE_NAME='exams'
AND COLUMN_NAME LIKE 'round%';" 2>/dev/null | grep -v "Warning"
echo "检查exam_mistakes表的question_type字段..."
docker exec -i kaopeilian-mysql-dev mysql -u root -pnj861021 kaopeilian -e "
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='kaopeilian'
AND TABLE_NAME='exam_mistakes'
AND COLUMN_NAME='question_type';" 2>/dev/null | grep -v "Warning"
# 3. 检查后端服务
echo -e "\n${YELLOW}3. 检查后端服务状态...${NC}"
if curl -s http://localhost:8000/health > /dev/null 2>&1; then
echo -e "${GREEN}✓ 后端服务正在运行${NC}"
else
echo -e "${RED}✗ 后端服务未运行${NC}"
echo "请在另一个终端启动后端:"
echo " cd kaopeilian-backend"
echo " uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
exit 1
fi
# 4. 测试API需要token
echo -e "\n${YELLOW}4. 测试API接口...${NC}"
echo "提示需要先登录获取token"
echo "运行Python测试脚本"
echo " python3 test_score_report_api.py"
# 5. 前端服务检查
echo -e "\n${YELLOW}5. 检查前端服务状态...${NC}"
if curl -s http://localhost:3001 > /dev/null 2>&1; then
echo -e "${GREEN}✓ 前端服务正在运行${NC}"
echo ""
echo "📊 成绩报告页面:"
echo " http://localhost:3001/trainee/score-report"
echo ""
echo "📝 错题本页面:"
echo " http://localhost:3001/trainee/mistakes"
else
echo -e "${RED}✗ 前端服务未运行${NC}"
echo "请在另一个终端启动前端:"
echo " cd kaopeilian-frontend"
echo " npm run dev"
fi
echo ""
echo "=========================================="
echo "检查完成!请根据上述提示进行测试。"
echo "=========================================="

68
deploy/scripts/robust_start.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/bash
# 健壮的服务启动脚本
set -e
LOG_FILE="/var/log/kaopeilian_start.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
cd /root/aiedu
log "=== 开始启动考培练系统服务 ==="
# 1. 检查Docker服务
log "1. 检查Docker服务状态..."
if ! systemctl is-active --quiet docker; then
log "启动Docker服务..."
systemctl start docker
sleep 10
fi
# 2. 清理可能的问题容器
log "2. 清理问题容器..."
docker compose down --remove-orphans 2>/dev/null || true
# 3. 分步启动服务(避免前端构建失败影响其他服务)
log "3. 启动基础服务..."
docker compose up -d mysql redis
sleep 30
log "4. 启动后端服务..."
docker compose up -d backend
sleep 20
log "5. 尝试构建前端..."
if docker compose build frontend; then
log "前端构建成功,启动前端服务..."
docker compose up -d frontend
else
log "前端构建失败,使用旧镜像启动..."
docker compose up -d frontend || log "前端启动失败,跳过前端服务"
fi
log "6. 启动Nginx..."
docker compose up -d nginx
# 7. 检查服务状态
log "7. 检查服务状态..."
sleep 20
docker compose ps
# 8. 健康检查
log "8. 执行健康检查..."
if curl -f -s https://aiedu.ireborn.com.cn/health > /dev/null; then
log "✅ 后端服务正常"
else
log "❌ 后端服务异常"
fi
if curl -f -s -I https://aiedu.ireborn.com.cn > /dev/null; then
log "✅ 前端服务正常"
else
log "❌ 前端服务异常"
fi
log "=== 服务启动完成 ==="

View File

@@ -0,0 +1,246 @@
#!/bin/bash
# 环境设置脚本
set -e
ENV_TYPE=${1:-development}
FORCE_SETUP=${2:-false}
echo "=== 考培练系统环境设置 ==="
echo "环境类型: $ENV_TYPE"
echo "设置时间: $(date)"
echo ""
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 打印带颜色的消息
print_message() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
# 检查必要的工具
check_requirements() {
print_message $BLUE "检查系统要求..."
# 检查Docker
if ! command -v docker &> /dev/null; then
print_message $RED "❌ Docker未安装请先安装Docker"
exit 1
fi
# 检查Docker Compose
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
print_message $RED "❌ Docker Compose未安装请先安装Docker Compose"
exit 1
fi
# 检查Node.js (如果需要本地开发)
if [ "$ENV_TYPE" = "development" ] && ! command -v node &> /dev/null; then
print_message $YELLOW "⚠️ Node.js未安装建议安装以支持本地开发"
fi
# 检查Python (如果需要本地开发)
if [ "$ENV_TYPE" = "development" ] && ! command -v python3 &> /dev/null; then
print_message $YELLOW "⚠️ Python3未安装建议安装以支持本地开发"
fi
print_message $GREEN "✅ 系统要求检查完成"
}
# 设置前端环境
setup_frontend_env() {
print_message $BLUE "设置前端环境配置..."
local env_file="kaopeilian-frontend/.env.$ENV_TYPE"
local example_file="kaopeilian-frontend/.env.example"
if [ ! -f "$env_file" ] || [ "$FORCE_SETUP" = "true" ]; then
if [ -f "$example_file" ]; then
cp "$example_file" "$env_file"
print_message $GREEN "✅ 创建前端环境配置: $env_file"
else
print_message $YELLOW "⚠️ 前端配置模板不存在: $example_file"
fi
else
print_message $GREEN "✅ 前端环境配置已存在: $env_file"
fi
# 根据环境类型更新配置
if [ -f "$env_file" ]; then
case $ENV_TYPE in
development)
sed -i.bak 's/VITE_APP_TITLE=.*/VITE_APP_TITLE=考培练系统(开发)/' "$env_file"
sed -i.bak 's/VITE_APP_ENV=.*/VITE_APP_ENV=development/' "$env_file"
sed -i.bak 's/VITE_API_BASE_URL=.*/VITE_API_BASE_URL=http:\/\/localhost:8000/' "$env_file"
sed -i.bak 's/VITE_ENABLE_DEVTOOLS=.*/VITE_ENABLE_DEVTOOLS=true/' "$env_file"
rm -f "$env_file.bak"
;;
production)
sed -i.bak 's/VITE_APP_TITLE=.*/VITE_APP_TITLE=考培练系统/' "$env_file"
sed -i.bak 's/VITE_APP_ENV=.*/VITE_APP_ENV=production/' "$env_file"
sed -i.bak 's/VITE_API_BASE_URL=.*/VITE_API_BASE_URL=https:\/\/aiedu.ireborn.com.cn/' "$env_file"
sed -i.bak 's/VITE_ENABLE_DEVTOOLS=.*/VITE_ENABLE_DEVTOOLS=false/' "$env_file"
rm -f "$env_file.bak"
;;
esac
print_message $GREEN "✅ 前端环境配置已更新"
fi
}
# 设置后端环境
setup_backend_env() {
print_message $BLUE "设置后端环境配置..."
local env_file="kaopeilian-backend/.env.$ENV_TYPE"
local example_file="kaopeilian-backend/.env.example"
if [ ! -f "$env_file" ] || [ "$FORCE_SETUP" = "true" ]; then
if [ -f "$example_file" ]; then
cp "$example_file" "$env_file"
print_message $GREEN "✅ 创建后端环境配置: $env_file"
else
print_message $YELLOW "⚠️ 后端配置模板不存在: $example_file"
fi
else
print_message $GREEN "✅ 后端环境配置已存在: $env_file"
fi
# 根据环境类型更新配置
if [ -f "$env_file" ]; then
case $ENV_TYPE in
development)
sed -i.bak 's/ENV=.*/ENV=development/' "$env_file"
sed -i.bak 's/DEBUG=.*/DEBUG=true/' "$env_file"
sed -i.bak 's/DATABASE_URL=.*/DATABASE_URL=mysql+aiomysql:\/\/root:Kaopeilian2025!@#@localhost:3306\/kaopeilian?charset=utf8mb4/' "$env_file"
sed -i.bak 's/MYSQL_HOST=.*/MYSQL_HOST=localhost/' "$env_file"
rm -f "$env_file.bak"
;;
production)
sed -i.bak 's/ENV=.*/ENV=production/' "$env_file"
sed -i.bak 's/DEBUG=.*/DEBUG=false/' "$env_file"
sed -i.bak 's/DATABASE_URL=.*/DATABASE_URL=mysql+aiomysql:\/\/root:Kaopeilian2025!@#@mysql:3306\/kaopeilian?charset=utf8mb4/' "$env_file"
sed -i.bak 's/MYSQL_HOST=.*/MYSQL_HOST=mysql/' "$env_file"
rm -f "$env_file.bak"
;;
esac
print_message $GREEN "✅ 后端环境配置已更新"
fi
}
# 设置Docker环境
setup_docker_env() {
print_message $BLUE "设置Docker环境..."
case $ENV_TYPE in
development)
if [ ! -f "docker-compose.dev.yml" ]; then
print_message $RED "❌ 开发环境Docker配置不存在: docker-compose.dev.yml"
return 1
fi
print_message $GREEN "✅ 开发环境Docker配置已就绪"
;;
production)
if [ ! -f "docker-compose.yml" ]; then
print_message $RED "❌ 生产环境Docker配置不存在: docker-compose.yml"
return 1
fi
print_message $GREEN "✅ 生产环境Docker配置已就绪"
;;
esac
}
# 启动环境
start_environment() {
print_message $BLUE "启动$ENV_TYPE环境..."
case $ENV_TYPE in
development)
if command -v docker-compose &> /dev/null; then
docker-compose -f docker-compose.dev.yml up -d
else
docker compose -f docker-compose.dev.yml up -d
fi
;;
production)
if command -v docker-compose &> /dev/null; then
docker-compose up -d
else
docker compose up -d
fi
;;
esac
if [ $? -eq 0 ]; then
print_message $GREEN "✅ 环境启动成功"
# 等待服务启动
print_message $BLUE "等待服务启动..."
sleep 10
# 检查服务状态
./scripts/check_environment.sh
else
print_message $RED "❌ 环境启动失败"
return 1
fi
}
# 显示使用说明
show_usage() {
echo "用法: $0 [环境类型] [强制设置]"
echo ""
echo "环境类型:"
echo " development - 开发环境 (默认)"
echo " production - 生产环境"
echo ""
echo "强制设置:"
echo " true - 强制重新创建配置文件"
echo " false - 保留已存在的配置文件 (默认)"
echo ""
echo "示例:"
echo " $0 # 设置开发环境"
echo " $0 development # 设置开发环境"
echo " $0 production # 设置生产环境"
echo " $0 development true # 强制重新设置开发环境"
}
# 主函数
main() {
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
show_usage
exit 0
fi
if [ "$ENV_TYPE" != "development" ] && [ "$ENV_TYPE" != "production" ]; then
print_message $RED "❌ 无效的环境类型: $ENV_TYPE"
show_usage
exit 1
fi
check_requirements
setup_frontend_env
setup_backend_env
setup_docker_env
# 询问是否启动环境
read -p "是否启动$ENV_TYPE环境? [y/N]: " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
start_environment
fi
print_message $GREEN "=== 环境设置完成 ==="
print_message $BLUE "环境类型: $ENV_TYPE"
print_message $BLUE "前端地址: http://localhost:3001"
print_message $BLUE "后端地址: http://localhost:8000"
print_message $BLUE "API文档: http://localhost:8000/docs"
}
main "$@"

View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Git分支策略配置脚本
echo "=== 配置Git分支策略 ==="
cd /root/aiedu
# 1. 创建production分支如果不存在
if ! git branch | grep -q production; then
echo "创建production分支..."
git checkout -b production
git push origin production
echo "production分支已创建"
else
echo "production分支已存在"
fi
# 2. 切换到production分支
git checkout production
# 3. 更新webhook脚本监听production分支
echo "更新webhook配置..."
sed -i 's/refs\/heads\/main/refs\/heads\/production/g' /root/aiedu/scripts/webhook_handler.py
# 4. 重启webhook服务
systemctl restart kaopeilian-webhook.service
# 5. 创建.gitignore规则
echo "更新.gitignore..."
cat >> /root/aiedu/.gitignore << 'EOF'
# 生产环境配置文件(不提交)
kaopeilian-backend/.env.production
docker-compose.override.yml
# 服务器运行时文件
scripts/force_restart.sh
scripts/diagnose.sh
/var/log/kaopeilian_*.log
EOF
# 6. 提交配置变更
echo "提交配置变更到production分支..."
git add .gitignore scripts/webhook_handler.py
git commit -m "配置生产环境分支策略"
git push origin production
echo ""
echo "=== Git分支策略配置完成 ==="
echo ""
echo "使用说明:"
echo "1. 开发者在main分支开发"
echo "2. 生产环境使用production分支"
echo "3. 发布流程:"
echo " git checkout production"
echo " git merge main"
echo " git push origin production"
echo ""
echo "4. 服务器自动更新监听production分支"

208
deploy/scripts/start-dev.sh Executable file
View File

@@ -0,0 +1,208 @@
#!/bin/bash
# 考培练系统开发环境启动脚本
# 使用方法:
# ./start-dev.sh # 启动基础服务
# ./start-dev.sh --with-admin # 启动服务 + phpMyAdmin
# ./start-dev.sh --with-mail # 启动服务 + 邮件测试
# ./start-dev.sh --full # 启动所有服务
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是否运行
check_docker() {
if ! docker info >/dev/null 2>&1; then
log_error "Docker 未运行,请先启动 Docker"
exit 1
fi
}
# 清理旧容器
cleanup() {
log_info "清理旧容器..."
docker-compose -f docker-compose.dev.yml down --remove-orphans 2>/dev/null || true
# 清理孤立的容器
docker ps -a --filter "name=kaopeilian-" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
}
# 构建镜像
build_images() {
log_info "构建开发环境镜像..."
docker-compose -f docker-compose.dev.yml build --no-cache
}
# 启动服务
start_services() {
local profiles=""
# 解析命令行参数
case "${1:-}" in
--with-admin)
profiles="--profile admin"
log_info "启动完全Docker化服务包含 phpMyAdmin..."
;;
--with-mail)
profiles="--profile mail"
log_info "启动完全Docker化服务包含邮件测试..."
;;
--full)
profiles="--profile admin --profile mail"
log_info "启动所有Docker化服务..."
;;
*)
log_info "启动完全Docker化基础服务前后端+数据库)..."
;;
esac
# 启动所有服务(包括前后端)
docker-compose -f docker-compose.dev.yml up -d frontend-dev backend-dev mysql-dev redis-dev $profiles
}
# 等待服务就绪
wait_for_services() {
log_info "等待服务启动..."
# 等待数据库
log_info "等待 MySQL 数据库启动..."
timeout=60
while ! docker exec kaopeilian-mysql-dev mysqladmin ping -h"localhost" --silent 2>/dev/null; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "MySQL 启动超时"
return 1
fi
sleep 1
done
log_success "MySQL 数据库已就绪"
# 等待 Redis
log_info "等待 Redis 缓存启动..."
timeout=30
while ! docker exec kaopeilian-redis-dev redis-cli ping 2>/dev/null | grep -q PONG; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "Redis 启动超时"
return 1
fi
sleep 1
done
log_success "Redis 缓存已就绪"
# 等待后端服务
log_info "等待后端服务启动..."
timeout=60
while ! curl -s http://localhost:8000/health >/dev/null 2>&1; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "后端服务启动超时"
return 1
fi
sleep 1
done
log_success "后端服务已就绪"
# 等待前端服务
log_info "等待前端服务启动..."
timeout=60
while ! curl -s http://localhost:3001/ >/dev/null 2>&1; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "前端服务启动超时"
return 1
fi
sleep 1
done
log_success "前端服务已就绪"
}
# 显示服务状态
show_status() {
echo ""
log_success "🎉 考培练系统开发环境启动成功!"
echo ""
echo "📋 服务访问地址:"
echo " 🌐 前端开发服务器: http://localhost:3001 (Docker容器)"
echo " 🔧 后端API服务: http://localhost:8000 (Docker容器)"
echo " 📚 API文档: http://localhost:8000/docs"
echo " 🗄️ MySQL数据库: localhost:3306 (Docker容器)"
echo " 🔄 Redis缓存: localhost:6379 (Docker容器)"
if docker ps --filter "name=kaopeilian-phpmyadmin-dev" --format "{{.Names}}" | grep -q phpmyadmin; then
echo " 🛠️ phpMyAdmin: http://localhost:8080"
fi
if docker ps --filter "name=kaopeilian-mailhog-dev" --format "{{.Names}}" | grep -q mailhog; then
echo " 📧 邮件测试界面: http://localhost:8025"
fi
echo ""
echo "🔧 常用命令:"
echo " 查看日志: docker-compose -f docker-compose.dev.yml logs -f [service_name]"
echo " 停止服务: docker-compose -f docker-compose.dev.yml down"
echo " 重启服务: docker-compose -f docker-compose.dev.yml restart [service_name]"
echo ""
echo "💡 开发提示:"
echo " - 🔥 代码修改会自动重载Docker热重载已启用"
echo " - 🎨 前端: 修改 kaopeilian-frontend/src/ 目录下的文件"
echo " - ⚙️ 后端: 修改 kaopeilian-backend/app/ 目录下的文件"
echo " - 🐳 所有服务均运行在Docker容器中环境完全一致"
echo ""
}
# 主函数
main() {
echo "🚀 考培练系统开发环境启动器"
echo "================================"
# 检查 Docker
check_docker
# 清理旧环境
cleanup
# 构建镜像
build_images
# 启动服务
start_services "$@"
# 等待服务就绪
if wait_for_services; then
show_status
exit 0
else
log_error "服务启动失败,请检查日志"
docker-compose -f docker-compose.dev.yml logs --tail=50
exit 1
fi
}
# 如果直接运行此脚本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

186
deploy/scripts/start-kpl.sh Executable file
View File

@@ -0,0 +1,186 @@
#!/bin/bash
# 瑞小美团队开发环境启动脚本
# 域名kpl.ireborn.com.cn
# 使用方法:
# ./start-kpl.sh # 启动基础服务
# ./start-kpl.sh --with-admin # 启动服务 + phpMyAdmin
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是否运行
check_docker() {
if ! docker info >/dev/null 2>&1; then
log_error "Docker 未运行,请先启动 Docker"
exit 1
fi
}
# 构建镜像
build_images() {
log_info "构建KPL开发环境镜像..."
docker compose -f docker-compose.kpl.yml build --no-cache
}
# 启动服务
start_services() {
local profiles=""
# 解析命令行参数
case "${1:-}" in
--with-admin)
profiles="--profile admin"
log_info "启动KPL服务包含 phpMyAdmin..."
;;
*)
log_info "启动KPL基础服务前后端+数据库)..."
;;
esac
# 启动所有服务
docker compose -f docker-compose.kpl.yml up -d $profiles
}
# 等待服务就绪
wait_for_services() {
log_info "等待服务启动..."
# 等待数据库
log_info "等待 MySQL 数据库启动..."
timeout=60
while ! docker exec kpl-mysql-dev mysqladmin ping -h"localhost" --silent 2>/dev/null; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "MySQL 启动超时"
return 1
fi
sleep 1
done
log_success "MySQL 数据库已就绪"
# 等待 Redis
log_info "等待 Redis 缓存启动..."
timeout=30
while ! docker exec kpl-redis-dev redis-cli ping 2>/dev/null | grep -q PONG; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "Redis 启动超时"
return 1
fi
sleep 1
done
log_success "Redis 缓存已就绪"
# 等待后端服务
log_info "等待后端服务启动..."
timeout=60
while ! curl -s http://localhost:8001/health >/dev/null 2>&1; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "后端服务启动超时"
return 1
fi
sleep 1
done
log_success "后端服务已就绪"
# 等待前端服务
log_info "等待前端服务启动..."
timeout=60
while ! curl -s http://localhost:3002/ >/dev/null 2>&1; do
timeout=$((timeout - 1))
if [ $timeout -eq 0 ]; then
log_error "前端服务启动超时"
return 1
fi
sleep 1
done
log_success "前端服务已就绪"
}
# 显示服务状态
show_status() {
echo ""
log_success "🎉 瑞小美团队开发环境启动成功!"
echo ""
echo "📋 服务访问地址:"
echo " 🌐 正式域名访问: https://kpl.ireborn.com.cn"
echo " 🖥️ 本地前端服务: http://localhost:3002"
echo " 🔧 本地后端API: http://localhost:8001"
echo " 📚 API文档: http://localhost:8001/docs"
echo " 🗄️ MySQL数据库: localhost:3308"
echo " 🔄 Redis缓存: localhost:6380"
if docker ps --filter "name=kpl-phpmyadmin-dev" --format "{{.Names}}" | grep -q phpmyadmin; then
echo " 🛠️ phpMyAdmin: http://localhost:8081"
fi
echo ""
echo "🔧 常用命令:"
echo " 查看日志: docker compose -f docker-compose.kpl.yml logs -f [service_name]"
echo " 停止服务: ./stop-kpl.sh 或 docker compose -f docker-compose.kpl.yml down"
echo " 重启服务: docker compose -f docker-compose.kpl.yml restart [service_name]"
echo ""
echo "💡 开发提示:"
echo " - 🔥 代码修改会自动重载(热重载已启用)"
echo " - 🎨 前端: 修改 kaopeilian-frontend/src/ 目录下的文件"
echo " - ⚙️ 后端: 修改 kaopeilian-backend/app/ 目录下的文件"
echo " - 🔒 已配置HTTPS访问使用域名访问更安全"
echo ""
echo "📌 注意:"
echo " - 此环境与演示系统aiedu.ireborn.com.cn完全隔离"
echo " - 拥有独立的数据库和Redis实例"
echo ""
}
# 主函数
main() {
echo "🚀 瑞小美团队开发环境启动器"
echo "================================"
# 检查 Docker
check_docker
# 启动服务(不重新构建)
start_services "$@"
# 等待服务就绪
if wait_for_services; then
show_status
exit 0
else
log_error "服务启动失败,请检查日志"
docker compose -f docker-compose.kpl.yml logs --tail=50
exit 1
fi
}
# 如果直接运行此脚本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

86
deploy/scripts/start.sh Executable file
View File

@@ -0,0 +1,86 @@
#!/bin/bash
# Coze智能体聊天系统启动脚本
echo "🚀 启动Coze智能体聊天系统..."
# 检查是否安装了必要工具
check_command() {
if ! command -v $1 &> /dev/null; then
echo "$1 未安装,请先安装 $1"
exit 1
fi
}
echo "🔍 检查环境..."
check_command python3
check_command node
check_command npm
# 关闭可能影响 coze.cn 访问的系统代理,并设置直连白名单
echo "🛡️ 配置网络直连(禁用代理,放行 *.coze.cn)..."
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY || true
export NO_PROXY=localhost,127.0.0.1,api.coze.cn,.coze.cn
# 启动后端
echo "🐍 启动Python后端..."
cd coze-chat-backend
# 检查虚拟环境
if [ ! -d "venv" ]; then
echo "📦 创建Python虚拟环境..."
python3 -m venv venv
fi
# 激活虚拟环境
source venv/bin/activate
# 安装依赖
echo "📦 安装Python依赖..."
pip install -r requirements.txt
# 检查配置文件
if [ ! -f "local_config.py" ]; then
echo "⚠️ local_config.py文件不存在请先配置API认证"
echo "📋 可以参考 local_config.py.example 创建配置文件"
exit 1
fi
# 后台启动Python服务
echo "🚀 启动后端服务..."
python main.py &
BACKEND_PID=$!
cd ..
# 启动前端
echo "⚛️ 启动React前端..."
cd coze-chat-frontend
# 安装依赖
echo "📦 安装前端依赖..."
npm install
# 启动前端开发服务器
echo "🚀 启动前端服务..."
npm run dev &
FRONTEND_PID=$!
# 显示启动信息
echo ""
echo "✅ 系统启动完成!"
echo "🔗 前端地址: http://localhost:3001"
echo "🔗 后端地址: http://localhost:8010"
echo "📖 API文档: http://localhost:8010/docs"
echo ""
echo "按 Ctrl+C 停止所有服务"
# 等待用户中断
wait
# 清理进程
echo "🧹 清理进程..."
kill $BACKEND_PID 2>/dev/null
kill $FRONTEND_PID 2>/dev/null
echo "👋 系统已停止"

50
deploy/scripts/stop-dev.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# 考培练系统开发环境停止脚本
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"
}
main() {
echo "🛑 停止考培练系统开发环境"
echo "=========================="
log_info "停止所有开发服务..."
docker-compose -f docker-compose.dev.yml down --remove-orphans
# 可选:清理数据卷(谨慎使用)
if [[ "$1" == "--clean-data" ]]; then
log_warning "清理开发数据卷..."
docker volume rm kaopeilian-mysql-dev-data kaopeilian-redis-dev-data 2>/dev/null || true
log_success "数据卷已清理"
fi
# 可选:清理镜像
if [[ "$1" == "--clean-all" ]]; then
log_warning "清理开发镜像..."
docker images --filter "reference=*kaopeilian*dev*" -q | xargs -r docker rmi -f
log_success "开发镜像已清理"
fi
log_success "✅ 开发环境已停止"
}
main "$@"

74
deploy/scripts/stop-kpl.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/bin/bash
# 瑞小美团队开发环境停止脚本
# 使用方法:
# ./stop-kpl.sh # 停止所有KPL服务
# ./stop-kpl.sh --keep-data # 停止服务但保留数据卷
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"
}
# 停止服务
stop_services() {
log_info "停止KPL开发环境服务..."
if [ "$1" = "--keep-data" ]; then
docker compose -f docker-compose.kpl.yml down
log_info "服务已停止,数据卷已保留"
else
docker compose -f docker-compose.kpl.yml down -v
log_warning "服务已停止,数据卷已删除"
fi
}
# 显示状态
show_status() {
echo ""
log_success "✅ KPL开发环境已停止"
echo ""
echo "💡 提示:"
echo " - 重新启动: ./start-kpl.sh"
echo " - 查看演示系统: docker ps | grep kaopeilian"
echo ""
}
# 主函数
main() {
echo "🛑 瑞小美团队开发环境停止器"
echo "================================"
# 停止服务
stop_services "$@"
# 显示状态
show_status
}
# 如果直接运行此脚本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# 测试与课程对话功能 - Dify 集成
echo "============================================================"
echo "🧪 与课程对话功能测试 - Dify 集成"
echo "============================================================"
API_BASE="http://localhost:8000"
# 1. 登录获取 token
echo ""
echo "🔑 正在登录..."
LOGIN_RESPONSE=$(curl -s -X POST "${API_BASE}/api/v1/auth/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=test_user&password=123456")
TOKEN=$(echo $LOGIN_RESPONSE | python3 -c "import sys, json; print(json.load(sys.stdin).get('access_token', ''))")
if [ -z "$TOKEN" ]; then
echo "❌ 登录失败"
echo "响应: $LOGIN_RESPONSE"
exit 1
fi
echo "✅ 登录成功"
echo "Token: ${TOKEN:0:20}..."
# 2. 测试首次对话
echo ""
echo "============================================================"
echo "测试场景 1: 首次对话(创建新会话)"
echo "============================================================"
echo ""
echo "💬 测试与课程 1 对话"
echo "问题: 这门课程讲什么?"
echo ""
echo "📡 SSE 事件流:"
echo "------------------------------------------------------------"
curl -N -X POST "${API_BASE}/api/v1/course/chat" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"course_id": 1,
"query": "这门课程讲什么?"
}'
echo ""
echo "------------------------------------------------------------"
echo ""
echo "✅ 测试完成!"
echo ""
echo "如需测试续接对话,请复制上面输出的 conversation_id然后运行"
echo " curl -N -X POST '${API_BASE}/api/v1/course/chat' \\"
echo " -H 'Authorization: Bearer ${TOKEN}' \\"
echo " -H 'Content-Type: application/json' \\"
echo " -d '{\"course_id\": 1, \"query\": \"能详细说说吗?\", \"conversation_id\": \"你的conversation_id\"}'"

View File

@@ -0,0 +1,39 @@
#!/bin/bash
# 测试所有统计分析API
API_BASE="http://localhost:8000/api/v1/statistics"
PERIOD="month"
echo "=========================================="
echo "测试统计分析API"
echo "=========================================="
echo ""
echo "1⃣ 测试关键指标..."
curl -s "${API_BASE}/key-metrics?period=${PERIOD}" | python3 -m json.tool | head -20
echo ""
echo "2⃣ 测试成绩分布..."
curl -s "${API_BASE}/score-distribution?period=${PERIOD}" | python3 -m json.tool
echo ""
echo "3⃣ 测试难度分析..."
curl -s "${API_BASE}/difficulty-analysis?period=${PERIOD}" | python3 -m json.tool
echo ""
echo "4⃣ 测试知识点掌握度..."
curl -s "${API_BASE}/knowledge-mastery" | python3 -m json.tool | head -30
echo ""
echo "5⃣ 测试学习时长..."
curl -s "${API_BASE}/study-time?period=${PERIOD}" | python3 -m json.tool | head -30
echo ""
echo "6⃣ 测试详细数据..."
curl -s "${API_BASE}/detail?period=${PERIOD}" | python3 -m json.tool | head -40
echo ""
echo "=========================================="
echo "✅ 测试完成"
echo "=========================================="

View File

@@ -0,0 +1,205 @@
#!/usr/bin/env python3
"""
环境配置验证脚本
验证开发和生产环境的配置是否正确
"""
import os
import sys
import json
from pathlib import Path
from urllib.parse import urlparse
def validate_frontend_config(env_file):
"""验证前端环境配置"""
print(f"\n🌐 验证前端配置: {env_file}")
if not os.path.exists(env_file):
print(f"❌ 配置文件不存在: {env_file}")
return False
config = {}
with open(env_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
config[key] = value
# 必需配置检查
required_configs = [
'VITE_APP_TITLE',
'VITE_APP_ENV',
'VITE_API_BASE_URL',
'VITE_WS_BASE_URL'
]
for key in required_configs:
if key not in config:
print(f"❌ 缺少必需配置: {key}")
return False
else:
print(f"{key} = {config[key]}")
# 环境特定验证
env_type = config.get('VITE_APP_ENV', '')
if env_type == 'development':
# 开发环境验证
if 'localhost' not in config.get('VITE_API_BASE_URL', ''):
print("⚠️ 开发环境建议使用localhost")
if config.get('VITE_ENABLE_DEVTOOLS') != 'true':
print("⚠️ 开发环境建议启用开发工具")
elif env_type == 'production':
# 生产环境验证
api_url = config.get('VITE_API_BASE_URL', '')
if 'localhost' in api_url or '127.0.0.1' in api_url:
print("❌ 生产环境不应使用localhost")
return False
if config.get('VITE_ENABLE_DEVTOOLS') == 'true':
print("⚠️ 生产环境建议禁用开发工具")
print("✅ 前端配置验证通过")
return True
def validate_backend_config(env_file):
"""验证后端环境配置"""
print(f"\n🚀 验证后端配置: {env_file}")
if not os.path.exists(env_file):
print(f"❌ 配置文件不存在: {env_file}")
return False
config = {}
with open(env_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
config[key] = value
# 必需配置检查
required_configs = [
'ENV',
'SECRET_KEY',
'DATABASE_URL',
'REDIS_URL'
]
for key in required_configs:
if key not in config:
print(f"❌ 缺少必需配置: {key}")
return False
else:
# 敏感信息脱敏显示
if 'PASSWORD' in key or 'SECRET' in key or 'TOKEN' in key or 'KEY' in key:
value = config[key]
if len(value) > 8:
masked_value = value[:4] + '*' * (len(value) - 8) + value[-4:]
else:
masked_value = '*' * len(value)
print(f"{key} = {masked_value}")
else:
print(f"{key} = {config[key]}")
# 安全性检查
secret_key = config.get('SECRET_KEY', '')
if secret_key in ['your-secret-key', 'your-secret-key-here', 'secret']:
print("❌ 请设置安全的密钥,不要使用默认值")
return False
if len(secret_key) < 32:
print("⚠️ 建议使用至少32位的密钥")
# 数据库URL检查
db_url = config.get('DATABASE_URL', '')
if db_url:
try:
parsed = urlparse(db_url)
print(f"📊 数据库信息: {parsed.scheme}://{parsed.hostname}:{parsed.port}/{parsed.path.lstrip('/')}")
except Exception as e:
print(f"⚠️ 数据库URL格式可能有误: {e}")
# 环境特定验证
env_type = config.get('ENV', '')
if env_type == 'development':
if config.get('DEBUG') != 'true':
print("⚠️ 开发环境建议启用调试模式")
elif env_type == 'production':
if config.get('DEBUG') == 'true':
print("❌ 生产环境不应启用调试模式")
return False
# 生产环境数据库检查
if 'localhost' in db_url or '127.0.0.1' in db_url:
print("⚠️ 生产环境建议使用容器内数据库")
print("✅ 后端配置验证通过")
return True
def validate_docker_config():
"""验证Docker配置"""
print(f"\n🐳 验证Docker配置")
compose_files = [
'docker-compose.dev.yml',
'docker-compose.yml'
]
for compose_file in compose_files:
if os.path.exists(compose_file):
print(f"{compose_file} 存在")
else:
print(f"{compose_file} 不存在")
return True
def main():
"""主函数"""
print("=== 考培练系统环境配置验证 ===")
# 检查工作目录
if not os.path.exists('kaopeilian-frontend') or not os.path.exists('kaopeilian-backend'):
print("❌ 请在项目根目录运行此脚本")
sys.exit(1)
all_valid = True
# 验证前端配置
frontend_configs = [
'kaopeilian-frontend/.env.development',
'kaopeilian-frontend/.env.production'
]
for config_file in frontend_configs:
if os.path.exists(config_file):
if not validate_frontend_config(config_file):
all_valid = False
else:
print(f"⚠️ 前端配置文件不存在: {config_file}")
# 验证后端配置
backend_configs = [
'kaopeilian-backend/.env.development',
'kaopeilian-backend/.env.production'
]
for config_file in backend_configs:
if os.path.exists(config_file):
if not validate_backend_config(config_file):
all_valid = False
else:
print(f"⚠️ 后端配置文件不存在: {config_file}")
# 验证Docker配置
validate_docker_config()
print(f"\n=== 验证结果 ===")
if all_valid:
print("✅ 所有配置验证通过")
sys.exit(0)
else:
print("❌ 配置验证失败,请检查上述错误")
sys.exit(1)
if __name__ == "__main__":
main()

164
deploy/scripts/webhook_handler.py Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
GitHub Webhook处理器
监听GitHub推送事件自动触发部署
"""
import os
import subprocess
import json
import hmac
import hashlib
import logging
from flask import Flask, request, jsonify
import threading
import time
app = Flask(__name__)
# 配置
WEBHOOK_SECRET = "kaopeilian-webhook-secret-2025" # GitHub中配置的密钥
PROJECT_DIR = "/root/aiedu"
UPDATE_SCRIPT = "/root/aiedu/scripts/auto_update.sh"
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/var/log/kaopeilian_webhook.log'),
logging.StreamHandler()
]
)
def verify_signature(payload_body, signature_header):
"""验证GitHub Webhook签名"""
if not signature_header:
return False
hash_object = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload_body,
hashlib.sha256
)
expected_signature = "sha256=" + hash_object.hexdigest()
return hmac.compare_digest(expected_signature, signature_header)
def run_update_async():
"""异步执行更新脚本"""
try:
# 等待5秒再执行避免GitHub推送过程中的竞争条件
time.sleep(5)
result = subprocess.run(
[UPDATE_SCRIPT],
cwd=PROJECT_DIR,
capture_output=True,
text=True,
timeout=600 # 10分钟超时
)
if result.returncode == 0:
logging.info(f"Update completed successfully: {result.stdout}")
else:
logging.error(f"Update failed: {result.stderr}")
except subprocess.TimeoutExpired:
logging.error("Update script timed out")
except Exception as e:
logging.error(f"Error running update script: {e}")
@app.route('/webhook', methods=['POST'])
def github_webhook():
"""处理GitHub Webhook请求"""
# 验证签名
signature = request.headers.get('X-Hub-Signature-256')
if not verify_signature(request.data, signature):
logging.warning("Invalid webhook signature")
return jsonify({"error": "Invalid signature"}), 403
# 解析请求
try:
payload = request.get_json()
except Exception as e:
logging.error(f"Failed to parse JSON: {e}")
return jsonify({"error": "Invalid JSON"}), 400
# 检查事件类型
event_type = request.headers.get('X-GitHub-Event')
if event_type != 'push':
logging.info(f"Ignoring event type: {event_type}")
return jsonify({"message": "Event ignored"}), 200
# 检查分支
ref = payload.get('ref', '')
if ref != 'refs/heads/production':
logging.info(f"Ignoring push to branch: {ref}")
return jsonify({"message": "Branch ignored"}), 200
# 获取提交信息
commit_info = {
'id': payload.get('after', 'unknown'),
'message': payload.get('head_commit', {}).get('message', 'No message'),
'author': payload.get('head_commit', {}).get('author', {}).get('name', 'Unknown'),
'timestamp': payload.get('head_commit', {}).get('timestamp', 'Unknown')
}
logging.info(f"Received push event: {commit_info}")
# 异步触发更新
update_thread = threading.Thread(target=run_update_async)
update_thread.daemon = True
update_thread.start()
return jsonify({
"message": "Update triggered successfully",
"commit": commit_info
}), 200
@app.route('/health', methods=['GET'])
def health_check():
"""健康检查端点"""
return jsonify({
"status": "healthy",
"service": "kaopeilian-webhook",
"timestamp": time.time()
}), 200
@app.route('/status', methods=['GET'])
def status():
"""状态检查端点"""
try:
# 检查项目目录
project_exists = os.path.exists(PROJECT_DIR)
# 检查更新脚本
script_exists = os.path.exists(UPDATE_SCRIPT)
script_executable = os.access(UPDATE_SCRIPT, os.X_OK) if script_exists else False
# 检查Docker服务
docker_result = subprocess.run(['docker', 'compose', 'ps'],
cwd=PROJECT_DIR, capture_output=True)
docker_running = docker_result.returncode == 0
return jsonify({
"status": "ok",
"checks": {
"project_directory": project_exists,
"update_script_exists": script_exists,
"update_script_executable": script_executable,
"docker_compose_running": docker_running
}
}), 200
except Exception as e:
return jsonify({
"status": "error",
"error": str(e)
}), 500
if __name__ == '__main__':
logging.info("Starting GitHub Webhook handler...")
app.run(host='0.0.0.0', port=9000, debug=False)

View File

@@ -0,0 +1,57 @@
#!/bin/bash
# 课程资料预览功能启动脚本
# 用途重建Docker镜像并启动服务
set -e
echo "========================================="
echo "课程资料预览功能启动脚本"
echo "========================================="
echo ""
# 切换到后端目录
cd "$(dirname "$0")/kaopeilian-backend"
echo "步骤 1/4: 停止现有服务..."
docker-compose -f docker-compose.dev.yml down
echo ""
echo "步骤 2/4: 重建后端镜像安装LibreOffice..."
echo "注意首次构建可能需要5-10分钟请耐心等待..."
docker-compose -f docker-compose.dev.yml build backend
echo ""
echo "步骤 3/4: 启动所有服务..."
docker-compose -f docker-compose.dev.yml up -d
echo ""
echo "步骤 4/4: 等待服务启动30秒..."
sleep 30
echo ""
echo "========================================="
echo "服务启动完成!"
echo "========================================="
echo ""
echo "📋 服务信息:"
echo " - 后端API: http://localhost:8000"
echo " - 前端页面: http://localhost:3001"
echo " - 课程详情: http://localhost:3001/trainee/course-detail?id=1"
echo ""
echo "🔍 检查LibreOffice安装状态"
echo " curl http://localhost:8000/api/v1/preview/check-converter"
echo ""
echo "📝 测试建议:"
echo " 1. 先在课程管理中上传各种格式的测试文件"
echo " 2. 访问课程详情页查看资料列表"
echo " 3. 点击不同类型的文件测试预览功能"
echo " 4. 特别测试Office文档的转换预览"
echo ""
echo "📖 详细测试指南:"
echo " 查看文件: kaopeilian-frontend/课程资料预览功能测试指南.md"
echo ""
echo "🔧 查看服务日志:"
echo " docker-compose -f docker-compose.dev.yml logs -f backend"
echo ""

View File

@@ -0,0 +1,92 @@
#!/bin/bash
# 课程资料预览功能测试脚本
# 用途快速测试API接口是否正常
set -e
BASE_URL="http://localhost:8000"
COURSE_ID=1
echo "========================================="
echo "课程资料预览功能测试"
echo "========================================="
echo ""
# 测试1: 检查后端服务是否启动
echo "测试 1/4: 检查后端服务..."
if curl -s "${BASE_URL}/health" > /dev/null 2>&1; then
echo "✅ 后端服务正常"
else
echo "❌ 后端服务未启动,请先运行启动脚本"
exit 1
fi
echo ""
# 测试2: 检查LibreOffice安装
echo "测试 2/4: 检查LibreOffice安装状态..."
CONVERTER_STATUS=$(curl -s "${BASE_URL}/api/v1/preview/check-converter" || echo "{}")
echo "$CONVERTER_STATUS" | python3 -m json.tool 2>/dev/null || echo "$CONVERTER_STATUS"
if echo "$CONVERTER_STATUS" | grep -q '"libreoffice_installed": true'; then
echo "✅ LibreOffice安装成功"
else
echo "⚠️ LibreOffice未安装或检测失败"
echo " 请检查Docker镜像是否正确构建"
fi
echo ""
# 测试3: 获取课程资料列表
echo "测试 3/4: 获取课程资料列表..."
MATERIALS=$(curl -s "${BASE_URL}/api/v1/courses/${COURSE_ID}/materials" || echo "{}")
echo "$MATERIALS" | python3 -m json.tool 2>/dev/null || echo "$MATERIALS"
MATERIAL_COUNT=$(echo "$MATERIALS" | grep -o '"id"' | wc -l)
if [ "$MATERIAL_COUNT" -gt 0 ]; then
echo "✅ 找到 ${MATERIAL_COUNT} 个资料"
else
echo "⚠️ 该课程暂无资料"
echo " 请先在课程管理中上传测试文件"
fi
echo ""
# 测试4: 测试预览接口(如果有资料)
if [ "$MATERIAL_COUNT" -gt 0 ]; then
echo "测试 4/4: 测试资料预览接口..."
# 提取第一个资料的ID
MATERIAL_ID=$(echo "$MATERIALS" | grep -o '"id": *[0-9]*' | head -1 | grep -o '[0-9]*')
if [ -n "$MATERIAL_ID" ]; then
echo " 测试资料ID: ${MATERIAL_ID}"
PREVIEW_INFO=$(curl -s "${BASE_URL}/api/v1/preview/material/${MATERIAL_ID}" || echo "{}")
echo "$PREVIEW_INFO" | python3 -m json.tool 2>/dev/null || echo "$PREVIEW_INFO"
if echo "$PREVIEW_INFO" | grep -q '"preview_type"'; then
echo "✅ 预览接口正常"
else
echo "❌ 预览接口异常"
fi
fi
else
echo "测试 4/4: 跳过(无资料可测试)"
fi
echo ""
echo "========================================="
echo "测试完成!"
echo "========================================="
echo ""
echo "📝 下一步:"
echo " 1. 如果LibreOffice未安装请重新构建Docker镜像"
echo " 2. 如果无资料,请访问管理后台上传测试文件"
echo " 3. 在浏览器中访问课程详情页进行完整测试"
echo " http://localhost:3001/trainee/course-detail?id=${COURSE_ID}"
echo ""
echo "📖 详细测试指南:"
echo " kaopeilian-frontend/课程资料预览功能测试指南.md"
echo ""