/** * 后端 API 调用助手(Node.js HTTP 直接调用,含 HMAC 签名) * 支持带签名和不带签名两种模式 */ const http = require('http'); const crypto = require('crypto'); const BASE_URL = 'http://192.168.110.64:8084'; const API_BASE = '/api'; const SIGNATURE_SECRET = 'NovalonManageSystemSecretKey2026'; const ENABLE_SIGNATURE = true; // HMAC 签名 function generateSignature(method, path, body, timestamp, nonce) { const stringToSign = [method, path, '', body || '', String(timestamp), nonce].join('\n'); return crypto.createHmac('sha256', SIGNATURE_SECRET).update(stringToSign).digest('base64'); } function generateNonce() { return Date.now().toString(36) + '-' + Math.random().toString(36).substring(2, 15); } function makeRequest(method, url, body, token) { return new Promise((resolve, reject) => { const urlObj = new URL(url, BASE_URL); const path = urlObj.pathname + urlObj.search; const bodyStr = body ? JSON.stringify(body) : ''; const headers = { 'Content-Type': 'application/json' }; if (ENABLE_SIGNATURE) { const timestamp = Date.now(); const nonce = generateNonce(); const signature = generateSignature(method.toUpperCase(), path, bodyStr, timestamp, nonce); headers['X-Signature'] = signature; headers['X-Timestamp'] = String(timestamp); headers['X-Nonce'] = nonce; } if (token) { headers['Authorization'] = token.startsWith('Bearer ') ? token : 'Bearer ' + token; } const options = { hostname: urlObj.hostname, port: urlObj.port, path: path, method: method.toUpperCase(), headers: headers }; const req = http.request(options, (res) => { let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { try { const parsed = JSON.parse(data); resolve({ status: res.statusCode, data: parsed }); } catch (e) { resolve({ status: res.statusCode, data: data }); } }); }); req.on('error', (err) => reject(err)); req.setTimeout(10000, () => { req.destroy(); reject(new Error('Request timeout')); }); if (bodyStr) req.write(bodyStr); req.end(); }); } function isSuccess(response, expectedStatus = 200) { return response.status === expectedStatus; } async function adminLogin(password) { console.log('[API] 管理员登录...'); const res = await makeRequest('POST', `${API_BASE}/system/auth/login`, { username: 'admin', password: password || 'admin' }); console.log(`[API] 登录响应: ${res.status}`); if (res.data && res.data.data && res.data.data.token) { return res.data.data.token; } if (res.data && res.data.token) { return res.data.token; } return null; } async function searchCourses(keyword, token) { console.log(`[API] 搜索课程: "${keyword}"`); const res = await makeRequest('POST', `${API_BASE}/groupCourse/page`, { page: 0, size: 50 }, token); console.log(`[API] 搜索结果: status=${res.status}, total=${res.data?.data?.totalElements || res.data?.totalElements || '?'}`); return res; } async function searchByKeyword(keyword, token) { console.log(`[API] 按关键词搜索: "${keyword}"`); const res = await makeRequest('POST', `${API_BASE}/groupCourse/page`, { page: 0, size: 50 }, token); console.log(`[API] 搜索结果: status=${res.status}, total=${res.data?.data?.totalElements || res.data?.totalElements || '?'}`); return res; } async function getCourseDetail(courseId, token) { console.log(`[API] 获取课程详情: id=${courseId}`); const res = await makeRequest('GET', `${API_BASE}/groupCourse/${courseId}/detail`, null, token); console.log(`[API] 课程详情: status=${res.status}`); return res; } async function getCourseTypes() { console.log('[API] 获取课程类型...'); const res = await makeRequest('GET', `${API_BASE}/groupCourse/types`); console.log(`[API] 课程类型: status=${res.status}, count=${Array.isArray(res.data) ? res.data.length : '?'}`); return res; } async function getActiveRecommendations() { console.log('[API] 获取活跃推荐课程...'); const res = await makeRequest('GET', `${API_BASE}/groupCourse/recommend/active`); console.log(`[API] 推荐课程: status=${res.status}`); return res; } // ── 会员端 API ── /** 会员小程序登录,返回 {memberId, accessToken} */ async function memberLogin() { console.log('[API] 会员登录 (miniappLogin)...'); const res = await makeRequest('POST', `${API_BASE}/member/auth/miniapp/login`, { code: 'dev-test-code-' + Date.now() }); console.log(`[API] 会员登录响应: status=${res.status}`); if (res.status === 200 && res.data) { return { memberId: res.data.memberId, accessToken: res.data.accessToken }; } return null; } /** 预约课程 */ async function bookCourse(courseId, memberId, token) { console.log(`[API] 预约课程: courseId=${courseId}, memberId=${memberId}`); const res = await makeRequest('POST', `${API_BASE}/groupCourse/book`, { courseId, memberId }, token); console.log(`[API] 预约结果: status=${res.status}`); return res; } /** 获取会员的预约列表 */ async function getMemberBookings(memberId, token) { console.log(`[API] 获取会员预约: memberId=${memberId}`); const res = await makeRequest('GET', `${API_BASE}/groupCourse/bookings/member/${memberId}`, null, token); console.log(`[API] 预约列表: status=${res.status}`); return res; } /** 扫码签到 */ async function signIn(courseId, memberId, token) { console.log(`[API] 签到: courseId=${courseId}, memberId=${memberId}`); const res = await makeRequest('POST', `${API_BASE}/groupCourse/signin/${memberId}`, { courseId }, token); console.log(`[API] 签到结果: status=${res.status}`); return res; } module.exports = { makeRequest, adminLogin, searchCourses, searchByKeyword, getCourseDetail, getCourseTypes, getActiveRecommendations, memberLogin, bookCourse, getMemberBookings, signIn, isSuccess, BASE_URL, API_BASE };