175 lines
6.1 KiB
JavaScript
175 lines
6.1 KiB
JavaScript
/**
|
||
* 生成1000个完整的账号数据(用于手动测试Stripe通过率)
|
||
*/
|
||
|
||
const AccountDataGenerator = require('./src/shared/libs/account-generator');
|
||
const CardGenerator = require('./src/shared/libs/card-generator');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
console.log('\n========== 生成1000个账号数据 ==========\n');
|
||
|
||
async function generate() {
|
||
const accountGen = new AccountDataGenerator();
|
||
const cardGen = new CardGenerator(); // 不连接数据库,只生成
|
||
|
||
const accounts = [];
|
||
const stats = {
|
||
total: 0,
|
||
success: 0,
|
||
failed: 0,
|
||
binDistribution: {}
|
||
};
|
||
|
||
console.log('开始生成...\n');
|
||
const startTime = Date.now();
|
||
|
||
for (let i = 0; i < 1000; i++) {
|
||
try {
|
||
// 生成账号
|
||
const account = accountGen.generateAccount();
|
||
|
||
// 生成卡号
|
||
const card = await cardGen.generate('unionpay');
|
||
|
||
// 组合数据
|
||
const fullAccount = {
|
||
id: i + 1,
|
||
email: account.email,
|
||
password: account.password,
|
||
firstName: account.firstName,
|
||
lastName: account.lastName,
|
||
cardNumber: card.number,
|
||
expMonth: card.month,
|
||
expYear: card.year,
|
||
cvv: card.cvv,
|
||
issuer: card.issuer,
|
||
country: card.country,
|
||
countryName: card.countryName
|
||
};
|
||
|
||
accounts.push(fullAccount);
|
||
stats.success++;
|
||
stats.total++;
|
||
|
||
// 统计BIN分布(前6位)
|
||
const bin6 = card.number.substring(0, 6);
|
||
stats.binDistribution[bin6] = (stats.binDistribution[bin6] || 0) + 1;
|
||
|
||
// 进度显示
|
||
if ((i + 1) % 100 === 0) {
|
||
console.log(` 进度: ${i + 1}/1000 (${((i + 1) / 10).toFixed(1)}%)`);
|
||
}
|
||
|
||
} catch (error) {
|
||
stats.failed++;
|
||
stats.total++;
|
||
console.log(` ❌ 第 ${i + 1} 个生成失败: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
const endTime = Date.now();
|
||
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
||
|
||
console.log(`\n✅ 生成完成!耗时: ${duration}秒`);
|
||
console.log(` 成功: ${stats.success}个`);
|
||
console.log(` 失败: ${stats.failed}个\n`);
|
||
|
||
// 保存为多种格式
|
||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
||
const outputDir = path.join(__dirname, 'generated-accounts');
|
||
|
||
// 创建输出目录
|
||
if (!fs.existsSync(outputDir)) {
|
||
fs.mkdirSync(outputDir, { recursive: true });
|
||
}
|
||
|
||
// 1. JSON格式(完整数据)
|
||
const jsonFile = path.join(outputDir, `accounts-1000-${timestamp}.json`);
|
||
fs.writeFileSync(jsonFile, JSON.stringify(accounts, null, 2), 'utf-8');
|
||
console.log(`✅ JSON文件: ${jsonFile}`);
|
||
|
||
// 2. CSV格式(便于Excel打开)
|
||
const csvFile = path.join(outputDir, `accounts-1000-${timestamp}.csv`);
|
||
const csvHeader = 'ID,Email,Password,FirstName,LastName,CardNumber,ExpMonth,ExpYear,CVV,Issuer,Country,CountryName\n';
|
||
const csvRows = accounts.map(a =>
|
||
`${a.id},${a.email},${a.password},${a.firstName},${a.lastName},${a.cardNumber},${a.expMonth},${a.expYear},${a.cvv},${a.issuer},${a.country},${a.countryName}`
|
||
).join('\n');
|
||
fs.writeFileSync(csvFile, csvHeader + csvRows, 'utf-8');
|
||
console.log(`✅ CSV文件: ${csvFile}`);
|
||
|
||
// 3. 可读格式(用于快速查看)
|
||
const txtFile = path.join(outputDir, `accounts-1000-${timestamp}.txt`);
|
||
let txtContent = '========== 1000个Windsurf测试账号 ==========\n\n';
|
||
txtContent += `生成时间: ${new Date().toLocaleString('zh-CN')}\n`;
|
||
txtContent += `总数: ${stats.success}个\n\n`;
|
||
txtContent += '=' .repeat(80) + '\n\n';
|
||
|
||
accounts.forEach((a, index) => {
|
||
txtContent += `【账号 ${a.id}】\n`;
|
||
txtContent += ` 邮箱: ${a.email}\n`;
|
||
txtContent += ` 密码: ${a.password}\n`;
|
||
txtContent += ` 姓名: ${a.firstName} ${a.lastName}\n`;
|
||
txtContent += ` 卡号: ${a.cardNumber}\n`;
|
||
txtContent += ` 有效期: ${a.expMonth}/${a.expYear}\n`;
|
||
txtContent += ` CVV: ${a.cvv}\n`;
|
||
txtContent += ` 银行: ${a.issuer}\n`;
|
||
txtContent += ` 国家: ${a.countryName} (${a.country})\n`;
|
||
txtContent += '\n';
|
||
|
||
if ((index + 1) % 50 === 0) {
|
||
txtContent += '-'.repeat(80) + '\n\n';
|
||
}
|
||
});
|
||
|
||
fs.writeFileSync(txtFile, txtContent, 'utf-8');
|
||
console.log(`✅ TXT文件: ${txtFile}`);
|
||
|
||
// 4. Stripe测试专用格式(卡号|有效期|CVV)
|
||
const stripeFile = path.join(outputDir, `stripe-test-cards-${timestamp}.txt`);
|
||
let stripeContent = '========== Stripe测试卡号列表 ==========\n\n';
|
||
stripeContent += '格式: 卡号|有效期|CVV|邮箱\n\n';
|
||
accounts.forEach(a => {
|
||
stripeContent += `${a.cardNumber}|${a.expMonth}/${a.expYear}|${a.cvv}|${a.email}\n`;
|
||
});
|
||
fs.writeFileSync(stripeFile, stripeContent, 'utf-8');
|
||
console.log(`✅ Stripe测试文件: ${stripeFile}`);
|
||
|
||
// 统计报告
|
||
console.log('\n========== 统计报告 ==========\n');
|
||
|
||
console.log('BIN分布(前6位):');
|
||
const sortedBins = Object.entries(stats.binDistribution)
|
||
.sort((a, b) => b[1] - a[1]);
|
||
|
||
sortedBins.forEach(([bin, count], index) => {
|
||
if (index < 5) {
|
||
console.log(` ${bin}: ${count}张 (${(count / stats.success * 100).toFixed(1)}%)`);
|
||
}
|
||
});
|
||
|
||
console.log(`\n唯一BIN数: ${sortedBins.length}个`);
|
||
console.log(`平均每个BIN: ${(stats.success / sortedBins.length).toFixed(1)}张`);
|
||
|
||
// 示例账号
|
||
console.log('\n========== 示例账号(前3个)==========\n');
|
||
accounts.slice(0, 3).forEach(a => {
|
||
console.log(`${a.id}. ${a.email} | ${a.password}`);
|
||
console.log(` 卡号: ${a.cardNumber} | ${a.expMonth}/${a.expYear} | CVV: ${a.cvv}`);
|
||
console.log(` 国家: ${a.countryName}\n`);
|
||
});
|
||
|
||
console.log('\n========== 使用说明 ==========\n');
|
||
console.log('1. 打开 CSV 文件用 Excel 查看');
|
||
console.log('2. 打开 TXT 文件查看完整信息');
|
||
console.log('3. 使用 stripe-test-cards 文件手动测试Stripe');
|
||
console.log('4. 建议测试方法:');
|
||
console.log(' - 先测试前50个,记录通过率');
|
||
console.log(' - 如果通过率高,再测试更多');
|
||
console.log(' - 记录失败原因(卡被拒/金额不足/其他)');
|
||
|
||
console.log('\n================================\n');
|
||
}
|
||
|
||
generate().catch(console.error);
|