// test-all.js —— 手动运行,逐接口验证全部 CRUD // 用法: node test-all.js require('dotenv').config() const http = require('http') const BASE = 'http://127.0.0.1:3000' let token = '' let failCount = 0 let passCount = 0 // ========== 工具函数 ========== function req(method, path, body, useToken = true) { return new Promise((resolve, reject) => { const u = new URL(path, BASE) const opts = { hostname: u.hostname, port: u.port, path: u.pathname + u.search, method, headers: { 'Content-Type': 'application/json' }, } if (useToken && token) opts.headers['Authorization'] = 'Bearer ' + token const r = http.request(opts, (res) => { let d = '' res.on('data', (c) => (d += c)) res.on('end', () => { try { resolve({ status: res.statusCode, body: JSON.parse(d) }) } catch { resolve({ status: res.statusCode, body: d.substring(0, 300) }) } }) }) r.on('error', reject) if (body) r.write(JSON.stringify(body)) r.end() }) } function check(label, res, expectStatus, expectInfo) { const ok = res.status === expectStatus if (ok) { passCount++ console.log(` ✅ ${label}`) } else { failCount++ console.log(` ❌ ${label} 预期 HTTP ${expectStatus}, 实际 HTTP ${res.status}`) console.log(` 响应: ${JSON.stringify(res.body).substring(0, 200)}`) } if (expectInfo) console.log(` ↳ ${expectInfo}`) } // ========== 主流程 ========== async function main() { console.log('╔══════════════════════════════════════════╗') console.log('║ 企业管理系统 — 接口全量测试 ║') console.log('╚══════════════════════════════════════════╝') console.log(`服务地址: ${BASE}\n`) // ────────── 1. 登录 ────────── console.log('━'.repeat(50)) console.log('【1】 用户登录模块') console.log('━'.repeat(50)) let r = await req('POST', '/api/user/login', { username: 'admin', password: '123456' }, false) check('POST /api/user/login (正确密码)', r, 200) if (r.body?.data?.token) { token = r.body.data.token console.log(` ↳ 角色: ${r.body.data.userInfo.role}, 姓名: ${r.body.data.userInfo.real_name}`) } r = await req('POST', '/api/user/login', { username: 'admin', password: 'wrong' }, false) check('POST /api/user/login (错误密码)', r, 400, '→ 账号或密码错误') r = await req('POST', '/api/user/login', { username: '', password: '' }, false) check('POST /api/user/login (空参数)', r, 400, '→ 用户名和密码必填') // ────────── 2. 个人信息 ────────── console.log('\n━'.repeat(50)) console.log('【2】 个人信息 & 改密') console.log('━'.repeat(50)) r = await req('GET', '/api/user/info') check('GET /api/user/info', r, 200) if (r.body?.data) console.log(` ↳ 用户名: ${r.body.data.username}, 角色: ${r.body.data.role}`) r = await req('POST', '/api/user/logout') check('POST /api/user/logout', r, 200, '→ JWT 无状态,前端删 token 即可') r = await req('PUT', '/api/user/password', { oldPassword: 'wrong', newPassword: 'newpwd999' }) check('PUT /api/user/password (旧密码错)', r, 400, '→ 旧密码错误') r = await req('PUT', '/api/user/password', { oldPassword: '123456', newPassword: '123456' }) check('PUT /api/user/password (新旧相同)', r, 400, '→ 新密码不能与旧密码相同') r = await req('PUT', '/api/user/password', { oldPassword: '123456', newPassword: '123' }) check('PUT /api/user/password (新密码太短)', r, 400, '→ 新密码至少 6 位') // ────────── 3. 用户管理 CRUD ────────── console.log('\n━'.repeat(50)) console.log('【3】 用户管理 CRUD(管理员)') console.log('━'.repeat(50)) r = await req('GET', '/api/users?page=1&pageSize=10') check('GET /api/users (列表)', r, 200) if (r.body?.data) console.log(` ↳ 共 ${r.body.data.total} 个用户, 本页 ${r.body.data.list?.length} 条`) const ts = Date.now() const newUser = `tester_${ts}` r = await req('POST', '/api/users', { username: newUser, password: 'pass123', real_name: '测试员', role: 'user' }) check('POST /api/users (创建)', r, 200) let userId = r.body?.data?.id if (userId) console.log(` ↳ 新建用户 ID: ${userId}, 用户名: ${newUser}`) r = await req('POST', '/api/users', { username: 'admin', password: '123456' }) check('POST /api/users (重复用户名)', r, 409, '→ 用户名已存在') r = await req('POST', '/api/users', { username: 'bad', password: '12', role: 'superman' }) check('POST /api/users (非法角色)', r, 400, '→ 角色只能为 admin 或 user') if (userId) { r = await req('GET', '/api/users/' + userId) check('GET /api/users/:id (详情)', r, 200) r = await req('PUT', '/api/users/' + userId, { real_name: '改名后的测试员', status: 1 }) check('PUT /api/users/:id (更新姓名)', r, 200) if (r.body?.data) console.log(` ↳ 新姓名: ${r.body.data.real_name}`) r = await req('PUT', '/api/users/' + userId, { password: 'newpass456' }) check('PUT /api/users/:id (管理员重置密码)', r, 200, '→ 管理员可直接改他人密码') // 用新用户登录验证改密成功 const r2 = await req('POST', '/api/user/login', { username: newUser, password: 'newpass456' }, false) check(' └ 新用户用新密码登录', r2, 200) // 测试非管理员访问 const userToken = r2.body?.data?.token if (userToken) { const oldToken = token token = userToken r = await req('GET', '/api/users') check(' └ 普通用户访问用户列表', r, 403, '→ 需要管理员权限') token = oldToken } r = await req('DELETE', '/api/users/' + userId) check('DELETE /api/users/:id (删除)', r, 200) } r = await req('DELETE', '/api/users/1') check('DELETE /api/users/1 (删自己)', r, 400, '→ 不能删除自己') r = await req('GET', '/api/users', null, false) check('GET /api/users (无 token)', r, 401, '→ 未登录') // ────────── 4. 客户管理 ────────── console.log('\n━'.repeat(50)) console.log('【4】 客户管理 CRUD') console.log('━'.repeat(50)) r = await req('GET', '/api/customers?page=1&pageSize=10') check('GET /api/customers (列表)', r, 200) if (r.body?.data) console.log(` ↳ 共 ${r.body.data.total} 条`) r = await req('POST', '/api/customers', { name: '测试加盟商', phone: '13800001111', province: '广东', city: '东莞', district: '松山湖', address: '科技路88号', customer_type: 'VIP', email: 'test@test.com' }) check('POST /api/customers (创建VIP客户)', r, 200) let custId = r.body?.data?.id if (custId) console.log(` ↳ 新建客户 ID: ${custId}, 类型: ${r.body.data.customer_type}`) r = await req('POST', '/api/customers', { name: '' }) check('POST /api/customers (缺姓名)', r, 400, '→ 客户姓名必填') // 不传 customer_type 应为默认 Normal r = await req('POST', '/api/customers', { name: '默认类型测试' }) check('POST /api/customers (不传类型默认Normal)', r, 200) if (r.body?.data) console.log(` ↳ 默认类型: ${r.body.data.customer_type}`) if (r.body?.data?.id) await req('DELETE', '/api/customers/' + r.body.data.id) if (custId) { r = await req('GET', '/api/customers/' + custId) check('GET /api/customers/:id (详情)', r, 200) r = await req('PUT', '/api/customers/' + custId, { phone: '13900002222', customer_type: 'Normal', remark: '更新备注' }) check('PUT /api/customers/:id (更新类型+电话)', r, 200) if (r.body?.data) console.log(` ↳ 更新后类型: ${r.body.data.customer_type}, 电话: ${r.body.data.phone}`) r = await req('GET', '/api/customers?name=测试') check('GET /api/customers?name=测试 (按姓名搜索)', r, 200) r = await req('GET', '/api/customers?customer_type=Normal') check('GET /api/customers?customer_type=Normal (按类型筛选)', r, 200) if (r.body?.data) console.log(` ↳ Normal 类型共 ${r.body.data.total} 条`) r = await req('DELETE', '/api/customers/' + custId) check('DELETE /api/customers/:id (删除)', r, 200) } r = await req('GET', '/api/customers/99999') check('GET /api/customers/99999 (不存在)', r, 404, '→ 客户不存在') // ────────── 5. 员工管理 ────────── console.log('\n━'.repeat(50)) console.log('【5】 员工管理 CRUD') console.log('━'.repeat(50)) r = await req('GET', '/api/employees?page=1&pageSize=10') check('GET /api/employees (列表)', r, 200) r = await req('POST', '/api/employees', { name: '测试员工', gender: '男', age: 30, education: '本科', department: '研发部', position: '工程师', salary: 15000, phone: '13700000001', email: 'emp@test.com' }) check('POST /api/employees (创建)', r, 200) let empId = r.body?.data?.id if (empId) console.log(` ↳ 新建员工 ID: ${empId}, 部门: ${r.body.data.department}`) if (empId) { r = await req('GET', '/api/employees/' + empId) check('GET /api/employees/:id (详情)', r, 200) r = await req('PUT', '/api/employees/' + empId, { salary: 18000, position: '高级工程师' }) check('PUT /api/employees/:id (更新)', r, 200) r = await req('GET', '/api/employees?status=1') check('GET /api/employees?status=1 (在职筛选)', r, 200) r = await req('DELETE', '/api/employees/' + empId) check('DELETE /api/employees/:id (删除)', r, 200) } // ────────── 6. 产品管理 ────────── console.log('\n━'.repeat(50)) console.log('【6】 产品管理 CRUD') console.log('━'.repeat(50)) r = await req('GET', '/api/products?page=1&pageSize=10') check('GET /api/products (列表)', r, 200) r = await req('POST', '/api/products', { name: '测试产品-X1', type: '电子产品', quantity: 500, price: 299.99, unit: '台', specification: '型号 X1-2026', supplier: '供应商A' }) check('POST /api/products (创建)', r, 200) let prodId = r.body?.data?.id if (prodId) console.log(` ↳ 新建产品 ID: ${prodId}, 库存: ${r.body.data.quantity}`) if (prodId) { r = await req('GET', '/api/products/' + prodId) check('GET /api/products/:id (详情)', r, 200) r = await req('PUT', '/api/products/' + prodId, { price: 259.99, quantity: 480 }) check('PUT /api/products/:id (更新价格库存)', r, 200) r = await req('GET', '/api/products?name=测试') check('GET /api/products?name=测试 (搜索)', r, 200) r = await req('DELETE', '/api/products/' + prodId) check('DELETE /api/products/:id (删除)', r, 200) } // ────────── 7. 合同管理 ────────── console.log('\n━'.repeat(50)) console.log('【7】 合同管理 CRUD(关联客户+员工)') console.log('━'.repeat(50)) // 先创建客户和员工做外键 const cRes = await req('POST', '/api/customers', { name: '合同测试客户' }) const eRes = await req('POST', '/api/employees', { name: '合同业务员' }) const cId = cRes.body?.data?.id const eId = eRes.body?.data?.id console.log(` ↳ 先创建客户(${cId}) 和 员工(${eId}) 供合同关联`) r = await req('POST', '/api/contracts', { customer_id: cId, contract_name: '年度供货协议', contract_no: 'HT-2026-088', amount: 500000, effective_date: '2026-06-01', expiry_date: '2027-05-31', employee_id: eId, status: '生效' }) check('POST /api/contracts (创建)', r, 200) let conId = r.body?.data?.id if (conId) { console.log(` ↳ 新建合同 ID: ${conId}`) if (r.body?.data?.customer_name) console.log(` ↳ 联查客户: ${r.body.data.customer_name}, 业务员: ${r.body.data.employee_name}`) } r = await req('POST', '/api/contracts', { customer_id: 99999, contract_name: '无效合同' }) check('POST /api/contracts (客户不存在)', r, 400, '→ 关联客户不存在') if (conId) { r = await req('GET', '/api/contracts') check('GET /api/contracts (列表)', r, 200) r = await req('GET', '/api/contracts/' + conId) check('GET /api/contracts/:id (详情)', r, 200) r = await req('PUT', '/api/contracts/' + conId, { status: '完成', remark: '已履约' }) check('PUT /api/contracts/:id (更新状态)', r, 200) r = await req('GET', '/api/contracts?status=完成') check('GET /api/contracts?status=完成 (状态筛选)', r, 200) r = await req('DELETE', '/api/contracts/' + conId) check('DELETE /api/contracts/:id (删除)', r, 200) } // 清理外键依赖数据 if (cId) await req('DELETE', '/api/customers/' + cId) if (eId) await req('DELETE', '/api/employees/' + eId) // ────────── 8. 售后管理 ────────── console.log('\n━'.repeat(50)) console.log('【8】 售后管理 CRUD(关联客户+员工)') console.log('━'.repeat(50)) const c2 = await req('POST', '/api/customers', { name: '售后测试客户' }) const e2 = await req('POST', '/api/employees', { name: '售后处理员' }) const cId2 = c2.body?.data?.id const eId2 = e2.body?.data?.id console.log(` ↳ 先创建客户(${cId2}) 和 员工(${eId2})`) r = await req('POST', '/api/after-sales', { customer_id: cId2, feedback: '设备运行异常,需技术支持', employee_id: eId2, handle_method: '更换故障模块', handle_status: '处理中', service_date: '2026-06-20' }) check('POST /api/after-sales (创建)', r, 200) let asId = r.body?.data?.id if (asId) { console.log(` ↳ 新建售后 ID: ${asId}`) if (r.body?.data?.customer_name) console.log(` ↳ 联查客户: ${r.body.data.customer_name}, 处理人: ${r.body.data.employee_name}`) } r = await req('POST', '/api/after-sales', { customer_id: 99999, feedback: '测试' }) check('POST /api/after-sales (客户不存在)', r, 400, '→ 关联客户不存在') if (asId) { r = await req('GET', '/api/after-sales') check('GET /api/after-sales (列表)', r, 200) r = await req('GET', '/api/after-sales/' + asId) check('GET /api/after-sales/:id (详情)', r, 200) r = await req('PUT', '/api/after-sales/' + asId, { handle_status: '已完成', handle_method: '远程升级固件解决' }) check('PUT /api/after-sales/:id (更新)', r, 200) r = await req('GET', '/api/after-sales?handle_status=已完成') check('GET /api/after-sales?handle_status=已完成 (状态筛选)', r, 200) r = await req('DELETE', '/api/after-sales/' + asId) check('DELETE /api/after-sales/:id (删除)', r, 200) } if (cId2) await req('DELETE', '/api/customers/' + cId2) if (eId2) await req('DELETE', '/api/employees/' + eId2) // ────────── 收尾 ────────── console.log('\n' + '═'.repeat(50)) console.log(`测试完成: 通过 ${passCount} / 失败 ${failCount}`) if (failCount > 0) { console.log('⚠️ 存在失败用例,请检查上方输出') } else { console.log('🎉 全部接口通过!') } console.log('═'.repeat(50)) // 优雅退出 process.exit(failCount > 0 ? 1 : 0) } main().catch((e) => { console.error('测试异常:', e.message) console.error('请确认服务已启动: node server.js') process.exit(1) })