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

570
tests/test_ai_functions.py Normal file
View File

@@ -0,0 +1,570 @@
#!/usr/bin/env python3
"""
考培练系统 AI 功能完整测试脚本
测试所有 AI 功能是否正常:
1. AI 基础服务连通性4sapi + OpenRouter
2. 试题生成功能V2 引擎)
3. 答案判断功能V2 引擎)
4. 知识点分析功能V2 引擎)
5. 课程对话功能V2 引擎)
6. 陪练场景提取功能V2 引擎)
7. 能力分析功能V2 引擎)
测试方式:通过 HTTP API 调用后端服务
"""
import asyncio
import httpx
import json
import sys
import time
from datetime import datetime
from typing import Optional, Dict, Any, List
# ==================== 配置 ====================
# 测试基础URL演示版后端
BASE_URL = "http://localhost:8000/api/v1"
# 测试账户(从数据库获取有效用户)
TEST_USERNAME = "admin"
TEST_PASSWORD = "admin123"
# 测试超时时间(秒)
TIMEOUT = 120
# 测试结果统计
test_results: List[Dict[str, Any]] = []
# ==================== 工具函数 ====================
def print_header(title: str):
"""打印标题"""
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60)
def print_result(test_name: str, success: bool, message: str, duration_ms: int = 0):
"""打印测试结果"""
status = "✅ 通过" if success else "❌ 失败"
duration_str = f" ({duration_ms}ms)" if duration_ms > 0 else ""
print(f" {status} | {test_name}{duration_str}")
if not success:
print(f" └─ {message}")
# 记录结果
test_results.append({
"name": test_name,
"success": success,
"message": message,
"duration_ms": duration_ms,
"timestamp": datetime.now().isoformat()
})
async def get_auth_token(client: httpx.AsyncClient) -> Optional[str]:
"""获取认证 token"""
try:
response = await client.post(
f"{BASE_URL}/auth/login",
data={"username": TEST_USERNAME, "password": TEST_PASSWORD}
)
if response.status_code == 200:
data = response.json()
return data.get("data", {}).get("access_token")
else:
print(f" ⚠️ 登录失败: {response.status_code} - {response.text}")
return None
except Exception as e:
print(f" ⚠️ 登录异常: {e}")
return None
# ==================== 测试用例 ====================
async def test_1_ai_basic_connectivity():
"""测试1: AI 基础服务连通性"""
print_header("测试1: AI 基础服务连通性")
# 直接测试 4sapi
async with httpx.AsyncClient(timeout=30) as client:
# 4sapi 测试
start = time.time()
try:
response = await client.post(
"https://4sapi.com/v1/chat/completions",
headers={
"Authorization": "Bearer sk-9yMCXjRGANbacz20kJY8doSNy6Rf446aYwmgGIuIXQ7DAyBw",
"Content-Type": "application/json"
},
json={
"model": "gemini-3-flash-preview",
"messages": [{"role": "user", "content": "你好,请回复'测试成功'"}],
"max_tokens": 50
}
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
print_result("4sapi.com 连通性", True, f"响应: {content[:50]}...", duration)
else:
print_result("4sapi.com 连通性", False, f"HTTP {response.status_code}: {response.text[:100]}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("4sapi.com 连通性", False, str(e), duration)
# OpenRouter 测试
start = time.time()
try:
response = await client.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": "Bearer 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",
"Content-Type": "application/json"
},
json={
"model": "google/gemini-3-flash-preview",
"messages": [{"role": "user", "content": "你好,请回复'测试成功'"}],
"max_tokens": 50
}
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
print_result("OpenRouter 连通性", True, f"响应: {content[:50]}...", duration)
else:
print_result("OpenRouter 连通性", False, f"HTTP {response.status_code}: {response.text[:100]}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("OpenRouter 连通性", False, str(e), duration)
async def test_2_exam_generation():
"""测试2: 试题生成功能"""
print_header("测试2: 试题生成功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("试题生成", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 先查询一个有知识点的课程
try:
response = await client.get(f"{BASE_URL}/courses", headers=headers)
if response.status_code != 200:
print_result("试题生成 - 查询课程", False, f"HTTP {response.status_code}")
return
courses = response.json().get("data", {}).get("items", [])
if not courses:
print_result("试题生成", False, "没有可用的课程")
return
course_id = courses[0].get("id")
print(f" 📝 使用课程ID: {course_id}")
except Exception as e:
print_result("试题生成 - 查询课程", False, str(e))
return
# 测试试题生成 API
start = time.time()
try:
response = await client.post(
f"{BASE_URL}/exams/generate?engine=v2",
headers=headers,
json={
"course_id": course_id,
"single_choice_count": 2,
"multiple_choice_count": 1,
"true_false_count": 1,
"fill_blank_count": 0,
"essay_count": 0,
"difficulty_level": 3,
"current_round": 1
}
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
result = data.get("data", {}).get("result", "")
try:
questions = json.loads(result) if result else []
print_result("试题生成 (V2)", True, f"生成了 {len(questions)} 道题目", duration)
except:
print_result("试题生成 (V2)", True, f"返回结果: {result[:100]}...", duration)
else:
print_result("试题生成 (V2)", False, f"HTTP {response.status_code}: {response.text[:200]}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("试题生成 (V2)", False, str(e), duration)
async def test_3_answer_judge():
"""测试3: 答案判断功能"""
print_header("测试3: 答案判断功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("答案判断", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 测试正确答案
start = time.time()
try:
response = await client.post(
f"{BASE_URL}/exams/judge-answer?engine=v2",
headers=headers,
json={
"question": "玻尿酸的主要作用是什么?",
"correct_answer": "保湿、填充、塑形",
"user_answer": "保湿和填充",
"analysis": "玻尿酸具有强大的保水能力,可用于皮肤保湿和面部填充"
}
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
is_correct = data.get("data", {}).get("is_correct")
print_result("答案判断 (V2)", True, f"判断结果: {'正确' if is_correct else '错误'}", duration)
else:
print_result("答案判断 (V2)", False, f"HTTP {response.status_code}: {response.text[:200]}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("答案判断 (V2)", False, str(e), duration)
async def test_4_knowledge_analysis():
"""测试4: 知识点分析功能"""
print_header("测试4: 知识点分析功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("知识点分析", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 先查询课程和资料
try:
response = await client.get(f"{BASE_URL}/courses", headers=headers)
if response.status_code != 200:
print_result("知识点分析 - 查询课程", False, f"HTTP {response.status_code}")
return
courses = response.json().get("data", {}).get("items", [])
if not courses:
print_result("知识点分析", False, "没有可用的课程")
return
course_id = courses[0].get("id")
# 查询课程资料
response = await client.get(f"{BASE_URL}/courses/{course_id}/materials", headers=headers)
if response.status_code != 200:
print_result("知识点分析 - 查询资料", False, f"HTTP {response.status_code}")
return
materials = response.json().get("data", {}).get("items", [])
if not materials:
print_result("知识点分析", False, "课程没有可用的资料")
return
material_id = materials[0].get("id")
print(f" 📝 使用课程ID: {course_id}, 资料ID: {material_id}")
except Exception as e:
print_result("知识点分析 - 查询资料", False, str(e))
return
# 测试知识点分析 API
start = time.time()
try:
response = await client.post(
f"{BASE_URL}/courses/{course_id}/materials/{material_id}/analyze?engine=v2",
headers=headers
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
kps = data.get("data", {}).get("knowledge_points", [])
print_result("知识点分析 (V2)", True, f"提取了 {len(kps)} 个知识点", duration)
else:
# 可能返回 400 因为没有文件内容
error_detail = response.json().get("detail", response.text[:200])
print_result("知识点分析 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("知识点分析 (V2)", False, str(e), duration)
async def test_5_course_chat():
"""测试5: 课程对话功能"""
print_header("测试5: 课程对话功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("课程对话", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 先查询课程
try:
response = await client.get(f"{BASE_URL}/courses", headers=headers)
if response.status_code != 200:
print_result("课程对话 - 查询课程", False, f"HTTP {response.status_code}")
return
courses = response.json().get("data", {}).get("items", [])
if not courses:
print_result("课程对话", False, "没有可用的课程")
return
course_id = courses[0].get("id")
print(f" 📝 使用课程ID: {course_id}")
except Exception as e:
print_result("课程对话 - 查询课程", False, str(e))
return
# 测试课程对话 API流式响应
start = time.time()
try:
# 注意:课程对话是流式响应,需要特殊处理
async with client.stream(
"POST",
f"{BASE_URL}/course/chat?engine=v2",
headers=headers,
json={
"course_id": course_id,
"query": "请简单介绍一下这门课程的主要内容"
}
) as response:
if response.status_code == 200:
content = ""
async for line in response.aiter_lines():
if line.startswith("data:"):
try:
data = json.loads(line[5:].strip())
if data.get("event") == "message_chunk":
content += data.get("content", "")
except:
pass
duration = int((time.time() - start) * 1000)
if content:
print_result("课程对话 (V2)", True, f"响应: {content[:100]}...", duration)
else:
print_result("课程对话 (V2)", True, "收到流式响应", duration)
else:
duration = int((time.time() - start) * 1000)
body = await response.aread()
print_result("课程对话 (V2)", False, f"HTTP {response.status_code}: {body.decode()[:200]}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("课程对话 (V2)", False, str(e), duration)
async def test_6_practice_scene():
"""测试6: 陪练场景提取功能"""
print_header("测试6: 陪练场景提取功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("陪练场景提取", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 先查询课程
try:
response = await client.get(f"{BASE_URL}/courses", headers=headers)
if response.status_code != 200:
print_result("陪练场景提取 - 查询课程", False, f"HTTP {response.status_code}")
return
courses = response.json().get("data", {}).get("items", [])
if not courses:
print_result("陪练场景提取", False, "没有可用的课程")
return
course_id = courses[0].get("id")
print(f" 📝 使用课程ID: {course_id}")
except Exception as e:
print_result("陪练场景提取 - 查询课程", False, str(e))
return
# 测试陪练场景提取 API
start = time.time()
try:
response = await client.post(
f"{BASE_URL}/practice/extract-scene?engine=v2",
headers=headers,
json={
"course_id": course_id,
"scene_type": "consultation" # 咨询场景
}
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
scenes = data.get("data", {}).get("scenes", [])
print_result("陪练场景提取 (V2)", True, f"提取了 {len(scenes)} 个场景", duration)
else:
error_detail = response.json().get("detail", response.text[:200])
print_result("陪练场景提取 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("陪练场景提取 (V2)", False, str(e), duration)
async def test_7_ability_analysis():
"""测试7: 能力分析功能"""
print_header("测试7: 能力分析功能 (V2 引擎)")
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
# 获取 token
token = await get_auth_token(client)
if not token:
print_result("能力分析", False, "无法获取认证 token")
return
headers = {"Authorization": f"Bearer {token}"}
# 测试能力分析 API需要员工的言迹数据
start = time.time()
try:
# 先查询是否有员工数据
response = await client.get(f"{BASE_URL}/users/me", headers=headers)
if response.status_code != 200:
print_result("能力分析 - 获取用户信息", False, f"HTTP {response.status_code}")
return
user = response.json().get("data", {})
user_id = user.get("id")
print(f" 📝 当前用户ID: {user_id}")
# 测试能力分析 API
response = await client.get(
f"{BASE_URL}/ability/user/{user_id}/analysis?engine=v2",
headers=headers
)
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
data = response.json()
dimensions = data.get("data", {}).get("dimensions", [])
print_result("能力分析 (V2)", True, f"分析了 {len(dimensions)} 个能力维度", duration)
elif response.status_code == 404:
# 可能没有言迹数据
print_result("能力分析 (V2)", True, "接口正常(无言迹数据)", duration)
else:
error_detail = response.json().get("detail", response.text[:200])
print_result("能力分析 (V2)", False, f"HTTP {response.status_code}: {error_detail}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result("能力分析 (V2)", False, str(e), duration)
async def test_backend_health():
"""测试后端健康状态"""
print_header("后端服务健康检查")
backends = [
("演示版 (aiedu)", "http://localhost:8000"),
("瑞小美 (kpl)", "http://localhost:8001"),
("华尔倍丽 (hua)", "http://localhost:8010"),
("杨扬宠物 (yy)", "http://localhost:8011"),
("武汉禾丽 (hl)", "http://localhost:8012"),
("芯颜定制 (xy)", "http://localhost:8013"),
("飞沃 (fw)", "http://localhost:8014"),
("恩喜成都 (ex)", "http://localhost:8015"),
("陪练试用版 (pl)", "http://localhost:8020"),
]
async with httpx.AsyncClient(timeout=10) as client:
for name, url in backends:
start = time.time()
try:
response = await client.get(f"{url}/health")
duration = int((time.time() - start) * 1000)
if response.status_code == 200:
print_result(name, True, "健康", duration)
else:
print_result(name, False, f"HTTP {response.status_code}", duration)
except Exception as e:
duration = int((time.time() - start) * 1000)
print_result(name, False, str(e), duration)
def print_summary():
"""打印测试汇总"""
print_header("测试汇总")
total = len(test_results)
passed = sum(1 for r in test_results if r["success"])
failed = total - passed
print(f"\n 总计: {total} 项测试")
print(f" ✅ 通过: {passed}")
print(f" ❌ 失败: {failed}")
if failed > 0:
print("\n 失败项目:")
for r in test_results:
if not r["success"]:
print(f" - {r['name']}: {r['message']}")
# 计算平均延迟
durations = [r["duration_ms"] for r in test_results if r["duration_ms"] > 0]
if durations:
avg_duration = sum(durations) / len(durations)
print(f"\n 平均响应时间: {avg_duration:.0f}ms")
print("\n" + "=" * 60)
return failed == 0
async def main():
"""主测试入口"""
print("\n" + "=" * 60)
print(" 考培练系统 AI 功能完整测试")
print(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
# 检查后端健康状态
await test_backend_health()
# 运行所有测试
await test_1_ai_basic_connectivity()
await test_2_exam_generation()
await test_3_answer_judge()
await test_4_knowledge_analysis()
await test_5_course_chat()
await test_6_practice_scene()
await test_7_ability_analysis()
# 打印汇总
success = print_summary()
sys.exit(0 if success else 1)
if __name__ == "__main__":
asyncio.run(main())

165
tests/test_course_chat.py Normal file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""
测试与课程对话功能 - Dify 集成
"""
import asyncio
import httpx
import json
# 测试配置
API_BASE_URL = "http://localhost:8000"
LOGIN_ENDPOINT = f"{API_BASE_URL}/api/v1/auth/login"
CHAT_ENDPOINT = f"{API_BASE_URL}/api/v1/course/chat"
# 测试账号
TEST_USER = {
"username": "test_user",
"password": "123456"
}
async def login() -> str:
"""登录获取 access_token"""
print("🔑 正在登录...")
async with httpx.AsyncClient() as client:
response = await client.post(
LOGIN_ENDPOINT,
data={
"username": TEST_USER["username"],
"password": TEST_USER["password"]
}
)
if response.status_code == 200:
data = response.json()
token = data.get("access_token")
print(f"✅ 登录成功token: {token[:20]}...")
return token
else:
print(f"❌ 登录失败: {response.status_code} - {response.text}")
raise Exception("登录失败")
async def test_course_chat(token: str, course_id: int, query: str, conversation_id: str = None):
"""测试课程对话"""
print(f"\n💬 测试与课程 {course_id} 对话")
print(f"问题: {query}")
if conversation_id:
print(f"会话ID: {conversation_id}")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
payload = {
"course_id": course_id,
"query": query
}
if conversation_id:
payload["conversation_id"] = conversation_id
new_conversation_id = None
answer = ""
async with httpx.AsyncClient(timeout=180.0) as client:
async with client.stream("POST", CHAT_ENDPOINT, headers=headers, json=payload) as response:
if response.status_code != 200:
error_text = await response.aread()
print(f"❌ API 调用失败: {response.status_code} - {error_text}")
return None, None
print("\n📡 SSE 事件流:")
print("-" * 60)
async for line in response.aiter_lines():
if not line or not line.strip():
continue
if line.startswith("data: "):
data_str = line[6:]
try:
event_data = json.loads(data_str)
event_type = event_data.get("event")
if event_type == "conversation_started":
new_conversation_id = event_data.get("conversation_id")
print(f"🆕 会话已创建: {new_conversation_id}")
elif event_type == "message_content":
answer = event_data.get("answer", "")
print(f"\n💡 AI 回答:\n{answer}")
elif event_type == "message_end":
print("\n✅ 消息接收完成")
elif event_type == "error":
error_msg = event_data.get("message", "未知错误")
print(f"\n❌ 错误: {error_msg}")
except json.JSONDecodeError as e:
print(f"⚠️ 解析失败: {e} - {data_str[:100]}")
print("-" * 60)
return new_conversation_id, answer
async def main():
"""主测试流程"""
print("=" * 60)
print("🧪 与课程对话功能测试 - Dify 集成")
print("=" * 60)
try:
# 1. 登录
token = await login()
# 2. 首次对话
print("\n" + "=" * 60)
print("测试场景 1: 首次对话(创建新会话)")
print("=" * 60)
conversation_id, answer1 = await test_course_chat(
token=token,
course_id=1,
query="这门课程讲什么?"
)
if not conversation_id:
print("\n❌ 测试失败:未获取到 conversation_id")
return
# 3. 续接对话
print("\n" + "=" * 60)
print("测试场景 2: 续接对话(使用已有会话)")
print("=" * 60)
_, answer2 = await test_course_chat(
token=token,
course_id=1,
query="能详细说说吗?",
conversation_id=conversation_id
)
# 4. 测试总结
print("\n" + "=" * 60)
print("📊 测试总结")
print("=" * 60)
print(f"✅ 首次对话: {'成功' if answer1 else '失败'}")
print(f"✅ 续接对话: {'成功' if answer2 else '失败'}")
print(f"✅ 会话管理: conversation_id = {conversation_id}")
print("\n🎉 所有测试通过!")
except Exception as e:
print(f"\n❌ 测试失败: {str(e)}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
测试cozepy 0.2.0的conversation续接功能
"""
from cozepy import Coze, TokenAuth, COZE_CN_BASE_URL, Message, ChatEventType
# 配置
TOKEN = "pat_Sa5OiuUl0gDflnKstQTToIz0sSMshBV06diX0owOeuI1ZK1xDLH5YZH9fSeuKLIi"
BOT_ID = "7560643598174683145"
def test_conversation_continuation():
"""测试对话续接"""
print("=" * 60)
print("测试cozepy 0.2.0对话续接功能")
print("=" * 60)
# 初始化客户端
coze = Coze(
auth=TokenAuth(token=TOKEN),
base_url=COZE_CN_BASE_URL
)
# 第1步创建conversation
print("\n1. 创建conversation...")
conversation = coze.conversations.create()
conversation_id = conversation.id
print(f"✅ Conversation已创建: {conversation_id}")
# 第2步发送第一条消息
print("\n2. 发送第一条消息...")
user_id = "test_user_001"
message1 = "我的名字是张三,请记住我的名字"
stream1 = coze.chat.stream(
bot_id=BOT_ID,
user_id=user_id,
additional_messages=[Message.build_user_question_text(message1)],
conversation_id=conversation_id
)
print(f"用户消息: {message1}")
print("AI回复: ", end="", flush=True)
ai_reply1 = ""
for event in stream1:
if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:
ai_reply1 += event.message.content
print(event.message.content, end="", flush=True)
elif event.event == ChatEventType.CONVERSATION_CHAT_COMPLETED:
break
print(f"\n✅ 第一轮对话完成")
# 第3步发送第二条消息测试是否记住
print("\n3. 发送第二条消息(测试是否记住名字)...")
message2 = "我叫什么名字?"
stream2 = coze.chat.stream(
bot_id=BOT_ID,
user_id=user_id,
additional_messages=[Message.build_user_question_text(message2)],
conversation_id=conversation_id # 使用同一个conversation_id
)
print(f"用户消息: {message2}")
print("AI回复: ", end="", flush=True)
ai_reply2 = ""
for event in stream2:
if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:
ai_reply2 += event.message.content
print(event.message.content, end="", flush=True)
elif event.event == ChatEventType.CONVERSATION_CHAT_COMPLETED:
break
print(f"\n✅ 第二轮对话完成")
# 分析结果
print("\n" + "=" * 60)
print("测试结果分析")
print("=" * 60)
print(f"conversation_id: {conversation_id}")
print(f"\n第一轮AI回复: {ai_reply1[:100]}...")
print(f"\n第二轮AI回复: {ai_reply2}")
# 检查AI是否记住了名字
if "张三" in ai_reply2:
print("\n✅ 成功AI记住了名字对话续接正常工作")
else:
print("\n❌ 失败AI没有记住名字对话续接可能不工作")
print(" 这说明cozepy 0.2.0的conversation_id参数可能不起作用")
if __name__ == "__main__":
test_conversation_continuation()

199
tests/test_exam_api.py Normal file
View File

@@ -0,0 +1,199 @@
#!/usr/bin/env python3
"""
测试考试API接口
"""
import requests
import json
import time
# API配置
BASE_URL = "http://localhost:8000/api/v1"
# 使用有效的token需要先登录获取
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImV4cCI6MTc0MTQxOTQ5Mn0.0ZoiGJaIpV0WvVdH-M2tq03RMfRd9OKUJvDIgJtJoXk"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"
}
def test_generate_exam():
"""测试生成考试试题"""
print("\n" + "="*50)
print("测试1: 生成考试试题")
print("="*50)
url = f"{BASE_URL}/exams/generate"
data = {
"course_id": 1,
"single_choice_count": 2,
"multiple_choice_count": 1,
"true_false_count": 1,
"fill_blank_count": 1,
"essay_count": 0,
"difficulty_level": 3,
"mistake_records": "[]"
}
print(f"请求URL: {url}")
print(f"请求数据: {json.dumps(data, indent=2, ensure_ascii=False)}")
print("\n正在调用Dify工作流生成试题请稍候...")
try:
response = requests.post(url, json=data, headers=headers, timeout=300)
print(f"\n响应状态码: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"响应数据: {json.dumps(result, indent=2, ensure_ascii=False)[:500]}...")
# 解析result字段中的试题
if "data" in result and "result" in result["data"]:
questions_str = result["data"]["result"]
questions = json.loads(questions_str)
print(f"\n✅ 成功生成 {len(questions)} 道试题")
print(f"第一题预览: {json.dumps(questions[0], indent=2, ensure_ascii=False)[:300]}...")
return questions
else:
print("❌ 响应格式不正确")
else:
print(f"❌ 请求失败: {response.text}")
except requests.exceptions.Timeout:
print("❌ 请求超时可能Dify工作流执行时间较长")
except Exception as e:
print(f"❌ 请求异常: {str(e)}")
return None
def test_judge_answer():
"""测试判断主观题答案"""
print("\n" + "="*50)
print("测试2: 判断主观题答案")
print("="*50)
url = f"{BASE_URL}/exams/judge-answer"
data = {
"question": "肉毒素注射后____小时内不能平躺",
"correct_answer": "4",
"user_answer": "4"
}
print(f"请求URL: {url}")
print(f"请求数据: {json.dumps(data, indent=2, ensure_ascii=False)}")
print("\n正在调用Dify答案判断工作流...")
try:
response = requests.post(url, json=data, headers=headers, timeout=60)
print(f"\n响应状态码: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"响应数据: {json.dumps(result, indent=2, ensure_ascii=False)}")
if "data" in result:
is_correct = result["data"].get("is_correct")
print(f"\n✅ 判断结果: {'正确' if is_correct else '错误'}")
return result
else:
print(f"❌ 请求失败: {response.text}")
except Exception as e:
print(f"❌ 请求异常: {str(e)}")
return None
def test_record_mistake():
"""测试记录错题"""
print("\n" + "="*50)
print("测试3: 记录错题")
print("="*50)
url = f"{BASE_URL}/exams/record-mistake"
data = {
"exam_id": 1,
"question_id": None,
"knowledge_point_id": 199,
"question_content": "以下哪一项最能准确描述艾维岚Avilan在医美注射材料中的独特作用机制",
"correct_answer": "B它是唯一可以做减法的注射材料实现收紧而不增容。",
"user_answer": "A它是一款主要用于增容塑形的支撑型材料。"
}
print(f"请求URL: {url}")
print(f"请求数据: {json.dumps(data, indent=2, ensure_ascii=False)}")
try:
response = requests.post(url, json=data, headers=headers, timeout=30)
print(f"\n响应状态码: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"响应数据: {json.dumps(result, indent=2, ensure_ascii=False)}")
if "data" in result:
print(f"\n✅ 成功记录错题ID: {result['data'].get('id')}")
return result
else:
print(f"❌ 请求失败: {response.text}")
except Exception as e:
print(f"❌ 请求异常: {str(e)}")
return None
def test_get_mistakes():
"""测试获取错题记录"""
print("\n" + "="*50)
print("测试4: 获取错题记录")
print("="*50)
exam_id = 1
url = f"{BASE_URL}/exams/mistakes?exam_id={exam_id}"
print(f"请求URL: {url}")
try:
response = requests.get(url, headers=headers, timeout=30)
print(f"\n响应状态码: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"响应数据: {json.dumps(result, indent=2, ensure_ascii=False)[:500]}...")
if "data" in result and "mistakes" in result["data"]:
mistakes = result["data"]["mistakes"]
print(f"\n✅ 成功获取 {len(mistakes)} 条错题记录")
return result
else:
print(f"❌ 请求失败: {response.text}")
except Exception as e:
print(f"❌ 请求异常: {str(e)}")
return None
def main():
"""主测试流程"""
print("\n" + "="*60)
print("开始测试考试API接口")
print("="*60)
# 测试1: 生成考试试题调用Dify工作流
# 注意这个测试可能需要较长时间因为要调用Dify工作流
# questions = test_generate_exam()
# 测试2: 判断答案调用Dify工作流
test_judge_answer()
# 测试3: 记录错题
test_record_mistake()
# 测试4: 获取错题记录
test_get_mistakes()
print("\n" + "="*60)
print("测试完成")
print("="*60)
print("\n注意: 试题生成测试已注释因为需要较长时间调用Dify工作流")
print("如需测试,请取消注释 test_generate_exam() 行")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""
前端登录测试脚本
"""
import requests
import json
import time
def test_login_flow():
"""测试完整的登录流程"""
print("🔍 开始前端登录测试...")
# 1. 测试前端页面访问
try:
response = requests.get("http://localhost:3001/login")
if response.status_code == 200:
print("✅ 前端登录页面可访问")
else:
print(f"❌ 前端登录页面访问失败: {response.status_code}")
return False
except Exception as e:
print(f"❌ 前端页面访问失败: {e}")
return False
# 2. 测试后端登录API
try:
login_data = {"username": "testuser", "password": "123456"}
response = requests.post("http://localhost:8000/api/v1/auth/login", json=login_data)
if response.status_code == 200:
data = response.json()
print("✅ 后端登录API成功")
print(f" 用户: {data['data']['user']['username']}")
print(f" 角色: {data['data']['user']['role']}")
# 保存token用于后续测试
global access_token, refresh_token
access_token = data['data']['token']['access_token']
refresh_token = data['data']['token']['refresh_token']
return True
else:
print(f"❌ 后端登录API失败: {response.status_code}")
print(f" 响应: {response.text}")
return False
except Exception as e:
print(f"❌ 后端API调用失败: {e}")
return False
def test_token_refresh():
"""测试token刷新"""
global access_token, refresh_token
if not access_token or not refresh_token:
print("❌ 没有可用的token进行刷新测试")
return False
print("\n🔄 测试token刷新...")
try:
refresh_data = {"refresh_token": refresh_token}
response = requests.post("http://localhost:8000/api/v1/auth/refresh", json=refresh_data)
if response.status_code == 200:
data = response.json()
print("✅ Token刷新成功")
return True
else:
print(f"❌ Token刷新失败: {response.status_code}")
print(f" 响应: {response.text}")
return False
except Exception as e:
print(f"❌ Token刷新请求失败: {e}")
return False
def test_frontend_proxy():
"""测试前端代理配置"""
print("\n🌐 测试前端代理...")
try:
# 通过前端代理调用后端API
headers = {"Content-Type": "application/json"}
response = requests.post("http://localhost:3001/api/v1/auth/login",
json={"username": "testuser", "password": "123456"},
headers=headers)
if response.status_code == 200:
data = response.json()
print("✅ 前端代理API调用成功")
print(f" 响应格式正确: {type(data)}")
return True
else:
print(f"❌ 前端代理API调用失败: {response.status_code}")
print(f" 响应: {response.text}")
return False
except Exception as e:
print(f"❌ 前端代理API调用失败: {e}")
return False
def main():
"""主测试函数"""
print("🚀 前端登录完整测试")
print("=" * 50)
# 测试后端API
if not test_login_flow():
print("❌ 后端登录测试失败")
return False
# 测试token刷新
if not test_token_refresh():
print("❌ Token刷新测试失败")
return False
# 测试前端代理
if not test_frontend_proxy():
print("❌ 前端代理测试失败")
return False
print("\n" + "=" * 50)
print("🎉 所有测试通过!前端登录功能正常!")
return True
if __name__ == "__main__":
main()

134
tests/test_integration.py Normal file
View File

@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""
前后端集成测试脚本
"""
import requests
import json
import time
def test_backend_api():
"""测试后端API"""
base_url = "http://localhost:8000"
print("🧪 测试后端API...")
# 1. 健康检查
try:
response = requests.get(f"{base_url}/health")
print(f"✅ 健康检查: {response.json()}")
except Exception as e:
print(f"❌ 健康检查失败: {e}")
return False
# 2. 登录测试
login_data = {"username": "testuser", "password": "123456"}
try:
response = requests.post(f"{base_url}/api/v1/auth/login", json=login_data)
if response.status_code == 200:
data = response.json()
print(f"✅ 登录成功: {data['message']}")
print(f" 用户ID: {data['data']['user']['id']}")
print(f" 用户名: {data['data']['user']['username']}")
print(f" 角色: {data['data']['user']['role']}")
# 保存token用于后续测试
global access_token, refresh_token
access_token = data['data']['token']['access_token']
refresh_token = data['data']['token']['refresh_token']
return True
else:
print(f"❌ 登录失败: {response.text}")
return False
except Exception as e:
print(f"❌ 登录请求失败: {e}")
return False
def test_frontend_proxy():
"""测试前端代理配置"""
base_url = "http://localhost:3001"
print("\n🌐 测试前端代理...")
# 1. 检查前端是否可访问
try:
response = requests.get(base_url)
if response.status_code == 200:
print("✅ 前端页面可访问")
else:
print(f"❌ 前端页面访问失败: {response.status_code}")
return False
except Exception as e:
print(f"❌ 前端页面访问失败: {e}")
return False
# 2. 测试前端代理到后端的API调用
try:
# 通过前端代理调用后端API
headers = {"Content-Type": "application/json"}
response = requests.post(f"{base_url}/api/v1/auth/login", json={"username": "testuser", "password": "123456"}, headers=headers)
if response.status_code == 200:
data = response.json()
print("✅ 前端代理API调用成功")
print(f" 响应数据: {data}")
return True
else:
print(f"❌ 前端代理API调用失败: {response.status_code}")
print(f" 响应: {response.text}")
return False
except Exception as e:
print(f"❌ 前端代理API调用失败: {e}")
return False
def test_token_refresh():
"""测试token刷新"""
global access_token, refresh_token
if not access_token or not refresh_token:
print("❌ 没有可用的token进行刷新测试")
return False
print("\n🔄 测试token刷新...")
try:
refresh_data = {"refresh_token": refresh_token}
response = requests.post("http://localhost:8000/api/v1/auth/refresh", json=refresh_data)
if response.status_code == 200:
data = response.json()
print("✅ Token刷新成功")
print(f" 新token: {data['data']['access_token'][:50]}...")
return True
else:
print(f"❌ Token刷新失败: {response.text}")
return False
except Exception as e:
print(f"❌ Token刷新请求失败: {e}")
return False
def main():
"""主测试函数"""
print("🚀 开始前后端集成测试")
print("=" * 50)
# 测试后端API
if not test_backend_api():
print("❌ 后端API测试失败终止测试")
return False
# 测试前端代理
if not test_frontend_proxy():
print("❌ 前端代理测试失败")
return False
# 测试token刷新
if not test_token_refresh():
print("❌ Token刷新测试失败")
return False
print("\n" + "=" * 50)
print("🎉 所有测试通过!前后端集成成功!")
return True
if __name__ == "__main__":
main()

177
tests/test_login.html Normal file
View File

@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 400px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
width: 100%;
padding: 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #0056b3;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.result {
margin-top: 20px;
padding: 10px;
border-radius: 4px;
display: none;
}
.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
pre {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
font-size: 12px;
}
</style>
</head>
<body>
<div class="container">
<h1>登录测试</h1>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" value="testuser" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" value="123456" placeholder="请输入密码">
</div>
<button id="loginBtn" onclick="login()">登录</button>
<div id="result" class="result"></div>
</div>
<script>
async function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const resultDiv = document.getElementById('result');
const loginBtn = document.getElementById('loginBtn');
// 显示加载状态
loginBtn.disabled = true;
loginBtn.textContent = '登录中...';
resultDiv.style.display = 'none';
try {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password
})
});
const data = await response.json();
if (response.ok && data.code === 200) {
// 登录成功
resultDiv.className = 'result success';
resultDiv.innerHTML = `
<h3>✅ 登录成功!</h3>
<p><strong>用户:</strong> ${data.data.user.username}</p>
<p><strong>角色:</strong> ${data.data.user.role}</p>
<p><strong>Token类型:</strong> ${data.data.token.token_type}</p>
<p><strong>Access Token:</strong> ${data.data.token.access_token.substring(0, 50)}...</p>
<p><strong>Refresh Token:</strong> ${data.data.token.refresh_token.substring(0, 50)}...</p>
`;
} else {
// 登录失败
resultDiv.className = 'result error';
resultDiv.innerHTML = `
<h3>❌ 登录失败</h3>
<p><strong>状态码:</strong> ${response.status}</p>
<p><strong>错误信息:</strong> ${data.message || data.detail || '未知错误'}</p>
<p><strong>完整响应:</strong></p>
<pre>${JSON.stringify(data, null, 2)}</pre>
`;
}
} catch (error) {
// 网络错误
resultDiv.className = 'result error';
resultDiv.innerHTML = `
<h3>❌ 网络错误</h3>
<p><strong>错误信息:</strong> ${error.message}</p>
<p>请检查:</p>
<ul>
<li>前端服务是否运行 (http://localhost:3001)</li>
<li>后端服务是否运行 (http://localhost:8000)</li>
<li>代理配置是否正确</li>
</ul>
`;
} finally {
// 恢复按钮状态
loginBtn.disabled = false;
loginBtn.textContent = '登录';
resultDiv.style.display = 'block';
}
}
// 页面加载时自动测试
window.addEventListener('load', function() {
console.log('页面加载完成');
});
</script>
</body>
</html>

113
tests/test_login_manual.py Normal file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
手动测试登录功能的脚本
"""
import requests
import json
from datetime import datetime
def test_login_page():
"""测试登录页面是否可访问"""
print("=" * 50)
print("登录功能测试报告")
print("=" * 50)
print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# 测试前端页面访问
frontend_url = "http://localhost:3001/login?redirect=/trainee/exam"
print(f"1. 测试前端页面访问: {frontend_url}")
try:
response = requests.get(frontend_url, timeout=5)
print(f" 状态码: {response.status_code}")
print(f" 响应头: {dict(response.headers)}")
if response.status_code == 200:
print(" ✅ 前端页面可以正常访问")
content_length = len(response.text)
print(f" 页面内容长度: {content_length} 字符")
# 检查是否包含登录相关元素
content = response.text.lower()
login_indicators = ['login', '登录', 'username', 'password', 'form']
found_indicators = [indicator for indicator in login_indicators if indicator in content]
if found_indicators:
print(f" 找到登录相关元素: {found_indicators}")
else:
print(" ⚠️ 未找到明显的登录相关元素")
else:
print(f" ❌ 前端页面访问失败,状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print(" ❌ 无法连接到前端服务 (localhost:3001)")
except requests.exceptions.Timeout:
print(" ❌ 前端服务响应超时")
except Exception as e:
print(f" ❌ 访问前端页面时出错: {e}")
print()
# 测试后端API
backend_base = "http://localhost:8000"
print(f"2. 测试后端API服务: {backend_base}")
try:
# 测试后端健康检查
health_url = f"{backend_base}/health"
response = requests.get(health_url, timeout=5)
print(f" 健康检查状态码: {response.status_code}")
# 测试登录API端点
login_api_url = f"{backend_base}/api/v1/auth/login"
response = requests.options(login_api_url, timeout=5)
print(f" 登录API OPTIONS请求状态码: {response.status_code}")
if response.status_code in [200, 405]: # 405表示方法不允许但端点存在
print(" ✅ 后端API服务正常运行")
else:
print(f" ⚠️ 后端API可能有问题状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print(" ❌ 无法连接到后端服务 (localhost:8000)")
except requests.exceptions.Timeout:
print(" ❌ 后端服务响应超时")
except Exception as e:
print(f" ❌ 访问后端API时出错: {e}")
print()
# 测试数据库连接通过后端API
print("3. 测试数据库连接状态")
try:
db_status_url = f"{backend_base}/api/v1/system/status"
response = requests.get(db_status_url, timeout=5)
if response.status_code == 200:
try:
data = response.json()
print(" ✅ 系统状态API响应正常")
print(f" 响应数据: {json.dumps(data, indent=2, ensure_ascii=False)}")
except json.JSONDecodeError:
print(" ⚠️ 系统状态API响应不是有效JSON")
else:
print(f" ⚠️ 系统状态API状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print(" ❌ 无法获取系统状态")
except Exception as e:
print(f" ❌ 获取系统状态时出错: {e}")
print()
print("=" * 50)
print("测试建议:")
print("1. 确保前后端服务都在运行")
print("2. 检查端口配置是否正确")
print("3. 验证数据库连接是否正常")
print("4. 使用浏览器手动访问登录页面进行测试")
print("=" * 50)
if __name__ == "__main__":
test_login_page()

350
tests/test_practice_api.py Normal file
View File

@@ -0,0 +1,350 @@
#!/usr/bin/env python3
"""
测试陪练功能API
"""
import requests
import json
import sys
BASE_URL = "http://localhost:8000"
USERNAME = "admin"
PASSWORD = "admin123"
def login():
"""登录获取token"""
print("=" * 60)
print("1. 登录获取token")
print("=" * 60)
response = requests.post(
f"{BASE_URL}/api/v1/auth/login",
json={"username": USERNAME, "password": PASSWORD}
)
if response.status_code != 200:
print(f"❌ 登录失败: {response.status_code}")
sys.exit(1)
data = response.json()
if data['code'] != 200:
print(f"❌ 登录失败: {data['message']}")
sys.exit(1)
token = data['data']['token']['access_token']
print(f"✅ 登录成功token: {token[:50]}...")
return token
def test_get_scenes(token):
"""测试获取场景列表"""
print("\n" + "=" * 60)
print("2. 测试获取场景列表")
print("=" * 60)
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{BASE_URL}/api/v1/practice/scenes", headers=headers)
if response.status_code != 200:
print(f"❌ 请求失败: {response.status_code}")
return False
data = response.json()
if data['code'] != 200:
print(f"❌ 获取失败: {data['message']}")
return False
scenes = data['data']['items']
total = data['data']['total']
print(f"✅ 成功获取 {total} 个场景")
print("\n场景列表:")
print("-" * 80)
for scene in scenes:
print(f" {scene['id']}. {scene['name']} - {scene['type']} - {scene['difficulty']}")
print("-" * 80)
return True
def test_get_scene_detail(token, scene_id=1):
"""测试获取场景详情"""
print("\n" + "=" * 60)
print(f"3. 测试获取场景详情 (ID={scene_id})")
print("=" * 60)
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{BASE_URL}/api/v1/practice/scenes/{scene_id}", headers=headers)
if response.status_code != 200:
print(f"❌ 请求失败: {response.status_code}")
return None
data = response.json()
if data['code'] != 200:
print(f"❌ 获取失败: {data['message']}")
return None
scene = data['data']
print(f"✅ 成功获取场景详情")
print(f"\n场景名称: {scene['name']}")
print(f"类型: {scene['type']}")
print(f"难度: {scene['difficulty']}")
print(f"时长: {scene['duration']}分钟")
print(f"\n场景背景:\n{scene['background']}")
print(f"\nAI角色:\n{scene['ai_role']}")
print(f"\n练习目标:")
for i, obj in enumerate(scene['objectives'], 1):
print(f" {i}. {obj}")
return scene
def test_sse_chat(token, scene):
"""测试SSE对话"""
print("\n" + "=" * 60)
print("4. 测试SSE流式对话")
print("=" * 60)
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# 首次消息
payload = {
"scene_id": scene['id'],
"scene_name": scene['name'],
"scene_description": scene['description'],
"scene_background": scene['background'],
"scene_ai_role": scene['ai_role'],
"scene_objectives": scene['objectives'],
"scene_keywords": scene['keywords'],
"user_message": "您好,我是某轻医美品牌的销售顾问,想占用您几分钟时间介绍一下我们的产品",
"is_first": True
}
print("\n发送首次消息(包含场景信息):")
print(f" 用户消息: {payload['user_message']}")
print(f" is_first: {payload['is_first']}")
print(f"\n等待AI回复流式...")
print("-" * 80)
try:
response = requests.post(
f"{BASE_URL}/api/v1/practice/start",
headers=headers,
json=payload,
stream=True,
timeout=180
)
if response.status_code != 200:
print(f"❌ SSE请求失败: {response.status_code}")
print(f" 响应: {response.text}")
return None
conversation_id = None
chat_id = None
ai_message = ""
# 处理SSE流
for line in response.iter_lines():
if not line:
continue
line = line.decode('utf-8')
# 解析SSE事件
if line.startswith('event: '):
event_type = line[7:].strip()
elif line.startswith('data: '):
data_str = line[6:].strip()
if data_str == '[DONE]':
print("\n\n✅ 对话流结束")
break
try:
event_data = json.loads(data_str)
if event_type == 'conversation.chat.created':
conversation_id = event_data.get('conversation_id')
chat_id = event_data.get('chat_id')
print(f"\n📝 对话已创建:")
print(f" conversation_id: {conversation_id}")
print(f" chat_id: {chat_id}")
print(f"\n🤖 AI回复: ", end="", flush=True)
elif event_type == 'message.delta':
content = event_data.get('content', '')
ai_message += content
print(content, end="", flush=True)
elif event_type == 'message.completed':
print()
elif event_type == 'conversation.completed':
token_count = event_data.get('token_count', 0)
print(f"\n\n📊 对话完成Token用量: {token_count}")
elif event_type == 'error':
error = event_data.get('error', '未知错误')
print(f"\n\n❌ 对话错误: {error}")
return None
except json.JSONDecodeError as e:
print(f"\n⚠️ JSON解析错误: {e}")
print("-" * 80)
print(f"\n✅ SSE对话测试成功")
print(f" conversation_id: {conversation_id}")
print(f" AI消息长度: {len(ai_message)} 字符")
return conversation_id
except Exception as e:
print(f"\n❌ SSE测试失败: {e}")
import traceback
traceback.print_exc()
return None
def test_follow_up_message(token, conversation_id):
"""测试后续消息(不包含场景信息)"""
print("\n" + "=" * 60)
print("5. 测试后续消息")
print("=" * 60)
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# 后续消息
payload = {
"user_message": "我们专注于轻医美行业,提供从设备到培训的一站式解决方案",
"conversation_id": conversation_id,
"is_first": False
}
print(f"\n发送后续消息(不包含场景信息):")
print(f" 用户消息: {payload['user_message']}")
print(f" is_first: {payload['is_first']}")
print(f" conversation_id: {conversation_id}")
print(f"\n等待AI回复...")
print("-" * 80)
try:
response = requests.post(
f"{BASE_URL}/api/v1/practice/start",
headers=headers,
json=payload,
stream=True,
timeout=180
)
if response.status_code != 200:
print(f"❌ SSE请求失败: {response.status_code}")
return False
ai_message = ""
# 处理SSE流
for line in response.iter_lines():
if not line:
continue
line = line.decode('utf-8')
if line.startswith('event: '):
event_type = line[7:].strip()
elif line.startswith('data: '):
data_str = line[6:].strip()
if data_str == '[DONE]':
break
try:
event_data = json.loads(data_str)
if event_type == 'message.delta':
content = event_data.get('content', '')
ai_message += content
print(content, end="", flush=True)
elif event_type == 'conversation.completed':
token_count = event_data.get('token_count', 0)
print(f"\n\n📊 对话完成Token用量: {token_count}")
except json.JSONDecodeError:
pass
print("\n" + "-" * 80)
print(f"\n✅ 后续消息测试成功")
print(f" AI消息长度: {len(ai_message)} 字符")
return True
except Exception as e:
print(f"\n❌ 后续消息测试失败: {e}")
return False
def main():
"""主测试流程"""
print("\n" + "=" * 60)
print("陪练功能API测试")
print("=" * 60)
try:
# 1. 登录
token = login()
# 2. 测试场景列表
if not test_get_scenes(token):
print("\n❌ 场景列表测试失败")
sys.exit(1)
# 3. 测试场景详情
scene = test_get_scene_detail(token, scene_id=1)
if not scene:
print("\n❌ 场景详情测试失败")
sys.exit(1)
# 4. 测试SSE对话
conversation_id = test_sse_chat(token, scene)
if not conversation_id:
print("\n❌ SSE对话测试失败")
sys.exit(1)
# 5. 测试后续消息
if not test_follow_up_message(token, conversation_id):
print("\n❌ 后续消息测试失败")
sys.exit(1)
# 全部测试通过
print("\n" + "=" * 60)
print("✅ 所有测试通过!")
print("=" * 60)
print("\n测试总结:")
print(" ✅ 登录认证")
print(" ✅ 场景列表查询")
print(" ✅ 场景详情查询")
print(" ✅ SSE流式对话首次消息")
print(" ✅ 后续消息(保持上下文)")
print("\n陪练中心入口功能开发完成!")
print("=" * 60)
except KeyboardInterrupt:
print("\n\n⚠️ 用户中断测试")
sys.exit(0)
except Exception as e:
print(f"\n❌ 测试过程出错: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,338 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>陪练记录API测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 3px solid #409eff;
padding-bottom: 10px;
}
.section {
margin: 20px 0;
padding: 20px;
background: #f9f9f9;
border-radius: 5px;
}
.form-group {
margin: 15px 0;
}
label {
display: inline-block;
width: 100px;
font-weight: bold;
}
input {
padding: 8px;
width: 300px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 10px 20px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin: 5px;
}
button:hover {
background: #66b1ff;
}
.success { color: #67c23a; }
.error { color: #f56c6c; }
.warning { color: #e6a23c; }
.info { color: #909399; }
pre {
background: #2d2d2d;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
max-height: 400px;
}
.status-item {
margin: 10px 0;
padding: 10px;
background: white;
border-left: 4px solid #409eff;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th {
background: #409eff;
color: white;
}
tr:hover {
background: #f5f7fa;
}
</style>
</head>
<body>
<div class="container">
<h1>陪练记录列表 - API测试工具</h1>
<div class="section">
<h3>1. 登录</h3>
<div class="form-group">
<label>用户名:</label>
<input type="text" id="username" value="admin" />
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" id="password" value="admin123" />
</div>
<button onclick="login()">登录</button>
<button onclick="checkLoginStatus()">检查登录状态</button>
<button onclick="logout()">退出登录</button>
<div id="loginStatus" class="status-item" style="display:none;"></div>
</div>
<div class="section">
<h3>2. 获取陪练记录</h3>
<button onclick="getPracticeRecords()">获取陪练记录列表</button>
<button onclick="getPracticeStats()">获取统计数据</button>
<div id="recordsStatus" class="status-item" style="display:none;"></div>
<div id="recordsTable"></div>
</div>
<div class="section">
<h3>3. 调试信息</h3>
<button onclick="showDebugInfo()">显示调试信息</button>
<pre id="debugInfo" style="display:none;"></pre>
</div>
</div>
<script>
const API_BASE = window.location.origin;
// 显示状态信息
function showStatus(elementId, message, type = 'info') {
const el = document.getElementById(elementId);
el.style.display = 'block';
el.className = `status-item ${type}`;
el.innerHTML = message;
}
// 登录
async function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
try {
showStatus('loginStatus', '正在登录...', 'info');
const response = await fetch(`${API_BASE}/api/v1/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
const result = await response.json();
if (result.code === 200) {
const token = result.data.token.access_token;
const refreshToken = result.data.token.refresh_token;
localStorage.setItem('access_token', token);
localStorage.setItem('refresh_token', refreshToken);
localStorage.setItem('user_info', JSON.stringify(result.data.user));
showStatus('loginStatus',
`✓ 登录成功!<br>
用户: ${result.data.user.username} (${result.data.user.full_name})<br>
Token已保存到localStorage`,
'success'
);
} else {
showStatus('loginStatus', `✗ 登录失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('loginStatus', `✗ 登录错误: ${error.message}`, 'error');
}
}
// 检查登录状态
function checkLoginStatus() {
const token = localStorage.getItem('access_token');
const userInfo = localStorage.getItem('user_info');
if (token && userInfo) {
const user = JSON.parse(userInfo);
showStatus('loginStatus',
`✓ 已登录<br>
用户: ${user.username} (${user.full_name})<br>
Token: ${token.substring(0, 30)}...`,
'success'
);
} else {
showStatus('loginStatus', '✗ 未登录', 'warning');
}
}
// 退出登录
function logout() {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_info');
showStatus('loginStatus', '✓ 已退出登录', 'info');
}
// 获取陪练记录
async function getPracticeRecords() {
const token = localStorage.getItem('access_token');
if (!token) {
showStatus('recordsStatus', '✗ 请先登录', 'error');
return;
}
try {
showStatus('recordsStatus', '正在获取陪练记录...', 'info');
const response = await fetch(`${API_BASE}/api/v1/practice/sessions/list?page=1&size=20`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (result.code === 200) {
const items = result.data.items || [];
const total = result.data.total || 0;
showStatus('recordsStatus',
`✓ 成功获取陪练记录!<br>
总记录数: ${total}<br>
当前页记录数: ${items.length}`,
'success'
);
// 显示表格
displayRecordsTable(items);
} else {
showStatus('recordsStatus', `✗ 获取失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('recordsStatus', `✗ 请求错误: ${error.message}`, 'error');
}
}
// 获取统计数据
async function getPracticeStats() {
const token = localStorage.getItem('access_token');
if (!token) {
showStatus('recordsStatus', '✗ 请先登录', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/api/v1/practice/stats`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (result.code === 200) {
const data = result.data;
showStatus('recordsStatus',
`✓ 统计数据:<br>
总次数: ${data.total_count}<br>
平均分: ${data.avg_score.toFixed(1)}<br>
总时长: ${data.total_duration_hours}小时<br>
本月进步: ${data.month_improvement}%`,
'success'
);
} else {
showStatus('recordsStatus', `✗ 获取失败: ${result.message}`, 'error');
}
} catch (error) {
showStatus('recordsStatus', `✗ 请求错误: ${error.message}`, 'error');
}
}
// 显示记录表格
function displayRecordsTable(items) {
if (items.length === 0) {
document.getElementById('recordsTable').innerHTML = '<p class="warning">⚠️ 没有陪练记录</p>';
return;
}
let html = '<table><thead><tr>';
html += '<th>会话ID</th><th>场景名称</th><th>开始时间</th><th>时长(秒)</th><th>对话轮数</th><th>评分</th><th>结果</th>';
html += '</tr></thead><tbody>';
items.forEach(item => {
html += '<tr>';
html += `<td>${item.session_id}</td>`;
html += `<td>${item.scene_name}</td>`;
html += `<td>${item.start_time}</td>`;
html += `<td>${item.duration_seconds}</td>`;
html += `<td>${item.turns}</td>`;
html += `<td>${item.total_score}</td>`;
html += `<td>${item.result}</td>`;
html += '</tr>';
});
html += '</tbody></table>';
document.getElementById('recordsTable').innerHTML = html;
}
// 显示调试信息
function showDebugInfo() {
const debugEl = document.getElementById('debugInfo');
debugEl.style.display = 'block';
const info = {
'API Base URL': API_BASE,
'localStorage.access_token': localStorage.getItem('access_token') ? localStorage.getItem('access_token').substring(0, 50) + '...' : 'null',
'localStorage.user_info': localStorage.getItem('user_info') || 'null',
'Current URL': window.location.href,
'User Agent': navigator.userAgent
};
debugEl.textContent = JSON.stringify(info, null, 2);
}
// 页面加载时检查登录状态
window.onload = function() {
checkLoginStatus();
};
</script>
</body>
</html>

View File

@@ -0,0 +1,123 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试陪练场景API</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.result { margin-top: 20px; padding: 15px; background: #f5f5f5; border-radius: 5px; }
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
pre { white-space: pre-wrap; word-wrap: break-word; }
</style>
</head>
<body>
<h1>陪练场景API测试</h1>
<div>
<button onclick="testLogin()">1. 测试登录</button>
<button onclick="testGetScenes()">2. 获取场景列表</button>
<button onclick="testWithBrowserToken()">3. 使用浏览器Token测试</button>
</div>
<div class="result" id="result"></div>
<script>
let token = '';
const API_BASE_URL = 'http://localhost:8000';
const resultDiv = document.getElementById('result');
function log(message, data = null) {
console.log(message, data);
let html = `<div><strong>${message}</strong></div>`;
if (data) {
html += `<pre>${JSON.stringify(data, null, 2)}</pre>`;
}
resultDiv.innerHTML = html + resultDiv.innerHTML;
}
async function testLogin() {
try {
log('正在登录...');
const response = await fetch(`${API_BASE_URL}/api/v1/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'admin',
password: 'admin123'
})
});
const data = await response.json();
log('登录响应:', data);
if (data.code === 200 && data.data.token) {
token = data.data.token.access_token;
log('✅ 登录成功Token已保存');
localStorage.setItem('access_token', token);
} else {
log('❌ 登录失败', data);
}
} catch (error) {
log('❌ 登录错误', error);
}
}
async function testGetScenes() {
if (!token) {
log('⚠️ 请先登录获取Token');
return;
}
try {
log('正在获取场景列表...');
const response = await fetch(`${API_BASE_URL}/api/v1/practice/scenes?page=1&size=20`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
log('场景列表响应:', data);
if (data.code === 200 && data.data) {
log(`✅ 获取成功!共 ${data.data.total} 个场景,当前页 ${data.data.items.length}`);
log('场景列表:', data.data.items.map(s => ({ id: s.id, name: s.name, type: s.type })));
} else {
log('❌ 获取失败', data);
}
} catch (error) {
log('❌ 请求错误', error);
}
}
async function testWithBrowserToken() {
const browserToken = localStorage.getItem('access_token');
if (!browserToken) {
log('⚠️ 浏览器中未找到Token请先在系统中登录');
return;
}
token = browserToken;
log('✅ 使用浏览器Token');
await testGetScenes();
}
// 页面加载时检查Token
window.onload = () => {
const browserToken = localStorage.getItem('access_token');
if (browserToken) {
token = browserToken;
log('✅ 检测到浏览器Token可直接使用');
} else {
log(' 未检测到Token请先登录');
}
};
</script>
</body>
</html>

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""
测试陪练记录列表API
"""
import requests
import json
# API基础地址
BASE_URL = "http://120.79.247.16"
def login(username: str, password: str):
"""登录获取token"""
url = f"https://aiedu.ireborn.com.cn/api/v1/auth/login"
data = {
"username": username,
"password": password
}
print(f"正在登录: {username}")
response = requests.post(url, json=data, verify=False)
print(f"登录响应: {response.status_code}")
result = response.json()
print(f"登录结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
if response.status_code == 200 and result.get('code') == 200:
token = result['data'].get('access_token')
print(f"✓ 登录成功Token: {token[:20]}...")
return token
else:
print(f"✗ 登录失败")
return None
def get_practice_sessions(token: str):
"""获取陪练记录列表"""
url = f"https://aiedu.ireborn.com.cn/api/v1/practice/sessions/list"
headers = {
"Authorization": f"Bearer {token}"
}
params = {
"page": 1,
"size": 20
}
print(f"\n正在获取陪练记录列表...")
print(f"请求URL: {url}")
print(f"请求参数: {params}")
response = requests.get(url, headers=headers, params=params, verify=False)
print(f"响应状态码: {response.status_code}")
result = response.json()
print(f"响应结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
if response.status_code == 200 and result.get('code') == 200:
data = result['data']
print(f"\n✓ 获取成功")
print(f"总记录数: {data.get('total', 0)}")
print(f"当前页记录数: {len(data.get('items', []))}")
# 打印前5条记录
items = data.get('items', [])
if items:
print(f"\n{min(5, len(items))}条记录:")
for i, item in enumerate(items[:5], 1):
print(f"{i}. {item.get('session_id')} - {item.get('scene_name')} - {item.get('start_time')}")
else:
print("\n⚠️ 记录列表为空!")
else:
print(f"\n✗ 获取失败")
def get_practice_stats(token: str):
"""获取陪练统计数据"""
url = f"https://aiedu.ireborn.com.cn/api/v1/practice/stats"
headers = {
"Authorization": f"Bearer {token}"
}
print(f"\n正在获取陪练统计数据...")
response = requests.get(url, headers=headers, verify=False)
print(f"响应状态码: {response.status_code}")
result = response.json()
print(f"响应结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
def main():
print("=" * 60)
print("测试陪练记录列表API")
print("=" * 60)
# 测试多个用户
test_users = [
("admin", "admin123"), # admin
("consultant_001", "admin123"), # consultant_001
("consultant_002", "admin123"), # consultant_002
]
for username, password in test_users:
print(f"\n{'=' * 60}")
print(f"测试用户: {username}")
print(f"{'=' * 60}")
# 登录
token = login(username, password)
if not token:
print(f"用户 {username} 登录失败,跳过")
continue
# 获取陪练记录
get_practice_sessions(token)
# 获取统计数据
get_practice_stats(token)
print("\n")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
测试成绩报告和错题本API
"""
import requests
import json
BASE_URL = "http://localhost:8000"
# 测试账号token需要先登录获取
# 这里假设已经有token实际使用时需要先登录
TOKEN = ""
def login():
"""登录获取token"""
response = requests.post(
f"{BASE_URL}/api/v1/auth/login",
json={
"username": "testuser",
"password": "TestPass123!"
}
)
if response.status_code == 200:
data = response.json()
if data.get('code') == 200:
return data['data']['access_token']
return None
def test_exam_report(token):
"""测试成绩报告API"""
print("\n=== 测试成绩报告API ===")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(
f"{BASE_URL}/api/v1/exams/statistics/report",
headers=headers
)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应code: {data.get('code')}")
print(f"响应message: {data.get('message')}")
if data.get('code') == 200:
report = data.get('data', {})
print(f"\n概览数据:")
print(f" - 平均成绩: {report.get('overview', {}).get('avg_score')}")
print(f" - 考试总数: {report.get('overview', {}).get('total_exams')}")
print(f" - 及格率: {report.get('overview', {}).get('pass_rate')}")
print(f"\n科目数量: {len(report.get('subjects', []))}")
print(f"最近考试数量: {len(report.get('recent_exams', []))}")
# 显示第一条最近考试记录
if report.get('recent_exams'):
exam = report['recent_exams'][0]
print(f"\n第一条考试记录:")
print(f" - ID: {exam.get('id')}")
print(f" - 课程: {exam.get('course_name')}")
print(f" - 三轮得分: {exam.get('round_scores')}")
else:
print(f"业务错误: {data.get('message')}")
else:
print(f"HTTP错误: {response.text}")
def test_mistakes_statistics(token):
"""测试错题统计API"""
print("\n=== 测试错题统计API ===")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(
f"{BASE_URL}/api/v1/exams/mistakes/statistics",
headers=headers
)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应code: {data.get('code')}")
if data.get('code') == 200:
stats = data.get('data', {})
print(f"\n错题总数: {stats.get('total')}")
print(f"按课程统计: {len(stats.get('by_course', []))} 个课程")
print(f"按题型统计: {len(stats.get('by_type', []))} 种题型")
print(f"时间统计:")
print(f" - 最近一周: {stats.get('by_time', {}).get('week')}")
print(f" - 最近一月: {stats.get('by_time', {}).get('month')}")
print(f" - 最近三月: {stats.get('by_time', {}).get('quarter')}")
# 显示按课程统计
if stats.get('by_course'):
print(f"\n按课程统计详情:")
for course in stats['by_course'][:3]:
print(f" - {course.get('course_name')}: {course.get('count')}")
else:
print(f"业务错误: {data.get('message')}")
else:
print(f"HTTP错误: {response.text}")
def test_mistakes_list(token):
"""测试错题列表API"""
print("\n=== 测试错题列表API ===")
headers = {"Authorization": f"Bearer {token}"}
# 测试1查询所有错题第1页
response = requests.get(
f"{BASE_URL}/api/v1/exams/mistakes/list",
headers=headers,
params={"page": 1, "size": 5}
)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应code: {data.get('code')}")
if data.get('code') == 200:
result = data.get('data', {})
print(f"\n总数: {result.get('total')}")
print(f"当前页: {result.get('page')}")
print(f"每页数量: {result.get('size')}")
print(f"总页数: {result.get('pages')}")
print(f"本页错题数: {len(result.get('items', []))}")
# 显示前2条错题
if result.get('items'):
print(f"\n前2条错题:")
for item in result['items'][:2]:
print(f" - ID: {item.get('id')}")
print(f" 课程: {item.get('course_name')}")
print(f" 题型: {item.get('question_type')}")
print(f" 题目: {item.get('question_content')[:50]}...")
else:
print(f"业务错误: {data.get('message')}")
else:
print(f"HTTP错误: {response.text}")
if __name__ == "__main__":
print("开始测试成绩报告和错题本API...")
# 1. 登录获取token
print("\n1. 登录获取token...")
token = login()
if not token:
print("❌ 登录失败")
exit(1)
print(f"✅ 登录成功token: {token[:20]}...")
# 2. 测试成绩报告API
test_exam_report(token)
# 3. 测试错题统计API
test_mistakes_statistics(token)
# 4. 测试错题列表API
test_mistakes_list(token)
print("\n\n测试完成!")

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python
"""
测试更新用户个人信息(包括手机号、学校、专业)
"""
import requests
import json
# API配置
API_BASE_URL = "http://localhost:8000"
def test_update_profile():
"""测试更新个人信息"""
# 1. 先登录获取token
print("1. 登录获取token...")
login_data = {
"username": "superadmin",
"password": "Superadmin123!"
}
response = requests.post(
f"{API_BASE_URL}/api/v1/auth/login",
json=login_data
)
if response.status_code != 200:
print(f"登录失败: {response.text}")
return
token = response.json()["data"]["token"]["access_token"]
print(f"登录成功获取到token")
# 2. 获取当前用户信息
print("\n2. 获取当前用户信息...")
headers = {
"Authorization": f"Bearer {token}"
}
response = requests.get(
f"{API_BASE_URL}/api/v1/users/me",
headers=headers
)
if response.status_code == 200:
user_data = response.json()["data"]
print(f"当前用户信息:")
print(f" 用户名: {user_data.get('username')}")
print(f" 邮箱: {user_data.get('email')}")
print(f" 手机号: {user_data.get('phone', '未设置')}")
print(f" 学校: {user_data.get('school', '未设置')}")
print(f" 专业: {user_data.get('major', '未设置')}")
else:
print(f"获取用户信息失败: {response.text}")
return
# 3. 更新用户信息
print("\n3. 更新用户信息(包括手机号、学校、专业)...")
update_data = {
"phone": "13800138000",
"school": "清华大学",
"major": "计算机科学与技术",
"bio": "这是一个测试用的个人简介"
}
response = requests.put(
f"{API_BASE_URL}/api/v1/users/me",
headers=headers,
json=update_data
)
if response.status_code == 200:
print("更新成功!")
updated_data = response.json()["data"]
print(f"更新后的信息:")
print(f" 手机号: {updated_data.get('phone', '未设置')}")
print(f" 学校: {updated_data.get('school', '未设置')}")
print(f" 专业: {updated_data.get('major', '未设置')}")
print(f" 个人简介: {updated_data.get('bio', '未设置')}")
else:
print(f"更新失败: {response.text}")
# 4. 再次获取信息验证
print("\n4. 再次获取用户信息验证更新...")
response = requests.get(
f"{API_BASE_URL}/api/v1/users/me",
headers=headers
)
if response.status_code == 200:
user_data = response.json()["data"]
print(f"验证结果:")
print(f" 手机号: {user_data.get('phone', '未设置')}")
print(f" 学校: {user_data.get('school', '未设置')}")
print(f" 专业: {user_data.get('major', '未设置')}")
else:
print(f"获取用户信息失败: {response.text}")
if __name__ == "__main__":
print("测试更新用户个人信息API...")
test_update_profile()
print("\n测试完成!")