383 lines
11 KiB
TypeScript
Executable File
383 lines
11 KiB
TypeScript
Executable File
/**
|
||
* 测试课程生成完整流程
|
||
* 使用方法:ts-node backend/scripts/test-course-generation-flow.ts
|
||
*/
|
||
|
||
import axios from 'axios';
|
||
import * as dotenv from 'dotenv';
|
||
import path from 'path';
|
||
|
||
// 加载环境变量
|
||
dotenv.config({ path: path.join(__dirname, '../.env.test') });
|
||
|
||
const BASE_URL = process.env.API_BASE_URL || 'http://localhost:3000';
|
||
const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL || 'test@example.com';
|
||
const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD || 'test123456';
|
||
|
||
interface CreateCourseResponse {
|
||
success: boolean;
|
||
data: {
|
||
courseId: string;
|
||
taskId: string;
|
||
status: string;
|
||
generationStyle?: string;
|
||
message?: string;
|
||
};
|
||
}
|
||
|
||
interface CourseStatusResponse {
|
||
success: boolean;
|
||
data: {
|
||
courses: Array<{
|
||
id: string;
|
||
title: string;
|
||
generation_status: string | null;
|
||
generation_progress: number | null;
|
||
status: string;
|
||
progress: number;
|
||
error_message?: string | null;
|
||
}>;
|
||
};
|
||
}
|
||
|
||
interface TaskResponse {
|
||
success: boolean;
|
||
data: {
|
||
task: {
|
||
id: string;
|
||
status: string;
|
||
generationStyle: string | null;
|
||
outline: any;
|
||
suggestedTitle: string | null;
|
||
errorMessage: string | null;
|
||
};
|
||
};
|
||
}
|
||
|
||
interface PromptLogResponse {
|
||
success: boolean;
|
||
data: {
|
||
logs: Array<{
|
||
id: string;
|
||
promptType: string;
|
||
taskId: string | null;
|
||
status: string;
|
||
duration: number | null;
|
||
tokensUsed: number | null;
|
||
errorMessage: string | null;
|
||
createdAt: string;
|
||
}>;
|
||
total: number;
|
||
};
|
||
}
|
||
|
||
// 测试内容
|
||
const TEST_CONTENT = `
|
||
# 如何有效社交
|
||
|
||
社交是每个人都需要掌握的重要技能。在现代社会,良好的人际关系不仅能帮助我们获得更多机会,还能提升我们的生活质量。
|
||
|
||
## 第一章:理解社交的本质
|
||
|
||
社交不仅仅是简单的聊天和应酬,它更是一种建立信任、传递价值的过程。真正的社交应该是双向的,既要有给予,也要有收获。
|
||
|
||
### 1.1 社交的核心价值
|
||
|
||
社交的核心在于建立有意义的人际关系。这种关系应该基于:
|
||
- 相互尊重
|
||
- 价值交换
|
||
- 长期维护
|
||
|
||
### 1.2 常见的社交误区
|
||
|
||
很多人对社交存在误解:
|
||
- 认为社交就是应酬和喝酒
|
||
- 认为社交需要花费大量时间
|
||
- 认为社交是外向者的专利
|
||
|
||
## 第二章:提升社交能力的方法
|
||
|
||
### 2.1 培养倾听能力
|
||
|
||
倾听是社交的基础。一个好的倾听者应该:
|
||
- 专注对方的表达
|
||
- 理解对方的情绪
|
||
- 给予适当的反馈
|
||
|
||
### 2.2 学会表达自己
|
||
|
||
表达自己同样重要:
|
||
- 清晰表达观点
|
||
- 适当展示价值
|
||
- 保持真诚和自然
|
||
|
||
## 第三章:维护人际关系
|
||
|
||
### 3.1 定期联系
|
||
|
||
不要等到需要帮助时才联系朋友:
|
||
- 定期问候
|
||
- 分享有价值的信息
|
||
- 在对方需要时提供帮助
|
||
|
||
### 3.2 维护关系
|
||
|
||
关系的维护需要:
|
||
- 时间和精力
|
||
- 真诚的态度
|
||
- 持续的价值交换
|
||
`;
|
||
|
||
async function sleep(ms: number) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
interface LoginResponse {
|
||
success: boolean;
|
||
data: {
|
||
token: string;
|
||
user: any;
|
||
};
|
||
}
|
||
|
||
async function login(): Promise<string> {
|
||
console.log('🔐 登录获取 Token...');
|
||
|
||
// 先尝试注册(如果用户不存在)
|
||
try {
|
||
console.log('📝 尝试注册测试用户...');
|
||
await axios.post(`${BASE_URL}/api/auth/register`, {
|
||
email: TEST_USER_EMAIL,
|
||
password: TEST_USER_PASSWORD,
|
||
username: '测试用户',
|
||
});
|
||
console.log('✅ 注册成功');
|
||
} catch (regError: any) {
|
||
if (regError.response?.status === 400 && regError.response?.data?.message?.includes('已存在')) {
|
||
console.log('ℹ️ 用户已存在,直接登录');
|
||
} else {
|
||
console.log(`⚠️ 注册失败(可能用户已存在): ${regError.response?.data?.message || regError.message}`);
|
||
}
|
||
}
|
||
|
||
// 登录
|
||
try {
|
||
const response = await axios.post<LoginResponse>(`${BASE_URL}/api/auth/login`, {
|
||
email: TEST_USER_EMAIL,
|
||
password: TEST_USER_PASSWORD,
|
||
});
|
||
|
||
if (response.data.success && response.data.data.token) {
|
||
console.log('✅ 登录成功');
|
||
return response.data.data.token;
|
||
} else {
|
||
throw new Error('登录失败:未返回 Token');
|
||
}
|
||
} catch (error: any) {
|
||
const errorMsg = error.response?.data?.message || error.message;
|
||
throw new Error(`登录失败: ${errorMsg}`);
|
||
}
|
||
}
|
||
|
||
async function createCourse(token: string, style: 'full' | 'essence' | 'one-page' = 'essence'): Promise<CreateCourseResponse['data']> {
|
||
console.log(`\n📝 创建课程(风格: ${style})...`);
|
||
|
||
const response = await axios.post<CreateCourseResponse>(
|
||
`${BASE_URL}/api/ai/content/upload`,
|
||
{
|
||
content: TEST_CONTENT,
|
||
style: style,
|
||
},
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
if (!response.data.success) {
|
||
throw new Error('创建课程失败');
|
||
}
|
||
|
||
console.log(`✅ 课程创建成功:`);
|
||
console.log(` - Course ID: ${response.data.data.courseId}`);
|
||
console.log(` - Task ID: ${response.data.data.taskId}`);
|
||
console.log(` - 状态: ${response.data.data.status}`);
|
||
|
||
return response.data.data;
|
||
}
|
||
|
||
async function checkCourseStatus(token: string, courseId: string, maxWait: number = 300000): Promise<void> {
|
||
console.log(`\n⏳ 等待课程生成完成(最多 ${maxWait / 1000} 秒)...`);
|
||
|
||
const startTime = Date.now();
|
||
let lastProgress = -1;
|
||
|
||
while (Date.now() - startTime < maxWait) {
|
||
try {
|
||
const response = await axios.get<CourseStatusResponse>(
|
||
`${BASE_URL}/api/my-courses`,
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
const course = response.data.data.courses.find(c => c.id === courseId);
|
||
|
||
if (!course) {
|
||
console.log('⚠️ 课程未找到,继续等待...');
|
||
await sleep(3000);
|
||
continue;
|
||
}
|
||
|
||
const progress = course.generation_progress ? Math.round(course.generation_progress * 100) : 0;
|
||
|
||
if (progress !== lastProgress) {
|
||
console.log(`📊 生成进度: ${progress}% (状态: ${course.generation_status || course.status})`);
|
||
lastProgress = progress;
|
||
}
|
||
|
||
if (course.generation_status === 'completed' || course.status === 'completed') {
|
||
console.log(`\n✅ 课程生成完成!`);
|
||
console.log(` - 标题: ${course.title}`);
|
||
console.log(` - 最终状态: ${course.status}`);
|
||
return;
|
||
}
|
||
|
||
if (course.generation_status === 'failed' || course.status === 'failed') {
|
||
console.log(`\n❌ 课程生成失败!`);
|
||
console.log(` - 错误信息: ${course.error_message || '未知错误'}`);
|
||
throw new Error(`生成失败: ${course.error_message || '未知错误'}`);
|
||
}
|
||
|
||
await sleep(3000);
|
||
} catch (error: any) {
|
||
if (error.response?.status === 401) {
|
||
throw new Error('Token 已过期,请重新登录');
|
||
}
|
||
console.log(`⚠️ 查询状态失败: ${error.message},继续重试...`);
|
||
await sleep(3000);
|
||
}
|
||
}
|
||
|
||
throw new Error('生成超时');
|
||
}
|
||
|
||
async function checkTaskStatus(token: string, taskId: string): Promise<void> {
|
||
console.log(`\n📋 查询任务详情 (Task ID: ${taskId})...`);
|
||
|
||
try {
|
||
const response = await axios.get<TaskResponse>(
|
||
`${BASE_URL}/api/ai/content/tasks/${taskId}`,
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
const task = response.data.data.task;
|
||
console.log(`✅ 任务状态:`);
|
||
console.log(` - 状态: ${task.status}`);
|
||
console.log(` - 风格: ${task.generationStyle || '未设置'}`);
|
||
console.log(` - 标题: ${task.suggestedTitle || '未生成'}`);
|
||
console.log(` - 大纲: ${task.outline ? '已生成' : '未生成'}`);
|
||
if (task.errorMessage) {
|
||
console.log(` - 错误: ${task.errorMessage}`);
|
||
}
|
||
} catch (error: any) {
|
||
console.log(`⚠️ 查询任务失败: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
async function checkPromptLogs(token: string, taskId: string): Promise<void> {
|
||
console.log(`\n📝 查询 AI 调用日志 (Task ID: ${taskId})...`);
|
||
|
||
try {
|
||
const response = await axios.get<PromptLogResponse>(
|
||
`${BASE_URL}/api/ai/prompts/logs?taskId=${taskId}`,
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
const logs = response.data.data.logs;
|
||
console.log(`✅ 找到 ${logs.length} 条日志记录:`);
|
||
|
||
logs.forEach((log, index) => {
|
||
console.log(`\n ${index + 1}. ${log.promptType} (${log.status})`);
|
||
console.log(` - 耗时: ${log.duration ? `${log.duration}ms` : 'N/A'}`);
|
||
console.log(` - Tokens: ${log.tokensUsed || 'N/A'}`);
|
||
console.log(` - 时间: ${new Date(log.createdAt).toLocaleString()}`);
|
||
if (log.errorMessage) {
|
||
console.log(` - 错误: ${log.errorMessage}`);
|
||
}
|
||
});
|
||
} catch (error: any) {
|
||
console.log(`⚠️ 查询日志失败: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('🧪 开始测试课程生成完整流程\n');
|
||
console.log(`📍 API 地址: ${BASE_URL}`);
|
||
console.log(`👤 测试用户: ${TEST_USER_EMAIL}\n`);
|
||
|
||
try {
|
||
// 1. 登录
|
||
const token = await login();
|
||
|
||
// 2. 创建课程(测试三种风格)
|
||
const styles: Array<'full' | 'essence' | 'one-page'> = ['essence', 'full', 'one-page'];
|
||
|
||
for (const style of styles) {
|
||
console.log(`\n${'='.repeat(60)}`);
|
||
console.log(`测试风格: ${style}`);
|
||
console.log('='.repeat(60));
|
||
|
||
try {
|
||
// 创建课程
|
||
const { courseId, taskId } = await createCourse(token, style);
|
||
|
||
// 查询任务状态
|
||
await checkTaskStatus(token, taskId);
|
||
|
||
// 等待生成完成
|
||
await checkCourseStatus(token, courseId);
|
||
|
||
// 查询日志
|
||
await checkPromptLogs(token, taskId);
|
||
|
||
console.log(`\n✅ 风格 ${style} 测试完成!`);
|
||
|
||
// 等待一下再进行下一个测试
|
||
if (style !== styles[styles.length - 1]) {
|
||
console.log('\n⏸️ 等待 5 秒后进行下一个测试...');
|
||
await sleep(5000);
|
||
}
|
||
} catch (error: any) {
|
||
console.error(`\n❌ 风格 ${style} 测试失败: ${error.message}`);
|
||
// 继续测试下一个风格
|
||
}
|
||
}
|
||
|
||
console.log(`\n${'='.repeat(60)}`);
|
||
console.log('🎉 所有测试完成!');
|
||
console.log('='.repeat(60));
|
||
|
||
} catch (error: any) {
|
||
console.error(`\n❌ 测试失败: ${error.message}`);
|
||
if (error.stack) {
|
||
console.error(error.stack);
|
||
}
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
// 运行测试
|
||
main().catch(console.error);
|