diff --git a/.env.example b/.env.example
index e14a26e..0c87966 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,7 @@
# Database Configuration
-# For Docker: postgresql+asyncpg://qquiz:qquiz_password@postgres:5432/qquiz_db
-# For Local: postgresql+asyncpg://localhost:5432/qquiz_db
-DATABASE_URL=postgresql+asyncpg://localhost:5432/qquiz_db
+# For Docker: mysql+aiomysql://qquiz:qquiz_password@mysql:3306/qquiz_db
+# For Local: mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
+DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
# JWT Secret (Please change this in production!)
SECRET_KEY=your-super-secret-key-change-in-production-minimum-32-characters
diff --git a/README.md b/README.md
index e6509b3..71e4809 100644
--- a/README.md
+++ b/README.md
@@ -39,22 +39,33 @@ docker-compose up -d
#### 前置要求
- Python 3.11+
- Node.js 18+
-- PostgreSQL 15+
+- MySQL 8.0+ 或 Docker (用于运行 MySQL)
+**Windows 用户:**
+```bash
+# 双击运行以下脚本之一:
+scripts\fix_and_start.bat # 推荐:自动修复并启动(支持 Docker/本地数据库)
+scripts\start_with_docker_db.bat # 使用 Docker 数据库启动
+scripts\setup.bat # 仅配置环境变量
+```
+
+**Linux/macOS 用户:**
```bash
# 1. 配置环境变量
cp .env.example .env
# 编辑 .env,修改 DATABASE_URL 为本地数据库地址
-# 2. 启动 PostgreSQL
-# macOS: brew services start postgresql
-# Linux: sudo systemctl start postgresql
+# 2. 启动 MySQL
+# macOS: brew services start mysql
+# Linux: sudo systemctl start mysql
# 3. 运行启动脚本
-chmod +x run_local.sh
-./run_local.sh
+chmod +x scripts/run_local.sh
+./scripts/run_local.sh
```
+**MySQL 安装指南:** 详见 [docs/MYSQL_SETUP.md](docs/MYSQL_SETUP.md)
+
## 默认账户
**管理员账户:**
@@ -69,23 +80,35 @@ chmod +x run_local.sh
QQuiz/
├── backend/ # FastAPI 后端
│ ├── alembic/ # 数据库迁移
-│ ├── routers/ # API 路由 (Step 2)
-│ ├── services/ # 业务逻辑 (Step 2)
-│ ├── models.py # 数据模型 ✅
-│ ├── database.py # 数据库配置 ✅
-│ ├── main.py # 应用入口 ✅
-│ └── requirements.txt # Python 依赖 ✅
+│ ├── routers/ # API 路由
+│ ├── services/ # 业务逻辑
+│ ├── models.py # 数据模型
+│ ├── database.py # 数据库配置
+│ ├── main.py # 应用入口
+│ └── requirements.txt # Python 依赖
├── frontend/ # React 前端
│ ├── src/
-│ │ ├── api/ # API 客户端 (Step 3)
-│ │ ├── pages/ # 页面组件 (Step 4)
-│ │ ├── components/ # 通用组件 (Step 4)
-│ │ └── App.jsx # 应用入口 ✅
-│ ├── package.json # Node 依赖 ✅
-│ └── vite.config.js # Vite 配置 ✅
-├── docker-compose.yml # Docker 编排 ✅
-├── .env.example # 环境变量模板 ✅
-└── run_local.sh # 本地运行脚本 ✅
+│ │ ├── api/ # API 客户端
+│ │ ├── pages/ # 页面组件
+│ │ ├── components/ # 通用组件
+│ │ └── App.jsx # 应用入口
+│ ├── package.json # Node 依赖
+│ └── vite.config.js # Vite 配置
+├── scripts/ # 部署和启动脚本
+│ ├── fix_and_start.bat # Windows 快速启动(推荐)
+│ ├── start_with_docker_db.bat # Docker 数据库启动
+│ ├── setup.bat # 环境配置脚本
+│ └── run_local.sh # Linux/macOS 启动脚本
+├── docs/ # 文档目录
+│ ├── QUICK_START.md # 快速入门指南
+│ ├── WINDOWS_DEPLOYMENT.md # Windows 部署指南
+│ ├── DOCKER_MIRROR_SETUP.md # Docker 镜像加速配置
+│ └── PROJECT_STRUCTURE.md # 项目架构详解
+├── test_data/ # 测试数据
+│ └── sample_questions.txt # 示例题目
+├── docker-compose.yml # Docker 编排
+├── .env.example # 环境变量模板
+└── README.md # 项目说明
```
## 核心业务流程
@@ -128,9 +151,10 @@ QQuiz/
**后端:**
- FastAPI - 现代化 Python Web 框架
-- SQLAlchemy - ORM
+- SQLAlchemy 2.0 - 异步 ORM
- Alembic - 数据库迁移
-- PostgreSQL - 数据库
+- MySQL 8.0 - 数据库
+- aiomysql - MySQL 异步驱动
- Pydantic - 数据验证
**前端:**
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 7d0b251..04442ac 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,13 +1,15 @@
fastapi==0.109.0
uvicorn[standard]==0.27.0
sqlalchemy==2.0.25
-asyncpg==0.29.0
+aiomysql==0.2.0
+pymysql==1.1.0
alembic==1.13.1
pydantic==2.5.3
pydantic-settings==2.1.0
python-dotenv==1.0.0
python-multipart==0.0.6
-passlib[bcrypt]==1.7.4
+passlib==1.7.4
+bcrypt==4.0.1
python-jose[cryptography]==3.3.0
aiofiles==23.2.1
httpx==0.26.0
diff --git a/backend/routers/admin.py b/backend/routers/admin.py
index ca0caef..e1ca233 100644
--- a/backend/routers/admin.py
+++ b/backend/routers/admin.py
@@ -24,11 +24,26 @@ async def get_system_config(
result = await db.execute(select(SystemConfig))
configs = {config.key: config.value for config in result.scalars().all()}
+ # Mask API keys (show only first 10 and last 4 characters)
+ def mask_api_key(key):
+ if not key or len(key) < 20:
+ return key
+ return f"{key[:10]}...{key[-4:]}"
+
return {
"allow_registration": configs.get("allow_registration", "true").lower() == "true",
"max_upload_size_mb": int(configs.get("max_upload_size_mb", "10")),
"max_daily_uploads": int(configs.get("max_daily_uploads", "20")),
- "ai_provider": configs.get("ai_provider", "openai")
+ "ai_provider": configs.get("ai_provider", "openai"),
+ # API Configuration
+ "openai_api_key": mask_api_key(configs.get("openai_api_key")),
+ "openai_base_url": configs.get("openai_base_url", "https://api.openai.com/v1"),
+ "openai_model": configs.get("openai_model", "gpt-4o-mini"),
+ "anthropic_api_key": mask_api_key(configs.get("anthropic_api_key")),
+ "anthropic_model": configs.get("anthropic_model", "claude-3-haiku-20240307"),
+ "qwen_api_key": mask_api_key(configs.get("qwen_api_key")),
+ "qwen_base_url": configs.get("qwen_base_url", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
+ "qwen_model": configs.get("qwen_model", "qwen-plus")
}
diff --git a/backend/routers/auth.py b/backend/routers/auth.py
index 1051e3f..bd90425 100644
--- a/backend/routers/auth.py
+++ b/backend/routers/auth.py
@@ -83,9 +83,12 @@ async def login(
# Create access token
access_token = create_access_token(
- data={"sub": user.id}
+ data={"sub": str(user.id)} # JWT 'sub' must be a string
)
+ print(f"✅ Login successful: user={user.username}, id={user.id}")
+ print(f"🔑 Generated token (first 50 chars): {access_token[:50]}...")
+
return {
"access_token": access_token,
"token_type": "bearer"
diff --git a/backend/routers/exam.py b/backend/routers/exam.py
index 2594d68..2d56165 100644
--- a/backend/routers/exam.py
+++ b/backend/routers/exam.py
@@ -17,7 +17,8 @@ from schemas import (
)
from services.auth_service import get_current_user
from services.document_parser import document_parser
-from services.llm_service import llm_service
+from services.llm_service import LLMService
+from services.config_service import load_llm_config
from utils import is_allowed_file, calculate_content_hash
router = APIRouter()
@@ -151,6 +152,10 @@ async def async_parse_and_save(
if not text_content or len(text_content.strip()) < 10:
raise Exception("Document appears to be empty or too short")
+ # Load LLM configuration from database
+ llm_config = await load_llm_config(db)
+ llm_service = LLMService(config=llm_config)
+
# Parse questions using LLM
print(f"[Exam {exam_id}] Calling LLM to extract questions...")
questions_data = await llm_service.parse_document(text_content)
diff --git a/backend/routers/question.py b/backend/routers/question.py
index 869b98d..19c6490 100644
--- a/backend/routers/question.py
+++ b/backend/routers/question.py
@@ -13,7 +13,8 @@ from schemas import (
AnswerSubmit, AnswerCheckResponse
)
from services.auth_service import get_current_user
-from services.llm_service import llm_service
+from services.llm_service import LLMService
+from services.config_service import load_llm_config
router = APIRouter()
@@ -177,6 +178,10 @@ async def check_answer(
# Check answer based on question type
if question.type == QuestionType.SHORT:
+ # Load LLM configuration from database
+ llm_config = await load_llm_config(db)
+ llm_service = LLMService(config=llm_config)
+
# Use AI to grade short answer
grading = await llm_service.grade_short_answer(
question.content,
diff --git a/backend/schemas.py b/backend/schemas.py
index 4481532..d1c6134 100644
--- a/backend/schemas.py
+++ b/backend/schemas.py
@@ -45,6 +45,15 @@ class SystemConfigUpdate(BaseModel):
max_upload_size_mb: Optional[int] = None
max_daily_uploads: Optional[int] = None
ai_provider: Optional[str] = None
+ # API Configuration
+ openai_api_key: Optional[str] = None
+ openai_base_url: Optional[str] = None
+ openai_model: Optional[str] = None
+ anthropic_api_key: Optional[str] = None
+ anthropic_model: Optional[str] = None
+ qwen_api_key: Optional[str] = None
+ qwen_base_url: Optional[str] = None
+ qwen_model: Optional[str] = None
class SystemConfigResponse(BaseModel):
@@ -52,6 +61,15 @@ class SystemConfigResponse(BaseModel):
max_upload_size_mb: int
max_daily_uploads: int
ai_provider: str
+ # API Configuration
+ openai_api_key: Optional[str] = None
+ openai_base_url: Optional[str] = None
+ openai_model: Optional[str] = None
+ anthropic_api_key: Optional[str] = None
+ anthropic_model: Optional[str] = None
+ qwen_api_key: Optional[str] = None
+ qwen_base_url: Optional[str] = None
+ qwen_model: Optional[str] = None
# ============ Exam Schemas ============
diff --git a/backend/services/auth_service.py b/backend/services/auth_service.py
index 41f8082..80c5266 100644
--- a/backend/services/auth_service.py
+++ b/backend/services/auth_service.py
@@ -28,13 +28,24 @@ async def get_current_user(
headers={"WWW-Authenticate": "Bearer"},
)
+ print(f"🔍 Received token (first 50 chars): {token[:50] if token else 'None'}...")
+
# Decode token
payload = decode_access_token(token)
if payload is None:
+ print(f"❌ Token decode failed - Invalid or expired token")
raise credentials_exception
- user_id: int = payload.get("sub")
+ user_id = payload.get("sub")
if user_id is None:
+ print(f"❌ No 'sub' in payload: {payload}")
+ raise credentials_exception
+
+ # Convert user_id to int if it's a string
+ try:
+ user_id = int(user_id)
+ except (ValueError, TypeError):
+ print(f"❌ Invalid user_id format: {user_id}")
raise credentials_exception
# Get user from database
@@ -42,8 +53,10 @@ async def get_current_user(
user = result.scalar_one_or_none()
if user is None:
+ print(f"❌ User not found with id: {user_id}")
raise credentials_exception
+ print(f"✅ User authenticated: {user.username} (id={user.id})")
return user
diff --git a/backend/services/config_service.py b/backend/services/config_service.py
new file mode 100644
index 0000000..69b0787
--- /dev/null
+++ b/backend/services/config_service.py
@@ -0,0 +1,43 @@
+"""
+Configuration Service - Load system configuration from database
+"""
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy import select
+from typing import Dict
+from models import SystemConfig
+
+
+async def load_llm_config(db: AsyncSession) -> Dict[str, str]:
+ """
+ Load LLM configuration from database.
+
+ Returns a dictionary with all LLM-related configuration:
+ {
+ 'ai_provider': 'openai',
+ 'openai_api_key': 'sk-...',
+ 'openai_base_url': 'https://api.openai.com/v1',
+ 'openai_model': 'gpt-4o-mini',
+ ...
+ }
+ """
+ # Fetch all config from database
+ result = await db.execute(select(SystemConfig))
+ db_configs = {config.key: config.value for config in result.scalars().all()}
+
+ # Build configuration dictionary
+ config = {
+ 'ai_provider': db_configs.get('ai_provider', 'openai'),
+ # OpenAI
+ 'openai_api_key': db_configs.get('openai_api_key'),
+ 'openai_base_url': db_configs.get('openai_base_url', 'https://api.openai.com/v1'),
+ 'openai_model': db_configs.get('openai_model', 'gpt-4o-mini'),
+ # Anthropic
+ 'anthropic_api_key': db_configs.get('anthropic_api_key'),
+ 'anthropic_model': db_configs.get('anthropic_model', 'claude-3-haiku-20240307'),
+ # Qwen
+ 'qwen_api_key': db_configs.get('qwen_api_key'),
+ 'qwen_base_url': db_configs.get('qwen_base_url', 'https://dashscope.aliyuncs.com/compatible-mode/v1'),
+ 'qwen_model': db_configs.get('qwen_model', 'qwen-plus')
+ }
+
+ return config
diff --git a/backend/services/llm_service.py b/backend/services/llm_service.py
index 2ebe2be..9b77624 100644
--- a/backend/services/llm_service.py
+++ b/backend/services/llm_service.py
@@ -15,28 +15,53 @@ from utils import calculate_content_hash
class LLMService:
"""Service for interacting with various LLM providers"""
- def __init__(self):
- self.provider = os.getenv("AI_PROVIDER", "openai")
+ def __init__(self, config: Optional[Dict[str, str]] = None):
+ """
+ Initialize LLM Service with optional configuration.
+ If config is not provided, falls back to environment variables.
+
+ Args:
+ config: Dictionary with keys like 'ai_provider', 'openai_api_key', etc.
+ """
+ # Get provider from config or environment
+ self.provider = (config or {}).get("ai_provider") or os.getenv("AI_PROVIDER", "openai")
if self.provider == "openai":
+ api_key = (config or {}).get("openai_api_key") or os.getenv("OPENAI_API_KEY")
+ base_url = (config or {}).get("openai_base_url") or os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
+ self.model = (config or {}).get("openai_model") or os.getenv("OPENAI_MODEL", "gpt-4o-mini")
+
+ if not api_key:
+ raise ValueError("OpenAI API key not configured")
+
self.client = AsyncOpenAI(
- api_key=os.getenv("OPENAI_API_KEY"),
- base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
+ api_key=api_key,
+ base_url=base_url
)
- self.model = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
elif self.provider == "anthropic":
+ api_key = (config or {}).get("anthropic_api_key") or os.getenv("ANTHROPIC_API_KEY")
+ self.model = (config or {}).get("anthropic_model") or os.getenv("ANTHROPIC_MODEL", "claude-3-haiku-20240307")
+
+ if not api_key:
+ raise ValueError("Anthropic API key not configured")
+
self.client = AsyncAnthropic(
- api_key=os.getenv("ANTHROPIC_API_KEY")
+ api_key=api_key
)
- self.model = os.getenv("ANTHROPIC_MODEL", "claude-3-haiku-20240307")
elif self.provider == "qwen":
+ api_key = (config or {}).get("qwen_api_key") or os.getenv("QWEN_API_KEY")
+ base_url = (config or {}).get("qwen_base_url") or os.getenv("QWEN_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
+ self.model = (config or {}).get("qwen_model") or os.getenv("QWEN_MODEL", "qwen-plus")
+
+ if not api_key:
+ raise ValueError("Qwen API key not configured")
+
self.client = AsyncOpenAI(
- api_key=os.getenv("QWEN_API_KEY"),
- base_url=os.getenv("QWEN_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
+ api_key=api_key,
+ base_url=base_url
)
- self.model = os.getenv("QWEN_MODEL", "qwen-plus")
else:
raise ValueError(f"Unsupported AI provider: {self.provider}")
diff --git a/backend/utils.py b/backend/utils.py
index 74f37c4..c608c8f 100644
--- a/backend/utils.py
+++ b/backend/utils.py
@@ -37,6 +37,8 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
+ print(f"🔑 Creating token with SECRET_KEY (first 20 chars): {SECRET_KEY[:20]}...")
+ print(f"📦 Token payload: {to_encode}")
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@@ -44,9 +46,12 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
def decode_access_token(token: str) -> Optional[dict]:
"""Decode a JWT access token"""
try:
+ print(f"🔑 SECRET_KEY (first 20 chars): {SECRET_KEY[:20]}...")
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
+ print(f"✅ Token decoded successfully: {payload}")
return payload
- except JWTError:
+ except JWTError as e:
+ print(f"❌ JWT decode error: {type(e).__name__}: {str(e)}")
return None
diff --git a/docker-compose.yml b/docker-compose.yml
index 2e7ad0e..76c1d75 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,17 +1,19 @@
services:
- postgres:
- image: postgres:15-alpine
- container_name: qquiz_postgres
+ mysql:
+ image: mysql:8.0
+ container_name: qquiz_mysql
environment:
- POSTGRES_USER: qquiz
- POSTGRES_PASSWORD: qquiz_password
- POSTGRES_DB: qquiz_db
+ MYSQL_ROOT_PASSWORD: root_password
+ MYSQL_DATABASE: qquiz_db
+ MYSQL_USER: qquiz
+ MYSQL_PASSWORD: qquiz_password
volumes:
- - postgres_data:/var/lib/postgresql/data
+ - mysql_data:/var/lib/mysql
ports:
- - "5432:5432"
+ - "3306:3306"
+ command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
- test: ["CMD-SHELL", "pg_isready -U qquiz"]
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "qquiz", "-pqquiz_password"]
interval: 10s
timeout: 5s
retries: 5
@@ -22,7 +24,7 @@ services:
dockerfile: Dockerfile
container_name: qquiz_backend
environment:
- - DATABASE_URL=postgresql+asyncpg://qquiz:qquiz_password@postgres:5432/qquiz_db
+ - DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@mysql:3306/qquiz_db
env_file:
- .env
volumes:
@@ -31,7 +33,7 @@ services:
ports:
- "8000:8000"
depends_on:
- postgres:
+ mysql:
condition: service_healthy
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
@@ -46,11 +48,11 @@ services:
ports:
- "3000:3000"
environment:
- - REACT_APP_API_URL=http://localhost:8000
+ - VITE_API_URL=http://localhost:8000
depends_on:
- backend
command: npm start
volumes:
- postgres_data:
+ mysql_data:
upload_files:
diff --git a/DEPLOYMENT.md b/docs/DEPLOYMENT.md
similarity index 100%
rename from DEPLOYMENT.md
rename to docs/DEPLOYMENT.md
diff --git a/DOCKER_MIRROR_SETUP.md b/docs/DOCKER_MIRROR_SETUP.md
similarity index 100%
rename from DOCKER_MIRROR_SETUP.md
rename to docs/DOCKER_MIRROR_SETUP.md
diff --git a/GITHUB_PUSH_GUIDE.md b/docs/GITHUB_PUSH_GUIDE.md
similarity index 100%
rename from GITHUB_PUSH_GUIDE.md
rename to docs/GITHUB_PUSH_GUIDE.md
diff --git a/docs/MYSQL_SETUP.md b/docs/MYSQL_SETUP.md
new file mode 100644
index 0000000..7997a04
--- /dev/null
+++ b/docs/MYSQL_SETUP.md
@@ -0,0 +1,262 @@
+# MySQL 安装与配置指南
+
+QQuiz 使用 MySQL 8.0 作为数据库,你可以选择 Docker 部署或本地安装。
+
+## 方式一:使用 Docker (推荐)
+
+### 优点
+- 无需手动安装 MySQL
+- 自动配置和初始化
+- 隔离环境,不影响系统
+
+### 使用步骤
+
+1. **安装 Docker Desktop**
+ - 下载地址:https://www.docker.com/products/docker-desktop/
+ - 安装后启动 Docker Desktop
+
+2. **运行启动脚本**
+ ```bash
+ scripts\fix_and_start.bat
+ ```
+ 选择 **[1] Use Docker**
+
+3. **完成!**
+ - Docker 会自动下载 MySQL 镜像
+ - 自动创建数据库和用户
+ - 自动启动服务
+
+---
+
+## 方式二:本地安装 MySQL
+
+### 下载 MySQL
+
+1. 访问 MySQL 官网下载页面:
+ https://dev.mysql.com/downloads/installer/
+
+2. 选择 **MySQL Installer for Windows**
+
+3. 下载 `mysql-installer-community-8.0.x.x.msi`
+
+### 安装步骤
+
+1. **运行安装程序**
+ - 双击下载的 .msi 文件
+
+2. **选择安装类型**
+ - 选择 "Developer Default" 或 "Server only"
+ - 点击 Next
+
+3. **配置 MySQL Server**
+ - **Config Type**: Development Computer
+ - **Port**: 3306 (默认)
+ - **Authentication Method**: 选择 "Use Strong Password Encryption"
+
+4. **设置 Root 密码**
+ - 输入并记住 root 用户的密码
+ - 建议密码:`root` (开发环境)
+
+5. **Windows Service 配置**
+ - ✅ Configure MySQL Server as a Windows Service
+ - Service Name: MySQL80
+ - ✅ Start the MySQL Server at System Startup
+
+6. **完成安装**
+ - 点击 Execute 开始安装
+ - 等待安装完成
+ - 点击 Finish
+
+### 验证安装
+
+打开命令提示符,运行:
+
+```bash
+mysql --version
+```
+
+应该显示:`mysql Ver 8.0.x for Win64 on x86_64`
+
+### 配置 QQuiz 数据库
+
+**方式 A:使用脚本自动创建 (推荐)**
+
+运行:
+```bash
+scripts\fix_and_start.bat
+```
+选择 **[2] Use Local MySQL**
+
+**方式 B:手动创建**
+
+1. 打开 MySQL 命令行客户端:
+ ```bash
+ mysql -u root -p
+ ```
+
+2. 输入 root 密码
+
+3. 创建数据库和用户:
+ ```sql
+ CREATE DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+ CREATE USER 'qquiz'@'localhost' IDENTIFIED BY 'qquiz_password';
+ GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'localhost';
+ FLUSH PRIVILEGES;
+ EXIT;
+ ```
+
+---
+
+## 数据库配置说明
+
+### .env 文件配置
+
+确保 `.env` 文件中的数据库连接字符串正确:
+
+**本地 MySQL:**
+```env
+DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
+```
+
+**Docker MySQL:**
+```env
+DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@mysql:3306/qquiz_db
+```
+
+### 连接参数说明
+
+- `mysql+aiomysql://` - 使用 aiomysql 异步驱动
+- `qquiz` - 数据库用户名
+- `qquiz_password` - 数据库密码
+- `localhost` 或 `mysql` - 数据库主机地址
+- `3306` - MySQL 默认端口
+- `qquiz_db` - 数据库名称
+
+---
+
+## 常见问题
+
+### 1. 端口 3306 被占用
+
+**错误信息:**
+```
+Error: Port 3306 is already in use
+```
+
+**解决方案:**
+- 检查是否已经有 MySQL 运行:`netstat -ano | findstr :3306`
+- 停止现有的 MySQL 服务
+- 或修改 `.env` 中的端口号
+
+### 2. 无法连接到 MySQL
+
+**错误信息:**
+```
+Can't connect to MySQL server on 'localhost'
+```
+
+**解决方案:**
+
+1. **检查 MySQL 服务是否运行**
+ - 按 Win+R,输入 `services.msc`
+ - 查找 "MySQL80" 服务
+ - 确认状态为 "正在运行"
+
+2. **启动 MySQL 服务**
+ ```bash
+ net start MySQL80
+ ```
+
+3. **检查防火墙设置**
+ - 确保防火墙允许 MySQL 端口 3306
+
+### 3. 密码验证失败
+
+**错误信息:**
+```
+Access denied for user 'qquiz'@'localhost'
+```
+
+**解决方案:**
+
+重新创建用户并设置密码:
+```sql
+mysql -u root -p
+DROP USER IF EXISTS 'qquiz'@'localhost';
+CREATE USER 'qquiz'@'localhost' IDENTIFIED BY 'qquiz_password';
+GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'localhost';
+FLUSH PRIVILEGES;
+```
+
+### 4. 字符集问题
+
+**解决方案:**
+
+确保数据库使用 UTF-8 字符集:
+```sql
+ALTER DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+```
+
+---
+
+## 管理工具推荐
+
+### 1. MySQL Workbench (官方)
+- 下载:https://dev.mysql.com/downloads/workbench/
+- 功能:可视化数据库管理、SQL 编辑器、备份还原
+
+### 2. DBeaver (免费开源)
+- 下载:https://dbeaver.io/download/
+- 功能:多数据库支持、数据导入导出、ER 图
+
+### 3. phpMyAdmin (Web 界面)
+- 适合习惯 Web 界面的用户
+
+---
+
+## 数据库备份与恢复
+
+### 备份数据库
+
+```bash
+mysqldump -u qquiz -p qquiz_db > backup.sql
+```
+
+### 恢复数据库
+
+```bash
+mysql -u qquiz -p qquiz_db < backup.sql
+```
+
+---
+
+## 切换回 PostgreSQL
+
+如果需要切换回 PostgreSQL:
+
+1. 修改 `requirements.txt`:
+ ```
+ asyncpg==0.29.0 # 替换 aiomysql
+ ```
+
+2. 修改 `.env`:
+ ```
+ DATABASE_URL=postgresql+asyncpg://qquiz:qquiz_password@localhost:5432/qquiz_db
+ ```
+
+3. 修改 `docker-compose.yml`:
+ - 将 `mysql` 服务改回 `postgres`
+
+4. 重新安装依赖:
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+---
+
+## 技术支持
+
+如遇到其他问题,请:
+1. 检查 MySQL 错误日志
+2. 确认防火墙和网络配置
+3. 查看项目 issues: https://github.com/handsomezhuzhu/QQuiz/issues
diff --git a/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md
similarity index 100%
rename from PROJECT_STRUCTURE.md
rename to docs/PROJECT_STRUCTURE.md
diff --git a/QUICK_START.md b/docs/QUICK_START.md
similarity index 100%
rename from QUICK_START.md
rename to docs/QUICK_START.md
diff --git a/README_QUICKSTART.md b/docs/README_QUICKSTART.md
similarity index 100%
rename from README_QUICKSTART.md
rename to docs/README_QUICKSTART.md
diff --git a/START_HERE.txt b/docs/START_HERE.txt
similarity index 100%
rename from START_HERE.txt
rename to docs/START_HERE.txt
diff --git a/WINDOWS_DEPLOYMENT.md b/docs/WINDOWS_DEPLOYMENT.md
similarity index 100%
rename from WINDOWS_DEPLOYMENT.md
rename to docs/WINDOWS_DEPLOYMENT.md
diff --git a/frontend/src/pages/AdminSettings.jsx b/frontend/src/pages/AdminSettings.jsx
index f7397bf..89d17c1 100644
--- a/frontend/src/pages/AdminSettings.jsx
+++ b/frontend/src/pages/AdminSettings.jsx
@@ -1,21 +1,38 @@
/**
- * Admin Settings Page
+ * Admin Settings Page - Enhanced with API Configuration
*/
import React, { useState, useEffect } from 'react'
import { adminAPI } from '../api/client'
import { useAuth } from '../context/AuthContext'
-import { Settings, Save, Loader } from 'lucide-react'
+import { Settings, Save, Loader, Key, Link as LinkIcon, Eye, EyeOff } from 'lucide-react'
import toast from 'react-hot-toast'
export const AdminSettings = () => {
const { user } = useAuth()
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
+ const [showApiKeys, setShowApiKeys] = useState({
+ openai: false,
+ anthropic: false,
+ qwen: false
+ })
+
const [config, setConfig] = useState({
allow_registration: true,
max_upload_size_mb: 10,
max_daily_uploads: 20,
- ai_provider: 'openai'
+ ai_provider: 'openai',
+ // OpenAI
+ openai_api_key: '',
+ openai_base_url: 'https://api.openai.com/v1',
+ openai_model: 'gpt-4o-mini',
+ // Anthropic
+ anthropic_api_key: '',
+ anthropic_model: 'claude-3-haiku-20240307',
+ // Qwen
+ qwen_api_key: '',
+ qwen_base_url: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+ qwen_model: 'qwen-plus'
})
useEffect(() => {
@@ -54,6 +71,36 @@ export const AdminSettings = () => {
})
}
+ const toggleApiKeyVisibility = (provider) => {
+ setShowApiKeys({
+ ...showApiKeys,
+ [provider]: !showApiKeys[provider]
+ })
+ }
+
+ // Get complete API endpoint URL
+ const getCompleteEndpoint = (provider) => {
+ const endpoints = {
+ openai: '/chat/completions',
+ anthropic: '/messages',
+ qwen: '/chat/completions'
+ }
+
+ let baseUrl = ''
+ if (provider === 'openai') {
+ baseUrl = config.openai_base_url || 'https://api.openai.com/v1'
+ } else if (provider === 'anthropic') {
+ baseUrl = 'https://api.anthropic.com/v1'
+ } else if (provider === 'qwen') {
+ baseUrl = config.qwen_base_url || 'https://dashscope.aliyuncs.com/compatible-mode/v1'
+ }
+
+ // Remove trailing slash
+ baseUrl = baseUrl.replace(/\/$/, '')
+
+ return `${baseUrl}${endpoints[provider]}`
+ }
+
if (loading) {
return (
@@ -66,7 +113,7 @@ export const AdminSettings = () => {
{/* Header */}
-
+
@@ -78,8 +125,11 @@ export const AdminSettings = () => {
{/* Content */}
-
+
+ {/* Basic Settings */}
+
基础设置
+
{/* Allow Registration */}
@@ -144,31 +194,244 @@ export const AdminSettings = () => {
- 需在 .env 文件中配置对应的 API Key
+ 选择后在下方配置对应的 API 密钥
+
+
+
+
+ {/* OpenAI Configuration */}
+
+
+
+
OpenAI 配置
+ {config.ai_provider === 'openai' && (
+ 当前使用
+ )}
+
+
+ {/* API Key */}
+
+
+
+ handleChange('openai_api_key', e.target.value)}
+ placeholder="sk-proj-..."
+ className="w-full px-4 py-2 pr-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent font-mono text-sm"
+ />
+
+
+
从 https://platform.openai.com/api-keys 获取
+
+
+ {/* Base URL */}
+
+
+
+
+ handleChange('openai_base_url', e.target.value)}
+ placeholder="https://api.openai.com/v1"
+ className="w-full pl-10 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent font-mono text-sm"
+ />
+
+
+ 完整 endpoint: {getCompleteEndpoint('openai')}
- {/* Save Button */}
-
-
+
+
+
+
+
+
+ {/* Anthropic Configuration */}
+
+
+
+
Anthropic 配置
+ {config.ai_provider === 'anthropic' && (
+ 当前使用
+ )}
+
+
+ {/* API Key */}
+
+
+
+ handleChange('anthropic_api_key', e.target.value)}
+ placeholder="sk-ant-..."
+ className="w-full px-4 py-2 pr-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent font-mono text-sm"
+ />
+
+
+
从 https://console.anthropic.com/settings/keys 获取
+
+
+ {/* Base URL (fixed for Anthropic) */}
+
+
+
+
+
+
+
+ 完整 endpoint: {getCompleteEndpoint('anthropic')}
+
+
+
+ {/* Model */}
+
+
+
+
+
+
+ {/* Qwen Configuration */}
+
+
+
+
通义千问 配置
+ {config.ai_provider === 'qwen' && (
+ 当前使用
+ )}
+
+
+ {/* API Key */}
+
+
+
+ handleChange('qwen_api_key', e.target.value)}
+ placeholder="sk-..."
+ className="w-full px-4 py-2 pr-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent font-mono text-sm"
+ />
+
+
+
从 https://dashscope.console.aliyun.com/apiKey 获取
+
+
+ {/* Base URL */}
+
+
+
+
+ handleChange('qwen_base_url', e.target.value)}
+ placeholder="https://dashscope.aliyuncs.com/compatible-mode/v1"
+ className="w-full pl-10 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent font-mono text-sm"
+ />
+
+
+ 完整 endpoint: {getCompleteEndpoint('qwen')}
+
+
+
+ {/* Model */}
+
+
+
+
+
+
+ {/* Save Button */}
+
+
+
)
diff --git a/activate_venv.bat b/scripts/activate_venv.bat
similarity index 78%
rename from activate_venv.bat
rename to scripts/activate_venv.bat
index 94abd1f..0d23fd4 100644
--- a/activate_venv.bat
+++ b/scripts/activate_venv.bat
@@ -1,5 +1,5 @@
@echo off
REM 激活虚拟环境的批处理脚本
-cd /d "%~dp0backend"
+cd /d "%~dp0..\backend"
call venv\Scripts\activate.bat
cmd /k
diff --git a/auto_setup_and_run.bat b/scripts/auto_setup_and_run.bat
similarity index 90%
rename from auto_setup_and_run.bat
rename to scripts/auto_setup_and_run.bat
index 30101b7..8a3a260 100644
--- a/auto_setup_and_run.bat
+++ b/scripts/auto_setup_and_run.bat
@@ -17,7 +17,7 @@ echo 全自动部署脚本
echo ==========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
REM ============================================
REM 步骤 1: 检查环境
@@ -197,7 +197,7 @@ REM ============================================
echo [7/8] 启动后端服务...
echo.
-start "QQuiz Backend" cmd /k "cd /d %~dp0backend && call venv\Scripts\activate.bat && echo ======================================== && echo QQuiz 后端服务 && echo ======================================== && echo. && echo API 地址: http://localhost:8000 && echo API 文档: http://localhost:8000/docs && echo. && echo 按 Ctrl+C 停止服务 && echo. && uvicorn main:app --reload"
+start "QQuiz Backend" cmd /k "cd /d %~dp0..\backend && call venv\Scripts\activate.bat && echo ======================================== && echo QQuiz 后端服务 && echo ======================================== && echo. && echo API 地址: http://localhost:8000 && echo API 文档: http://localhost:8000/docs && echo. && echo 按 Ctrl+C 停止服务 && echo. && uvicorn main:app --reload"
echo ✓ 后端服务已在新窗口中启动
echo 等待服务启动...
@@ -210,7 +210,7 @@ REM ============================================
echo [8/8] 启动前端服务...
echo.
-start "QQuiz Frontend" cmd /k "cd /d %~dp0frontend && echo ======================================== && echo QQuiz 前端服务 && echo ======================================== && echo. && echo 前端地址: http://localhost:3000 && echo. && echo 按 Ctrl+C 停止服务 && echo. && npm start"
+start "QQuiz Frontend" cmd /k "cd /d %~dp0..\frontend && echo ======================================== && echo QQuiz 前端服务 && echo ======================================== && echo. && echo 前端地址: http://localhost:3000 && echo. && echo 按 Ctrl+C 停止服务 && echo. && npm start"
echo ✓ 前端服务已在新窗口中启动
echo 等待服务启动...
diff --git a/scripts/check_postgres.bat b/scripts/check_postgres.bat
new file mode 100644
index 0000000..d007d31
--- /dev/null
+++ b/scripts/check_postgres.bat
@@ -0,0 +1,93 @@
+@echo off
+title PostgreSQL Status Check
+color 0B
+
+echo.
+echo ========================================
+echo PostgreSQL Database Status Check
+echo ========================================
+echo.
+
+REM Check PostgreSQL installation
+echo [1] Checking PostgreSQL Installation...
+if exist "C:\Program Files\PostgreSQL\18" (
+ echo OK - PostgreSQL 18 is installed
+ echo Location: C:\Program Files\PostgreSQL\18
+) else (
+ echo ERROR - PostgreSQL 18 not found!
+ pause
+ exit /b 1
+)
+echo.
+
+REM Check PostgreSQL service
+echo [2] Checking PostgreSQL Service...
+sc query postgresql-x64-18 >nul 2>&1
+if %errorlevel% equ 0 (
+ echo OK - PostgreSQL service exists
+ echo.
+ echo Service details:
+ sc query postgresql-x64-18
+ echo.
+) else (
+ echo WARNING - PostgreSQL service not found!
+ echo Trying alternative names...
+ sc query | findstr /i "postgres"
+)
+echo.
+
+REM Check if port 5432 is listening
+echo [3] Checking Port 5432...
+netstat -ano | findstr ":5432" | findstr "LISTENING" >nul
+if %errorlevel% equ 0 (
+ echo OK - PostgreSQL is listening on port 5432
+ netstat -ano | findstr ":5432" | findstr "LISTENING"
+) else (
+ echo ERROR - Port 5432 is NOT listening!
+ echo PostgreSQL service is probably not running.
+ echo.
+ echo To start the service, you can:
+ echo 1. Open Services (services.msc)
+ echo 2. Find "postgresql-x64-18" service
+ echo 3. Right-click and select "Start"
+ echo.
+ echo OR run: net start postgresql-x64-18
+)
+echo.
+
+REM Try to connect to database
+echo [4] Testing Database Connection...
+set PGPASSWORD=postgres
+"C:\Program Files\PostgreSQL\18\pgAdmin 4\runtime\psql.exe" -h localhost -U postgres -c "SELECT version();" postgres >nul 2>&1
+if %errorlevel% equ 0 (
+ echo OK - Successfully connected to PostgreSQL!
+ echo.
+ "C:\Program Files\PostgreSQL\18\pgAdmin 4\runtime\psql.exe" -h localhost -U postgres -c "SELECT version();" postgres
+ echo.
+
+ REM Check if qquiz database exists
+ echo [5] Checking QQuiz Database...
+ "C:\Program Files\PostgreSQL\18\pgAdmin 4\runtime\psql.exe" -h localhost -U postgres -c "\l" postgres | findstr "qquiz_db" >nul
+ if %errorlevel% equ 0 (
+ echo OK - qquiz_db database exists
+ ) else (
+ echo INFO - qquiz_db database does not exist yet
+ echo This is normal for first-time setup
+ )
+) else (
+ echo ERROR - Cannot connect to PostgreSQL!
+ echo.
+ echo Possible reasons:
+ echo 1. PostgreSQL service is not running
+ echo 2. Default password 'postgres' is incorrect
+ echo 3. PostgreSQL is not configured to accept local connections
+ echo.
+ echo Please start the PostgreSQL service first!
+)
+echo.
+
+echo ========================================
+echo Check Complete
+echo ========================================
+echo.
+pause
diff --git a/check_status.bat b/scripts/check_status.bat
similarity index 99%
rename from check_status.bat
rename to scripts/check_status.bat
index cfb09bc..ed1f6c8 100644
--- a/check_status.bat
+++ b/scripts/check_status.bat
@@ -1,6 +1,8 @@
@echo off
title QQuiz - System Status Check
+cd /d "%~dp0.."
+
echo.
echo ========================================
echo QQuiz System Status Check
diff --git a/fix_and_start.bat b/scripts/fix_and_start.bat
similarity index 79%
rename from fix_and_start.bat
rename to scripts/fix_and_start.bat
index 1b1e108..35dd7d3 100644
--- a/fix_and_start.bat
+++ b/scripts/fix_and_start.bat
@@ -8,7 +8,7 @@ echo QQuiz - Automatic Fix and Start
echo ========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
REM Check if .env exists
if not exist ".env" (
@@ -50,28 +50,28 @@ if %errorlevel% equ 1 (
exit /b 1
)
- echo Starting PostgreSQL in Docker...
- docker-compose up -d postgres
+ echo Starting MySQL in Docker...
+ docker-compose up -d mysql
if %errorlevel% neq 0 (
echo.
echo Docker failed to start. Trying to fix...
docker-compose down
- docker-compose up -d postgres
+ docker-compose up -d mysql
)
echo Waiting for database...
- timeout /t 8 /nobreak >nul
+ timeout /t 10 /nobreak >nul
) else (
echo.
- echo Using Local PostgreSQL...
+ echo Using Local MySQL...
echo.
- echo Make sure PostgreSQL is running on port 5432
+ echo Make sure MySQL is running on port 3306
echo.
echo If you see connection errors, you need to:
- echo 1. Start PostgreSQL service
- echo 2. Or install PostgreSQL from https://www.postgresql.org/download/
+ echo 1. Start MySQL service
+ echo 2. Or install MySQL from https://dev.mysql.com/downloads/installer/
echo 3. Or choose option 1 to use Docker instead
echo.
pause
@@ -111,11 +111,11 @@ echo.
echo Starting services...
echo.
-start "QQuiz Backend" cmd /k "cd /d %~dp0backend && call venv\Scripts\activate.bat && echo Backend: http://localhost:8000 && echo Docs: http://localhost:8000/docs && echo. && uvicorn main:app --reload"
+start "QQuiz Backend" cmd /k "cd /d %~dp0..backend && call venv\Scripts\activate.bat && echo Backend: http://localhost:8000 && echo Docs: http://localhost:8000/docs && echo. && uvicorn main:app --reload"
timeout /t 8 /nobreak >nul
-start "QQuiz Frontend" cmd /k "cd /d %~dp0frontend && echo Frontend: http://localhost:3000 && echo. && npm start"
+start "QQuiz Frontend" cmd /k "cd /d %~dp0..frontend && echo Frontend: http://localhost:3000 && echo. && npm start"
echo.
echo ========================================
diff --git a/logs_windows.bat b/scripts/logs_windows.bat
similarity index 93%
rename from logs_windows.bat
rename to scripts/logs_windows.bat
index c6e967a..220f73f 100644
--- a/logs_windows.bat
+++ b/scripts/logs_windows.bat
@@ -2,6 +2,8 @@
chcp 65001 >nul
title QQuiz - 查看日志
+cd /d "%~dp0.."
+
echo.
echo ==========================================
echo QQuiz 服务日志
diff --git a/push_to_github.bat b/scripts/push_to_github.bat
similarity index 93%
rename from push_to_github.bat
rename to scripts/push_to_github.bat
index dd65c28..ee778e0 100644
--- a/push_to_github.bat
+++ b/scripts/push_to_github.bat
@@ -8,7 +8,7 @@ echo 推送 QQuiz 到 GitHub
echo ==========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
REM 检查是否有远程仓库
git remote -v | findstr "origin" >nul
@@ -48,7 +48,7 @@ echo [重要] 如果是首次推送,需要输入 GitHub 认证:
echo Username: handsomezhuzhu
echo Password: 使用 Personal Access Token (不是密码!)
echo.
-echo 如何获取 Token: 参考 GITHUB_PUSH_GUIDE.md
+echo 如何获取 Token: 参考 docs/GITHUB_PUSH_GUIDE.md
echo.
pause
@@ -74,7 +74,7 @@ if %errorlevel% equ 0 (
echo 3. 网络连接问题
echo.
echo 解决方案:
- echo 1. 阅读 GITHUB_PUSH_GUIDE.md 配置认证
+ echo 1. 阅读 docs/GITHUB_PUSH_GUIDE.md 配置认证
echo 2. 确认 Personal Access Token 有效
echo 3. 检查网络连接
echo.
diff --git a/quick_config.bat b/scripts/quick_config.bat
similarity index 91%
rename from quick_config.bat
rename to scripts/quick_config.bat
index b6dae69..d5dcbbd 100644
--- a/quick_config.bat
+++ b/scripts/quick_config.bat
@@ -8,7 +8,7 @@ echo QQuiz 快速配置
echo ==========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
REM 创建 .env 文件
echo 正在创建配置文件...
@@ -51,6 +51,6 @@ pause >nul
notepad .env
echo.
-echo 配置完成!现在可以运行 auto_setup_and_run.bat 启动系统
+echo 配置完成!现在可以运行 scripts\auto_setup_and_run.bat 启动系统
echo.
pause
diff --git a/scripts/restart_backend.bat b/scripts/restart_backend.bat
new file mode 100644
index 0000000..70d9728
--- /dev/null
+++ b/scripts/restart_backend.bat
@@ -0,0 +1,31 @@
+@echo off
+title QQuiz - Restart Backend
+color 0B
+
+cd /d "%~dp0.."
+
+echo.
+echo ========================================
+echo Restarting Backend Container
+echo ========================================
+echo.
+
+echo Restarting backend...
+docker-compose restart backend
+
+echo.
+echo Waiting for backend to start...
+timeout /t 5 /nobreak >nul
+
+echo.
+echo ========================================
+echo Backend Restarted!
+echo ========================================
+echo.
+echo Backend URL: http://localhost:8000
+echo API Docs: http://localhost:8000/docs
+echo.
+echo View logs: scripts\view_backend_logs.bat
+echo.
+
+pause
diff --git a/scripts/restart_docker.bat b/scripts/restart_docker.bat
new file mode 100644
index 0000000..fd6cf40
--- /dev/null
+++ b/scripts/restart_docker.bat
@@ -0,0 +1,47 @@
+@echo off
+title QQuiz - Restart Docker Services
+color 0B
+
+cd /d "%~dp0.."
+
+echo.
+echo ========================================
+echo Restarting QQuiz Docker Services
+echo ========================================
+echo.
+
+echo [1/4] Stopping existing containers...
+docker-compose down
+echo OK
+echo.
+
+echo [2/4] Rebuilding backend image...
+docker-compose build backend --no-cache
+echo OK
+echo.
+
+echo [3/4] Starting all services...
+docker-compose up -d
+echo OK
+echo.
+
+echo [4/4] Waiting for services to be ready...
+timeout /t 15 /nobreak >nul
+echo.
+
+echo ========================================
+echo Services Restarted!
+echo ========================================
+echo.
+echo Checking service status...
+docker-compose ps
+echo.
+
+echo Frontend: http://localhost:3000
+echo Backend: http://localhost:8000
+echo Database: MySQL on port 3306
+echo.
+echo Login: admin / admin123
+echo.
+
+pause
diff --git a/run_backend_local.bat b/scripts/run_backend_local.bat
similarity index 98%
rename from run_backend_local.bat
rename to scripts/run_backend_local.bat
index 80d0bfa..22c604a 100644
--- a/run_backend_local.bat
+++ b/scripts/run_backend_local.bat
@@ -8,7 +8,7 @@ echo QQuiz Backend - 本地启动
echo ==========================================
echo.
-cd /d "%~dp0backend"
+cd /d "%~dp0..\backend"
REM 检查虚拟环境是否存在
if not exist "venv\Scripts\activate.bat" (
diff --git a/run_frontend_local.bat b/scripts/run_frontend_local.bat
similarity index 97%
rename from run_frontend_local.bat
rename to scripts/run_frontend_local.bat
index 0638c59..50cbb50 100644
--- a/run_frontend_local.bat
+++ b/scripts/run_frontend_local.bat
@@ -8,7 +8,7 @@ echo QQuiz Frontend - 本地启动
echo ==========================================
echo.
-cd /d "%~dp0frontend"
+cd /d "%~dp0..\frontend"
REM 检查 node_modules 是否存在
if not exist "node_modules" (
diff --git a/run_local.sh b/scripts/run_local.sh
similarity index 100%
rename from run_local.sh
rename to scripts/run_local.sh
diff --git a/setup.bat b/scripts/setup.bat
similarity index 88%
rename from setup.bat
rename to scripts/setup.bat
index d53f09e..fa537ac 100644
--- a/setup.bat
+++ b/scripts/setup.bat
@@ -1,11 +1,11 @@
@echo off
-cd /d "%~dp0"
+cd /d "%~dp0.."
echo Creating .env configuration file...
echo.
(
-echo DATABASE_URL=postgresql+asyncpg://qquiz:qquiz_password@localhost:5432/qquiz_db
+echo DATABASE_URL=mysql+aiomysql://qquiz:qquiz_password@localhost:3306/qquiz_db
echo SECRET_KEY=qquiz-secret-key-for-development-change-in-production-32chars
echo AI_PROVIDER=openai
echo OPENAI_API_KEY=sk-your-openai-api-key-here
diff --git a/setup_docker_mirror.bat b/scripts/setup_docker_mirror.bat
similarity index 100%
rename from setup_docker_mirror.bat
rename to scripts/setup_docker_mirror.bat
diff --git a/start_app.bat b/scripts/start_app.bat
similarity index 71%
rename from start_app.bat
rename to scripts/start_app.bat
index 3ec8646..2d53fff 100644
--- a/start_app.bat
+++ b/scripts/start_app.bat
@@ -8,7 +8,7 @@ echo QQuiz - Auto Deploy Script
echo ========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
REM Check if .env exists
if not exist ".env" (
@@ -40,17 +40,13 @@ if %errorlevel% neq 0 (
echo OK - Node.js installed
echo.
-echo [3/7] Creating PostgreSQL database...
+echo [3/7] Creating MySQL database...
echo.
-echo Please enter PostgreSQL admin password when prompted
-echo (Default password is usually: postgres)
+echo Please enter MySQL root password when prompted
+echo (Leave blank if no password set)
echo.
-set PGPASSWORD=postgres
-psql -U postgres -h localhost -c "DROP DATABASE IF EXISTS qquiz_db;" 2>nul
-psql -U postgres -h localhost -c "DROP USER IF EXISTS qquiz;" 2>nul
-psql -U postgres -h localhost -c "CREATE USER qquiz WITH PASSWORD 'qquiz_password';" 2>nul
-psql -U postgres -h localhost -c "CREATE DATABASE qquiz_db OWNER qquiz;" 2>nul
+mysql -u root -p -e "DROP DATABASE IF EXISTS qquiz_db; DROP USER IF EXISTS 'qquiz'@'localhost'; CREATE DATABASE qquiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'qquiz'@'localhost' IDENTIFIED BY 'qquiz_password'; GRANT ALL PRIVILEGES ON qquiz_db.* TO 'qquiz'@'localhost'; FLUSH PRIVILEGES;" 2>nul
echo Database setup complete
echo.
@@ -88,11 +84,11 @@ echo.
echo [7/7] Starting services...
echo.
-start "QQuiz Backend" cmd /k "cd /d %~dp0backend && call venv\Scripts\activate.bat && echo Backend API: http://localhost:8000 && echo API Docs: http://localhost:8000/docs && echo. && uvicorn main:app --reload"
+start "QQuiz Backend" cmd /k "cd /d %~dp0..\backend && call venv\Scripts\activate.bat && echo Backend API: http://localhost:8000 && echo API Docs: http://localhost:8000/docs && echo. && uvicorn main:app --reload"
timeout /t 5 /nobreak >nul
-start "QQuiz Frontend" cmd /k "cd /d %~dp0frontend && echo Frontend: http://localhost:3000 && echo. && npm start"
+start "QQuiz Frontend" cmd /k "cd /d %~dp0..\frontend && echo Frontend: http://localhost:3000 && echo. && npm start"
timeout /t 3 /nobreak >nul
diff --git a/start_backend_only.bat b/scripts/start_backend_only.bat
similarity index 97%
rename from start_backend_only.bat
rename to scripts/start_backend_only.bat
index cad6d81..37a2ad0 100644
--- a/start_backend_only.bat
+++ b/scripts/start_backend_only.bat
@@ -8,7 +8,7 @@ echo Starting QQuiz Backend Server
echo ========================================
echo.
-cd /d "%~dp0backend"
+cd /d "%~dp0..\backend"
if not exist "venv\Scripts\activate.bat" (
echo Creating virtual environment...
diff --git a/start_frontend_only.bat b/scripts/start_frontend_only.bat
similarity index 96%
rename from start_frontend_only.bat
rename to scripts/start_frontend_only.bat
index 7981deb..0b99f39 100644
--- a/start_frontend_only.bat
+++ b/scripts/start_frontend_only.bat
@@ -8,7 +8,7 @@ echo Starting QQuiz Frontend Server
echo ========================================
echo.
-cd /d "%~dp0frontend"
+cd /d "%~dp0..\frontend"
if not exist "node_modules" (
echo Installing dependencies (first time only)...
diff --git a/scripts/start_postgres.bat b/scripts/start_postgres.bat
new file mode 100644
index 0000000..03ec064
--- /dev/null
+++ b/scripts/start_postgres.bat
@@ -0,0 +1,44 @@
+@echo off
+title Start PostgreSQL Service
+color 0B
+
+echo.
+echo ========================================
+echo Starting PostgreSQL Service
+echo ========================================
+echo.
+
+echo Attempting to start PostgreSQL service...
+echo.
+
+net start postgresql-x64-18
+
+if %errorlevel% equ 0 (
+ echo.
+ echo ========================================
+ echo SUCCESS!
+ echo ========================================
+ echo.
+ echo PostgreSQL service is now running
+ echo Port: 5432
+ echo.
+ echo You can now run: scripts\fix_and_start.bat
+ echo.
+) else (
+ echo.
+ echo ========================================
+ echo Failed to start service
+ echo ========================================
+ echo.
+ echo Please try starting manually:
+ echo 1. Press Win+R
+ echo 2. Type: services.msc
+ echo 3. Find "postgresql-x64-18"
+ echo 4. Right-click and select "Start"
+ echo.
+ echo OR run this script as Administrator:
+ echo Right-click this bat file and select "Run as administrator"
+ echo.
+)
+
+pause
diff --git a/start_windows.bat b/scripts/start_windows.bat
similarity index 99%
rename from start_windows.bat
rename to scripts/start_windows.bat
index 589e7fb..79991c8 100644
--- a/start_windows.bat
+++ b/scripts/start_windows.bat
@@ -2,6 +2,8 @@
chcp 65001 >nul
title QQuiz - 启动服务
+cd /d "%~dp0.."
+
echo.
echo ==========================================
echo QQuiz - 智能刷题与题库管理平台
diff --git a/start_windows_china.bat b/scripts/start_windows_china.bat
similarity index 94%
rename from start_windows_china.bat
rename to scripts/start_windows_china.bat
index d9a412e..51b7902 100644
--- a/start_windows_china.bat
+++ b/scripts/start_windows_china.bat
@@ -2,6 +2,8 @@
chcp 65001 >nul
title QQuiz - 启动服务 (国内优化版)
+cd /d "%~dp0.."
+
echo.
echo ==========================================
echo QQuiz - 智能刷题与题库管理平台
@@ -56,7 +58,7 @@ if %errorlevel% neq 0 (
if %errorlevel% equ 1 (
echo.
echo 请按照提示配置 Docker Desktop 镜像加速器...
- call setup_docker_mirror.bat
+ call scripts\setup_docker_mirror.bat
echo.
echo 配置完成后,请重新运行此脚本
pause
@@ -90,9 +92,9 @@ if %errorlevel% neq 0 (
echo 3. 配置错误 - 检查 .env 文件
echo.
echo 解决方案:
- echo 1. 配置镜像加速器: 运行 setup_docker_mirror.bat
+ echo 1. 配置镜像加速器: 运行 scripts\setup_docker_mirror.bat
echo 2. 查看详细错误: docker-compose logs
- echo 3. 阅读文档: DOCKER_MIRROR_SETUP.md
+ echo 3. 阅读文档: docs\DOCKER_MIRROR_SETUP.md
echo.
pause
exit /b 1
diff --git a/start_with_docker_db.bat b/scripts/start_with_docker_db.bat
similarity index 62%
rename from start_with_docker_db.bat
rename to scripts/start_with_docker_db.bat
index aacec59..47d2a94 100644
--- a/start_with_docker_db.bat
+++ b/scripts/start_with_docker_db.bat
@@ -8,7 +8,7 @@ echo QQuiz - Starting with Docker DB
echo ========================================
echo.
-cd /d "%~dp0"
+cd /d "%~dp0.."
echo [1/4] Checking Docker...
docker --version >nul 2>&1
@@ -21,31 +21,32 @@ if %errorlevel% neq 0 (
echo OK - Docker installed
echo.
-echo [2/4] Starting PostgreSQL in Docker...
-docker-compose up -d postgres
+echo [2/4] Starting MySQL in Docker...
+docker-compose up -d mysql
if %errorlevel% neq 0 (
- echo ERROR: Failed to start PostgreSQL
+ echo ERROR: Failed to start MySQL
echo Try: docker-compose down
echo Then run this script again
pause
exit /b 1
)
-echo OK - PostgreSQL started
+echo OK - MySQL started
echo Waiting for database to be ready...
-timeout /t 5 /nobreak >nul
+echo.
+timeout /t 10 /nobreak >nul
echo.
echo [3/4] Starting Backend...
-start "QQuiz Backend" cmd /k "cd /d %~dp0backend && call venv\Scripts\activate.bat && echo ======================================== && echo QQuiz Backend Server && echo ======================================== && echo. && echo API: http://localhost:8000 && echo Docs: http://localhost:8000/docs && echo. && alembic upgrade head && echo. && uvicorn main:app --reload"
+start "QQuiz Backend" cmd /k "cd /d %~dp0..backend && call venv\Scripts\activate.bat && echo ======================================== && echo QQuiz Backend Server && echo ======================================== && echo. && echo API: http://localhost:8000 && echo Docs: http://localhost:8000/docs && echo. && alembic upgrade head && echo. && uvicorn main:app --reload"
echo Waiting for backend to start...
timeout /t 8 /nobreak >nul
echo.
echo [4/4] Starting Frontend...
-start "QQuiz Frontend" cmd /k "cd /d %~dp0frontend && echo ======================================== && echo QQuiz Frontend Server && echo ======================================== && echo. && echo URL: http://localhost:3000 && echo. && npm start"
+start "QQuiz Frontend" cmd /k "cd /d %~dp0..frontend && echo ======================================== && echo QQuiz Frontend Server && echo ======================================== && echo. && echo URL: http://localhost:3000 && echo. && npm start"
echo.
echo ========================================
diff --git a/stop_windows.bat b/scripts/stop_windows.bat
similarity index 95%
rename from stop_windows.bat
rename to scripts/stop_windows.bat
index 6690385..7358a2e 100644
--- a/stop_windows.bat
+++ b/scripts/stop_windows.bat
@@ -2,6 +2,8 @@
chcp 65001 >nul
title QQuiz - 停止服务
+cd /d "%~dp0.."
+
echo.
echo ==========================================
echo 停止 QQuiz 服务
diff --git a/scripts/view_backend_logs.bat b/scripts/view_backend_logs.bat
new file mode 100644
index 0000000..72c5979
--- /dev/null
+++ b/scripts/view_backend_logs.bat
@@ -0,0 +1,15 @@
+@echo off
+title QQuiz - Backend Logs
+color 0B
+
+cd /d "%~dp0.."
+
+echo.
+echo ========================================
+echo QQuiz Backend Logs (Real-time)
+echo ========================================
+echo.
+echo Press Ctrl+C to stop viewing logs
+echo.
+
+docker-compose logs -f --tail=50 backend