前后端目录重命名: - reading-platform-java/ → lesingle-edu-reading-platform-backend/ - reading-platform-frontend/ → lesingle-edu-reading-platform-frontend/ 更新相关文件: - 所有 shell 脚本中的目录引用 - pom.xml 和 application.yml 中的项目名称 - package.json 中的项目名称 - .claude/CLAUDE.md 中的路径引用 - README 文档中的路径引用
247 lines
11 KiB
TypeScript
247 lines
11 KiB
TypeScript
/**
|
|
* 学校端 API 接口测试 - 检测 500 报错
|
|
*
|
|
* 测试所有学校端 API 接口,检查是否有 500 服务器错误
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
// 学校端测试账号
|
|
const SCHOOL_ACCOUNT = 'school1';
|
|
const SCHOOL_PASSWORD = '123456';
|
|
|
|
// API 端点列表 (学校端所有接口)
|
|
const SCHOOL_APIS = [
|
|
// ==================== 认证相关 ====================
|
|
{ method: 'POST', path: '/api/v1/auth/login', name: '登录', requiresAuth: false },
|
|
|
|
// ==================== 仪表盘/统计 ====================
|
|
{ method: 'GET', path: '/api/v1/school/stats', name: '学校统计数据' },
|
|
{ method: 'GET', path: '/api/v1/school/stats/teachers', name: '活跃教师统计', params: { limit: '5' } },
|
|
{ method: 'GET', path: '/api/v1/school/stats/courses', name: '课程使用统计' },
|
|
{ method: 'GET', path: '/api/v1/school/stats/activities', name: '最近活动', params: { limit: '5' } },
|
|
{ method: 'GET', path: '/api/v1/school/stats/lesson-trend', name: '课程趋势', params: { months: '6' } },
|
|
{ method: 'GET', path: '/api/v1/school/stats/course-distribution', name: '课程分布' },
|
|
|
|
// ==================== 教师管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/teachers', name: '教师列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/teachers/1', name: '获取教师详情' },
|
|
|
|
// ==================== 学生管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/students', name: '学生列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/students/1', name: '获取学生详情' },
|
|
{ method: 'GET', path: '/api/v1/school/students/import/template', name: '学生导入模板' },
|
|
|
|
// ==================== 班级管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/classes', name: '班级列表' },
|
|
{ method: 'GET', path: '/api/v1/school/classes/1', name: '获取班级详情' },
|
|
{ method: 'GET', path: '/api/v1/school/classes/1/students', name: '班级学生列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/classes/1/teachers', name: '班级教师列表' },
|
|
|
|
// ==================== 家长管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/parents', name: '家长列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/parents/1', name: '获取家长详情' },
|
|
|
|
// ==================== 课程管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/courses', name: '学校课程列表' },
|
|
{ method: 'GET', path: '/api/v1/school/courses/1', name: '获取学校课程详情' },
|
|
|
|
// ==================== 套餐管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/package', name: '套餐信息' },
|
|
{ method: 'GET', path: '/api/v1/school/package/usage', name: '套餐使用情况' },
|
|
{ method: 'GET', path: '/api/v1/school/packages', name: '租户套餐列表' },
|
|
|
|
// ==================== 系统设置 ====================
|
|
{ method: 'GET', path: '/api/v1/school/settings', name: '系统设置' },
|
|
|
|
// ==================== 排课管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/schedules', name: '排课列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/schedules/1', name: '获取排课详情' },
|
|
{ method: 'GET', path: '/api/v1/school/schedules/timetable', name: '课程表', params: { startDate: '2026-03-01', endDate: '2026-03-31' } },
|
|
|
|
// ==================== 排课模板 ====================
|
|
{ method: 'GET', path: '/api/v1/school/schedule-templates', name: '排课模板列表' },
|
|
{ method: 'GET', path: '/api/v1/school/schedule-templates/1', name: '获取排课模板详情' },
|
|
|
|
// ==================== 任务管理 ====================
|
|
{ method: 'GET', path: '/api/v1/school/tasks', name: '任务列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/1', name: '获取任务详情' },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/stats', name: '任务统计' },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/stats/by-type', name: '任务统计 (按类型)' },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/stats/by-class', name: '任务统计 (按班级)' },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/stats/monthly', name: '任务月度统计', params: { months: '6' } },
|
|
{ method: 'GET', path: '/api/v1/school/tasks/1/completions', name: '任务完成情况' },
|
|
|
|
// ==================== 任务模板 ====================
|
|
{ method: 'GET', path: '/api/v1/school/task-templates', name: '任务模板列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/task-templates/1', name: '获取任务模板详情' },
|
|
{ method: 'GET', path: '/api/v1/school/task-templates/default/READING', name: '获取默认任务模板' },
|
|
|
|
// ==================== 成长记录 ====================
|
|
{ method: 'GET', path: '/api/v1/school/growth-records', name: '成长记录列表', params: { pageNum: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/growth-records/1', name: '获取成长记录详情' },
|
|
{ method: 'GET', path: '/api/v1/school/growth-records/recent', name: '最近成长记录', params: { limit: '5' } },
|
|
{ method: 'GET', path: '/api/v1/school/growth-records/student/1', name: '学生成长记录', params: { pageNum: '1', pageSize: '10' } },
|
|
|
|
// ==================== 数据报告 ====================
|
|
{ method: 'GET', path: '/api/v1/school/reports/overview', name: '报告概览' },
|
|
{ method: 'GET', path: '/api/v1/school/reports/teachers', name: '教师报告' },
|
|
{ method: 'GET', path: '/api/v1/school/reports/courses', name: '课程报告' },
|
|
{ method: 'GET', path: '/api/v1/school/reports/students', name: '学生报告' },
|
|
|
|
// ==================== 操作日志 ====================
|
|
{ method: 'GET', path: '/api/v1/school/operation-logs', name: '操作日志列表', params: { page: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/operation-logs/1', name: '获取操作日志详情' },
|
|
{ method: 'GET', path: '/api/v1/school/operation-logs/stats', name: '操作日志统计' },
|
|
|
|
// ==================== 通知 ====================
|
|
{ method: 'GET', path: '/api/v1/school/notifications', name: '通知列表', params: { page: '1', pageSize: '10' } },
|
|
{ method: 'GET', path: '/api/v1/school/notifications/unread-count', name: '未读通知数量' },
|
|
|
|
// ==================== 文件上传 ====================
|
|
{ method: 'POST', path: '/api/v1/files/oss/upload', name: 'OSS 文件上传', requiresAuth: true, isUpload: true },
|
|
|
|
// ==================== 数据导出 (需要特殊处理) ====================
|
|
// 这些接口返回二进制数据,需要特殊处理
|
|
// { method: 'GET', path: '/api/v1/school/export/lessons', name: '导出课程记录' },
|
|
// { method: 'GET', path: '/api/v1/school/export/teacher-stats', name: '导出教师统计' },
|
|
// { method: 'GET', path: '/api/v1/school/export/student-stats', name: '导出学生统计' },
|
|
];
|
|
|
|
// 错误报告
|
|
const errorReport: Array<{
|
|
api: typeof SCHOOL_APIS[0];
|
|
status: number;
|
|
error: string;
|
|
response?: any;
|
|
}> = [];
|
|
|
|
// 成功报告
|
|
const successReport: Array<{
|
|
api: typeof SCHOOL_APIS[0];
|
|
status: number;
|
|
}> = [];
|
|
|
|
test.describe('学校端 API 接口 500 错误检测', () => {
|
|
let authToken: string = '';
|
|
|
|
test.beforeAll('登录获取 Token', async ({ request }) => {
|
|
const response = await request.post('http://localhost:8080/api/v1/auth/login', {
|
|
data: {
|
|
username: SCHOOL_ACCOUNT,
|
|
password: SCHOOL_PASSWORD,
|
|
role: 'school',
|
|
},
|
|
});
|
|
|
|
const data = await response.json();
|
|
authToken = data.data?.token || '';
|
|
console.log('✅ 登录成功,获取 Token');
|
|
});
|
|
|
|
for (const api of SCHOOL_APIS) {
|
|
test(`${api.method} ${api.path} - ${api.name}`, async ({ request }) => {
|
|
const url = `http://localhost:8080${api.path}`;
|
|
|
|
try {
|
|
const options: any = {
|
|
headers: {
|
|
'Content-Type': api.isUpload ? 'multipart/form-data' : 'application/json',
|
|
...(authToken && { Authorization: `Bearer ${authToken}` }),
|
|
},
|
|
};
|
|
|
|
if (api.params) {
|
|
const queryString = new URLSearchParams(api.params).toString();
|
|
options.query = api.params;
|
|
}
|
|
|
|
let response;
|
|
switch (api.method) {
|
|
case 'GET':
|
|
response = await request.get(url, options);
|
|
break;
|
|
case 'POST':
|
|
response = await request.post(url, options);
|
|
break;
|
|
case 'PUT':
|
|
response = await request.put(url, options);
|
|
break;
|
|
case 'DELETE':
|
|
response = await request.delete(url, options);
|
|
break;
|
|
default:
|
|
throw new Error(`不支持的 HTTP 方法:${api.method}`);
|
|
}
|
|
|
|
const status = response.status();
|
|
|
|
// 检查是否为 500 错误
|
|
if (status >= 500) {
|
|
let errorBody;
|
|
try {
|
|
errorBody = await response.text();
|
|
} catch (e) {
|
|
errorBody = '无法读取响应体';
|
|
}
|
|
|
|
errorReport.push({
|
|
api,
|
|
status,
|
|
error: `服务器内部错误 (500)`,
|
|
response: errorBody,
|
|
});
|
|
|
|
console.error(`❌ [500 错误] ${api.method} ${api.path} - ${api.name}`);
|
|
console.error(` 响应:${errorBody?.substring(0, 200)}`);
|
|
} else {
|
|
successReport.push({ api, status });
|
|
}
|
|
|
|
// 断言:不应出现 500 错误
|
|
expect(status).toBeLessThan(500);
|
|
|
|
} catch (error: any) {
|
|
// 网络错误或其他异常也可能意味着后端有问题
|
|
errorReport.push({
|
|
api,
|
|
status: 0,
|
|
error: error.message || '未知错误',
|
|
});
|
|
|
|
console.error(`❌ [异常] ${api.method} ${api.path} - ${api.name}: ${error.message}`);
|
|
|
|
// 失败但继续执行
|
|
}
|
|
});
|
|
}
|
|
|
|
test.afterAll('生成测试报告', async () => {
|
|
console.log('\n' + '='.repeat(80));
|
|
console.log('学校端 API 接口测试报告');
|
|
console.log('='.repeat(80));
|
|
console.log(`总接口数:${SCHOOL_APIS.length}`);
|
|
console.log(`✅ 通过:${successReport.length}`);
|
|
console.log(`❌ 500 错误:${errorReport.length}`);
|
|
console.log('='.repeat(80));
|
|
|
|
if (errorReport.length > 0) {
|
|
console.log('\n📋 500 错误接口列表:\n');
|
|
errorReport.forEach((item, index) => {
|
|
console.log(`${index + 1}. [${item.api.method}] ${item.api.path}`);
|
|
console.log(` 名称:${item.api.name}`);
|
|
console.log(` 状态码:${item.status}`);
|
|
console.log(` 错误:${item.error}`);
|
|
if (item.response) {
|
|
console.log(` 响应:${item.response?.substring(0, 300)}...`);
|
|
}
|
|
console.log('');
|
|
});
|
|
} else {
|
|
console.log('\n🎉 所有接口测试通过,无 500 错误!');
|
|
}
|
|
|
|
console.log('\n' + '='.repeat(80));
|
|
});
|
|
});
|