/** * 学校端 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)); }); });