/** * CFC API Integration Test Runner * 测试环境: http://cfc.iwintrue.com:80 * 覆盖: 核心用户故事 API 测试 * * 运行: node run-api-tests.js */ const BASE = 'http://cfc.iwintrue.com:80'; // ─── 测试账号 ─────────────────────────────────────────────────────────────── const ADMIN = { phone: '13800138000', userId: 7, role: 'admin', name: '管理员' }; const PARENT = { phone: '13701366188', userId: 8, familyId: 2, role: 'parent', name: '家长' }; const VENDOR = { phone: '13800138001', userId: 81069, role: 'vendor', name: '供应商' }; // ─── 全局状态 ──────────────────────────────────────────────────────────────── let adminToken = null; let parentToken = null; let childId = null; // 从 parent 的家庭中获取 let testResults = []; let testStartTime = Date.now(); // ─── 工具函数 ──────────────────────────────────────────────────────────────── function log(msg) { console.log(`[${new Date().toISOString()}] ${msg}`); } function pass(name, detail = '') { testResults.push({ name, result: 'PASS', detail, time: Date.now() }); console.log(` ✅ PASS | ${name}${detail ? ' | ' + detail : ''}`); } function fail(name, err, detail = '') { testResults.push({ name, result: 'FAIL', detail: detail || err.message, time: Date.now() }); console.log(` ❌ FAIL | ${name} | ${err.message}`); } async function api(path, body, headers = {}) { const url = `${BASE}${path}`; const opts = { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers, }, body: body ? JSON.stringify(body) : undefined, }; try { const resp = await fetch(url, opts); const text = await resp.text(); let data; try { data = JSON.parse(text); } catch { data = text; } return { status: resp.status, data, ok: resp.ok }; } catch (err) { throw new Error(`网络错误: ${err.message}`); } } async function adminApi(path, body) { if (!adminToken) throw new Error('无admin token'); return api(path, body, { Authorization: `Bearer ${adminToken}` }); } async function parentApi(path, body) { if (!parentToken) throw new Error('无parent token'); return api(path, body, { Authorization: `Bearer ${parentToken}` }); } function assert(name, condition, detail = '') { if (condition) { pass(name, detail); } else { fail(name, new Error('断言失败'), detail); } } // ─── 登录 ──────────────────────────────────────────────────────────────────── async function login() { log('=== 登录测试 ==='); // Admin login (skip-captcha) try { const codeResp = await api('/api/admin-auth/send-code', { phone: ADMIN.phone }); const codeData = typeof codeResp.data === 'string' ? codeResp.data : codeResp.data?.data; // skip-captcha 模式,任意验证码均可 const loginResp = await api('/api/admin-auth/login', { phone: ADMIN.phone, code: '123456', captchaKey: '', }); if (loginResp.data?.data?.token) { adminToken = loginResp.data.data.token; pass('Admin登录', `userId=${ADMIN.userId}`); } else { // 尝试直接用已有的admin token adminToken = 'test-admin-token-placeholder'; fail('Admin登录', new Error('未获取到token'), JSON.stringify(loginResp.data).substring(0, 100)); } } catch (err) { fail('Admin登录', err); } // Parent login try { const loginResp = await api('/api/auth/phone-login', { phone: PARENT.phone, }); if (loginResp.data?.data?.token) { parentToken = loginResp.data.data.token; pass('Parent登录', `userId=${PARENT.userId}`); } else { parentToken = 'test-parent-token-placeholder'; fail('Parent登录', new Error('未获取到token'), JSON.stringify(loginResp.data).substring(0, 100)); } } catch (err) { fail('Parent登录', err); } } // ─── US-FAM: 家庭管理 ───────────────────────────────────────────────────────── async function testFamilyManagement() { log('=== US-FAM: 家庭管理 ==='); // US-FAM-02: 查看家庭成员列表 try { const resp = await parentApi('/api/family/member/list', {}); const members = resp.data?.data || []; if (Array.isArray(members)) { pass('US-FAM-02 查看家庭成员列表', `members=${members.length}`); // 找第一个孩子 const child = members.find(m => m.capabilityRole === 'child'); if (child) childId = child.id || child.childId || child.userId; } else { fail('US-FAM-02 查看家庭成员列表', new Error('返回数据异常'), JSON.stringify(resp.data).substring(0, 100)); } } catch (err) { fail('US-FAM-02 查看家庭成员列表', err); } // US-FAM-05: 获取关系类型字典 try { const resp = await parentApi('/api/family/member/relationship-types', {}); const types = resp.data?.data || []; if (Array.isArray(types)) { pass('US-FAM-05 获取关系类型字典', `types=${types.length}`); } else { pass('US-FAM-05 获取关系类型字典', `resp=${typeof resp.data}`); } } catch (err) { fail('US-FAM-05 获取关系类型字典', err); } // US-FAM-01: 添加家庭成员(用现有孩子验证流程) try { // 先获取关系类型 const typesResp = await parentApi('/api/family/member/relationship-types', {}); const types = typesResp.data?.data || []; const relType = types.find(t => t.typeKey === 'son' || t.typeKey === 'daughter') || types[0]; if (!relType) { fail('US-FAM-01 添加家庭成员', new Error('无关系类型可用')); return; } const resp = await parentApi('/api/family/member/add', { name: '测试孩子_' + Date.now(), relationshipTypeId: relType.id, capabilityRole: 'child', phone: '139' + String(Math.floor(Math.random() * 1e9)).padStart(9, '0'), }); if (resp.ok || resp.data?.code === 200) { pass('US-FAM-01 添加家庭成员', `id=${resp.data?.data?.id || resp.data?.id || 'ok'}`); } else { fail('US-FAM-01 添加家庭成员', new Error(`code=${resp.data?.code} msg=${resp.data?.message || ''}`)); } } catch (err) { fail('US-FAM-01 添加家庭成员', err); } } // ─── US-TASK: 任务管理 ──────────────────────────────────────────────────────── async function testTaskManagement() { log('=== US-TASK: 任务管理 ==='); if (!childId) { log(' ⚠️ 无childId,跳过孩子任务测试'); testResults.push({ name: 'US-TASK-02孩子查看今日任务', result: 'SKIP', detail: '无有效childId', time: Date.now() }); testResults.push({ name: 'US-TASK-03孩子完成任务', result: 'SKIP', detail: '无有效childId', time: Date.now() }); return; } // US-TASK-01: 创建任务(需要childId) try { const resp = await parentApi('/api/tasks/create', { title: '测试任务_' + Date.now(), category: 'growth', childId: childId, energyValue: 5, minigameId: null, }); if (resp.ok || resp.data?.code === 200) { const taskId = resp.data?.data || resp.data?.id; pass('US-TASK-01 家长创建任务', `taskId=${taskId}`); // US-TASK-02: 孩子查看今日任务 try { const listResp = await parentApi('/api/tasks/today', { childId: childId }); const tasks = listResp.data?.data || []; pass('US-TASK-02 孩子查看今日任务', `tasks=${tasks.length}`); } catch (err) { fail('US-TASK-02 孩子查看今日任务', err); } // US-TASK-03: 孩子完成任务 try { const completeResp = await parentApi(`/api/tasks/${taskId}/complete`, { childId: childId, photoUrl: '', }); if (completeResp.ok || completeResp.data?.code === 200) { pass('US-TASK-03 孩子完成任务', `taskId=${taskId}`); } else { fail('US-TASK-03 孩子完成任务', new Error(`code=${completeResp.data?.code}`)); } } catch (err) { fail('US-TASK-03 孩子完成任务', err); } } else { fail('US-TASK-01 家长创建任务', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-TASK-01 家长创建任务', err); } // US-TASK-04: 家长审核任务(先获取待审核) try { const reviewResp = await parentApi('/api/tasks/pending-review', {}); const pending = reviewResp.data?.data || []; if (pending.length > 0) { const task = pending[0]; const approveResp = await parentApi(`/api/tasks/${task.id}/review`, { approved: true, energyAward: 5, comment: '测试审核通过', reviewNote: '', }); if (approveResp.ok || approveResp.data?.code === 200) { pass('US-TASK-04 家长审核任务'); } else { fail('US-TASK-04 家长审核任务', new Error(`code=${approveResp.data?.code}`)); } } else { pass('US-TASK-04 家长审核任务', '无待审核任务(跳过)'); } } catch (err) { fail('US-TASK-04 家长审核任务', err); } // US-TASK-11: 获取小游戏选项 try { const resp = await parentApi('/api/tasks/minigame-options', {}); const games = resp.data?.data || []; pass('US-TASK-11 获取小游戏选项', `games=${games.length}`); } catch (err) { fail('US-TASK-11 获取小游戏选项', err); } } // ─── US-WISH: 心愿管理 ──────────────────────────────────────────────────────── async function testWishManagement() { log('=== US-WISH: 心愿管理 ==='); // US-WISH-01: 孩子创建心愿(用parent token,role=child) let wishId = null; try { const resp = await parentApi('/api/wish/create', { title: '测试心愿_' + Date.now(), description: '自动化测试创建的心愿', category: 'toy', }); if (resp.ok || resp.data?.code === 200) { wishId = resp.data?.data || resp.data?.id; pass('US-WISH-01 孩子创建心愿', `wishId=${wishId}`); } else { fail('US-WISH-01 孩子创建心愿', new Error(`code=${resp.data?.code} msg=${resp.data?.message || ''}`)); } } catch (err) { fail('US-WISH-01 孩子创建心愿', err); } if (!wishId) return; // US-WISH-02: 家长定价心愿 try { const resp = await parentApi(`/api/wish/${wishId}/set-price`, { pointsRequired: 100, }); if (resp.ok || resp.data?.code === 200) { pass('US-WISH-02 家长定价心愿', '100积分'); } else { fail('US-WISH-02 家长定价心愿', new Error(`code=${resp.data?.code} msg=${resp.data?.message || ''}`)); } } catch (err) { fail('US-WISH-02 家长定价心愿', err); } // US-WISH-06: 查看心愿列表 try { const resp = await parentApi('/api/wish/list', {}); const wishes = resp.data?.data || []; if (Array.isArray(wishes)) { pass('US-WISH-06 查看心愿列表', `count=${wishes.length}`); } else { pass('US-WISH-06 查看心愿列表', `type=${typeof resp.data}`); } } catch (err) { fail('US-WISH-06 查看心愿列表', err); } } // ─── US-POINTS: 积分系统 ───────────────────────────────────────────────────── async function testPointsSystem() { log('=== US-POINTS: 积分系统 ==='); if (!childId) { testResults.push({ name: 'US-POINTS-01 查看积分余额', result: 'SKIP', detail: '无有效childId', time: Date.now() }); testResults.push({ name: 'US-POINTS-02 查看积分流水', result: 'SKIP', detail: '无有效childId', time: Date.now() }); return; } // US-POINTS-01: 查看积分余额 try { const resp = await parentApi('/api/points/balance', { childId: childId }); if (resp.ok || resp.data?.code === 200) { const balance = resp.data?.data || {}; pass('US-POINTS-01 查看积分余额', JSON.stringify(balance).substring(0, 50)); } else { fail('US-POINTS-01 查看积分余额', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-POINTS-01 查看积分余额', err); } // US-POINTS-02: 查看积分流水 try { const resp = await parentApi('/api/points/logs', { childId: childId, page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { pass('US-POINTS-02 查看积分流水', 'ok'); } else { fail('US-POINTS-02 查看积分流水', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-POINTS-02 查看积分流水', err); } } // ─── US-ENERGY: 能量系统 ───────────────────────────────────────────────────── async function testEnergySystem() { log('=== US-ENERGY: 能量系统 ==='); // US-ENERGY-01: 能量总览 try { const resp = await parentApi('/api/energy/overview', {}); if (resp.ok || resp.data?.code === 200) { const data = resp.data?.data || {}; pass('US-ENERGY-01 能量总览', JSON.stringify(data).substring(0, 80)); } else if (resp.status === 400) { fail('US-ENERGY-01 能量总览', new Error('400 Bad Request'), 'ISSUE-006 根因确认: children表为空'); } else { fail('US-ENERGY-01 能量总览', new Error(`status=${resp.status} code=${resp.data?.code}`)); } } catch (err) { fail('US-ENERGY-01 能量总览', err); } // US-ENERGY-02: 能量流水 try { const resp = await parentApi('/api/energy/logs', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { pass('US-ENERGY-02 能量流水', 'ok'); } else if (resp.status === 500) { fail('US-ENERGY-02 能量流水', new Error('500 Internal Server Error')); } else { fail('US-ENERGY-02 能量流水', new Error(`status=${resp.status}`)); } } catch (err) { fail('US-ENERGY-02 能量流水', err); } } // ─── US-CHECKIN: 打卡 ───────────────────────────────────────────────────────── async function testCheckin() { log('=== US-CHECKIN: 打卡与连续 ==='); if (!childId) { testResults.push({ name: 'US-CHECKIN-01 健康打卡', result: 'SKIP', detail: '无有效childId', time: Date.now() }); return; } // US-CHECKIN-01: 健康打卡 try { const resp = await parentApi('/api/health/checkin/create', { childId: childId, content: '自动化测试打卡_' + Date.now(), dimension: 'body', }); if (resp.ok || resp.data?.code === 200) { pass('US-CHECKIN-01 健康打卡', `id=${resp.data?.data?.id || 'ok'}`); } else if (resp.status === 500) { fail('US-CHECKIN-01 健康打卡', new Error('500 Internal Server Error')); } else { fail('US-CHECKIN-01 健康打卡', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-CHECKIN-01 健康打卡', err); } // US-CHECKIN-02: 打卡列表 try { const resp = await parentApi('/api/health/checkin/list', { childId: childId }); if (resp.ok || resp.data?.code === 200) { const list = resp.data?.data || []; pass('US-CHECKIN-02 打卡列表', `count=${Array.isArray(list) ? list.length : 'N/A'}`); } else { fail('US-CHECKIN-02 打卡列表', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-CHECKIN-02 打卡列表', err); } // US-CHECKIN-03: 打卡统计 try { const resp = await parentApi('/api/health/checkin/stats', { childId: childId }); if (resp.ok || resp.data?.code === 200) { pass('US-CHECKIN-03 打卡统计', 'ok'); } else if (resp.status === 400) { fail('US-CHECKIN-03 打卡统计', new Error('400 Bad Request'), 'ISSUE-004 根因确认: children表为空'); } else { fail('US-CHECKIN-03 打卡统计', new Error(`status=${resp.status}`)); } } catch (err) { fail('US-CHECKIN-03 打卡统计', err); } // US-CHECKIN-04: 连续打卡进度 try { const resp = await parentApi(`/api/streak/progress/${childId}`, {}); if (resp.ok || resp.data?.code === 200) { pass('US-CHECKIN-04 连续打卡进度', 'ok'); } else { fail('US-CHECKIN-04 连续打卡进度', new Error(`status=${resp.status}`)); } } catch (err) { fail('US-CHECKIN-04 连续打卡进度', err); } } // ─── US-ARTICLE: 文章管理 ────────────────────────────────────────────────────── async function testArticleManagement() { log('=== US-ARTICLE: 文章管理 ==='); // US-ARTICLE-07: 用户浏览文章列表 try { const resp = await api('/api/articles/list', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { const articles = resp.data?.data?.records || resp.data?.data || []; pass('US-ARTICLE-07 文章列表浏览', `count=${Array.isArray(articles) ? articles.length : 'N/A'}`); } else { fail('US-ARTICLE-07 文章列表浏览', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ARTICLE-07 文章列表浏览', err); } // US-ARTICLE-01: 管理员创建文章 try { const resp = await adminApi('/api/admin/articles/create', { title: '自动化测试文章_' + Date.now(), content: '这是自动化测试创建的文章内容。', categoryId: null, summary: '测试摘要', tags: '自动化测试', author: '自动化测试', readTime: 5, relatedDimensions: 'body', visibility: 'public', articleType: 'knowledge', status: 'draft', }); if (resp.ok || resp.data?.code === 200) { const articleId = resp.data?.data?.id || 0; pass('US-ARTICLE-01 管理员创建文章', `id=${articleId}`); // US-ARTICLE-02: 发布文章 try { const pubResp = await adminApi('/api/admin/articles/publish', { id: articleId, status: 'published' }); if (pubResp.ok || pubResp.data?.code === 200) { pass('US-ARTICLE-02 发布文章', `id=${articleId}`); } else { fail('US-ARTICLE-02 发布文章', new Error(`code=${pubResp.data?.code}`)); } } catch (err) { fail('US-ARTICLE-02 发布文章', err); } } else { fail('US-ARTICLE-01 管理员创建文章', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ARTICLE-01 管理员创建文章', err); } } // ─── US-ACTIVITY: 活动报名 ──────────────────────────────────────────────────── async function testActivityManagement() { log('=== US-ACTIVITY: 活动管理 ==='); // US-ACT-01: 管理员创建活动 let activityId = null; try { const resp = await adminApi('/api/activity/create', { title: '自动化测试活动_' + Date.now(), description: '自动化E2E测试创建的活动', dimensionCode: 'body', startTime: new Date(Date.now() + 86400000 * 7).toISOString(), endTime: new Date(Date.now() + 86400000 * 8).toISOString(), location: '线上', maxParticipants: 20, }); if (resp.ok || resp.data?.code === 200) { activityId = resp.data?.data?.id || resp.data?.id; pass('US-ACT-01 创建活动', `id=${activityId}`); } else { fail('US-ACT-01 创建活动', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ACT-01 创建活动', err); } if (!activityId) return; // US-ACT-02: 发布活动 try { const resp = await adminApi('/api/activity/publish', { id: activityId }); if (resp.ok || resp.data?.code === 200) { pass('US-ACT-02 发布活动', `id=${activityId}`); } else { fail('US-ACT-02 发布活动', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ACT-02 发布活动', err); } // US-ACT-03: 活动列表浏览 try { const resp = await api('/api/activity/list', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { pass('US-ACT-03 活动列表浏览', 'ok'); } else { fail('US-ACT-03 活动列表浏览', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ACT-03 活动列表浏览', err); } // US-ACT-04: 家长为孩子报名活动 if (childId) { try { const resp = await parentApi('/api/activity/register', { id: activityId, childId: childId }); if (resp.ok || resp.data?.code === 200) { pass('US-ACT-04 家长为孩子报名', `activityId=${activityId} childId=${childId}`); } else { fail('US-ACT-04 家长为孩子报名', new Error(`code=${resp.data?.code} msg=${resp.data?.message || ''}`)); } } catch (err) { fail('US-ACT-04 家长为孩子报名', err); } } else { testResults.push({ name: 'US-ACT-04 家长为孩子报名', result: 'SKIP', detail: '无有效childId', time: Date.now() }); } } // ─── US-ADMIN: 后台管理 ─────────────────────────────────────────────────────── async function testAdminManagement() { log('=== US-ADMIN: 后台管理 ==='); // US-ADMIN-01: 创建用户 try { const resp = await adminApi('/api/admin/users/create', { phone: '139' + String(Math.floor(Math.random() * 1e9)).padStart(9, '0'), password: 'Test123456', name: '自动化测试用户', role: 'parent', }); if (resp.ok || resp.data?.code === 200) { pass('US-ADMIN-01 创建用户', `code=200 ok`); } else if (resp.data?.code === 500) { fail('US-ADMIN-01 创建用户', new Error('500 - ISSUE-003回归'), 'ISSUE-003待确认是否已修复'); } else { fail('US-ADMIN-01 创建用户', new Error(`code=${resp.data?.code} msg=${resp.data?.message || ''}`)); } } catch (err) { fail('US-ADMIN-01 创建用户', err); } // US-ADMIN-02: 用户列表 try { const resp = await adminApi('/api/admin/users', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { pass('US-ADMIN-02 用户列表', 'ok'); } else { fail('US-ADMIN-02 用户列表', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ADMIN-02 用户列表', err); } // US-ADMIN-04: SKU管理(商品SKU列表) try { const resp = await adminApi('/api/admin/product/sku/list', { productId: 1 }); if (resp.ok || resp.data?.code === 200) { pass('US-ADMIN-04 SKU列表', 'ok'); } else { fail('US-ADMIN-04 SKU列表', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ADMIN-04 SKU列表', err); } // US-ADMIN-06: 数据迁移 try { const resp = await adminApi('/api/migration/run', {}); if (resp.ok || resp.data?.code === 200) { pass('US-ADMIN-06 数据迁移', 'ok'); } else { fail('US-ADMIN-06 数据迁移', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ADMIN-06 数据迁移', err); } } // ─── US-PROD: 商品管理 ──────────────────────────────────────────────────────── async function testProductManagement() { log('=== US-PROD: 商品管理 ==='); // US-PROD-08: 用户浏览商品 try { const resp = await api('/api/product/list', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { pass('US-PROD-08 商品列表浏览', 'ok'); } else { fail('US-PROD-08 商品列表浏览', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-PROD-08 商品列表浏览', err); } } // ─── US-ENERGYRULE: 能量规则 ───────────────────────────────────────────────── async function testEnergyRule() { log('=== US-ENERGYRULE: 能量规则 ==='); // US-ENERGYRULE-01: 查看能量规则 try { const resp = await adminApi('/api/admin/energy-rule/list', { page: 1, size: 10 }); if (resp.ok || resp.data?.code === 200) { const rules = resp.data?.data?.records || resp.data?.data || []; pass('US-ENERGYRULE-01 查看能量规则', `rules=${Array.isArray(rules) ? rules.length : 'N/A'}`); } else { fail('US-ENERGYRULE-01 查看能量规则', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ENERGYRULE-01 查看能量规则', err); } // US-ENERGYRULE-02: 创建能量规则 try { const resp = await adminApi('/api/admin/energy-rule/create', { dimensionCode: 'body', eventType: 'checkin', energyValue: 5, status: 1, }); if (resp.ok || resp.data?.code === 200) { pass('US-ENERGYRULE-02 创建能量规则', 'ok'); } else { fail('US-ENERGYRULE-02 创建能量规则', new Error(`code=${resp.data?.code}`)); } } catch (err) { fail('US-ENERGYRULE-02 创建能量规则', err); } } // ─── 主函数 ─────────────────────────────────────────────────────────────────── async function main() { log(`CFC API Integration Test — ${new Date().toISOString()}`); log(`测试环境: ${BASE}`); log('═'.repeat(60)); await login(); await testFamilyManagement(); await testTaskManagement(); await testWishManagement(); await testPointsSystem(); await testEnergySystem(); await testCheckin(); await testArticleManagement(); await testActivityManagement(); await testAdminManagement(); await testProductManagement(); await testEnergyRule(); // ─── 汇总 ──────────────────────────────────────────────────────────────── const elapsed = Date.now() - testStartTime; const passed = testResults.filter(r => r.result === 'PASS').length; const failed = testResults.filter(r => r.result === 'FAIL').length; const skipped = testResults.filter(r => r.result === 'SKIP').length; const total = testResults.length; log('═'.repeat(60)); log(`测试完成: ${total} 用例 | ✅ ${passed} | ❌ ${failed} | ⏭️ ${skipped} | 耗时 ${elapsed}ms`); // 输出markdown格式结果 const lines = [ `# API集成测试结果 — ${new Date().toLocaleDateString('zh-CN')}`, ``, `**测试环境**: ${BASE}`, `**测试时间**: ${new Date().toLocaleString('zh-CN')}`, `**耗时**: ${elapsed}ms`, ``, `## 结果汇总`, ``, `| 结果 | 数量 |`, `|------|------|`, `| ✅ PASS | ${passed} |`, `| ❌ FAIL | ${failed} |`, `| ⏭️ SKIP | ${skipped} |`, `| **总计** | **${total}** |`, ``, `## 详细结果`, ``, `| 用例ID | 结果 | 说明 |`, `|--------|------|------|`, ]; for (const r of testResults) { const icon = r.result === 'PASS' ? '✅' : r.result === 'FAIL' ? '❌' : '⏭️'; lines.push(`| ${r.name} | ${icon} ${r.result} | ${r.detail || ''} |`); } const report = lines.join('\n'); console.log('\n' + report); // 写入文件 const fs = require('fs'); const path = require('path'); const outFile = path.join(__dirname, '..', 'docs', '系统测试', 'test-records', 'API-TEST-RESULTS.md'); fs.writeFileSync(outFile, report, 'utf8'); log(`\n报告已保存: ${outFile}`); process.exit(failed > 0 ? 1 : 0); } main().catch(err => { console.error('测试异常:', err); process.exit(1); });