新增e2e测试脚本,修复部分问题

This commit was merged in pull request #51.
This commit is contained in:
2026-07-22 20:00:13 +08:00
parent 53d1ce6fb2
commit 4c07ec5455
105 changed files with 21700 additions and 318 deletions
+192
View File
@@ -0,0 +1,192 @@
/**
* 后端 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
};