auto-account-machine/src/shared/libs/card-generator/generator.js
2025-11-19 17:44:35 +08:00

304 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Card Generator - 核心生成逻辑
*/
const { randomInt, randomDigits, padZero, generateLuhnNumber } = require('../../utils');
const { CARD_TYPES, EXPIRY_CONFIG } = require('./config');
const { ValidationError } = require('../../errors');
class CardGenerator {
constructor(database = null) {
this.cardTypes = CARD_TYPES;
this.expiryConfig = EXPIRY_CONFIG;
this.usedNumbers = new Set(); // 去重机制:记录已生成的卡号
this.database = database; // 数据库连接(可选)
}
/**
* 生成卡号(带去重机制 + 数据库查重)
* @param {string} type - 卡类型
* @returns {Promise<string>}
*/
async generateCardNumber(type) {
const config = this.cardTypes[type];
if (!config) {
throw new ValidationError('card-generator', `Unknown card type: ${type}`);
}
// 尝试生成唯一卡号最多100次
let attempts = 0;
const maxAttempts = 100;
while (attempts < maxAttempts) {
const cardNumber = this._generateCardNumberInternal(type, config);
// 检查1内存去重本次运行
if (this.usedNumbers.has(cardNumber)) {
attempts++;
continue;
}
// 检查2数据库去重历史记录
if (this.database) {
const existsInDb = await this.checkCardNumberInDatabase(cardNumber);
if (existsInDb) {
attempts++;
continue;
}
}
// 通过所有检查,记录并返回
this.usedNumbers.add(cardNumber);
return cardNumber;
}
throw new ValidationError('card-generator', `无法生成唯一卡号(${maxAttempts}次尝试后仍重复)`);
}
/**
* 检查卡号是否在数据库中已存在
* @param {string} cardNumber - 卡号
* @returns {Promise<boolean>}
*/
async checkCardNumberInDatabase(cardNumber) {
try {
// 检查数据库连接是否已初始化
if (!this.database || !this.database.pool) {
return false; // 数据库未初始化,跳过数据库查询
}
const sql = 'SELECT COUNT(*) as count FROM windsurf_accounts WHERE payment_card_number = ?';
const rows = await this.database.query(sql, [cardNumber]);
return rows[0].count > 0;
} catch (error) {
// 如果数据库查询失败,静默降级(不输出,避免乱码)
return false;
}
}
/**
* 内部生成逻辑(不含去重)
* @param {string} type - 卡类型
* @param {Object} config - 卡类型配置
* @returns {string}
*/
_generateCardNumberInternal(type, config) {
const { prefix, length, useLuhn, successfulPatterns, generation } = config;
// 如果有成功案例和生成策略配置,使用三策略混合
if (successfulPatterns && generation) {
const rand = Math.random();
// 策略130%):加权生成(基于统计分布)
if (rand < 0.3) {
return this.generateByWeights(prefix, length);
}
// 策略240%):变异真实案例
else if (rand < 0.7) {
return this.generateByMutation(prefix, successfulPatterns, generation.mutationDigits);
}
// 策略330%):完全随机
}
// 策略3纯随机生成默认或回退
if (useLuhn) {
return generateLuhnNumber(prefix, length);
} else {
const remainingLength = length - prefix.length;
return prefix + randomDigits(remainingLength);
}
}
/**
* 基于真实案例变异生成卡号
* @param {string} prefix - BIN前缀
* @param {Array} patterns - 成功案例的后7位
* @param {Array} mutationDigits - 变异数字个数[min, max]
* @returns {string}
*/
generateByMutation(prefix, patterns, mutationDigits) {
// 随机选择一个成功案例
const basePattern = patterns[randomInt(0, patterns.length - 1)];
// 随机决定改变几个数字
const changeCount = randomInt(mutationDigits[0], mutationDigits[1]);
const mutated = basePattern.split('');
// 改变指定数量的数字
const changedPositions = new Set();
for (let i = 0; i < changeCount; i++) {
let pos;
do {
pos = randomInt(0, mutated.length - 1);
} while (changedPositions.has(pos)); // 避免重复位置
changedPositions.add(pos);
// 生成不同的数字
let newDigit;
do {
newDigit = randomInt(0, 9).toString();
} while (newDigit === mutated[pos]);
mutated[pos] = newDigit;
}
// 组合前缀和变异后的后7位去掉最后一位校验位
const prefix15 = prefix + mutated.join('').slice(0, -1);
// 重新计算Luhn校验位
const checkDigit = this.calculateLuhnCheckDigit(prefix15);
return prefix15 + checkDigit;
}
/**
* 基于统计权重生成卡号60个成功案例的分布
* @param {string} prefix - BIN前缀
* @param {number} length - 总长度
* @returns {string}
*/
generateByWeights(prefix, length) {
// 基于60个成功案例的位置权重频率分布
const positionWeights = [
[7, 5, 8, 2, 5, 7, 6, 7, 6, 7], // 位置1
[7, 5, 7, 4, 11, 4, 2, 8, 8, 4], // 位置2
[5, 4, 4, 9, 6, 7, 7, 6, 5, 7], // 位置3
[12, 5, 8, 7, 7, 5, 4, 6, 5, 1], // 位置4: 数字0占20%
[9, 6, 7, 3, 5, 6, 3, 9, 7, 5], // 位置5
[10, 4, 5, 9, 7, 8, 4, 5, 6, 2], // 位置6
[7, 3, 7, 2, 9, 6, 4, 6, 9, 7], // 位置7
];
// 生成6位前15位去掉最后一位校验位
let pattern = '';
for (let pos = 0; pos < 6; pos++) {
const weights = positionWeights[pos];
const digit = this.weightedRandomDigit(weights);
pattern += digit;
}
// 组合前缀
const prefix15 = prefix + pattern;
// 计算Luhn校验位
const checkDigit = this.calculateLuhnCheckDigit(prefix15);
return prefix15 + checkDigit;
}
/**
* 按权重随机选择数字
* @param {Array} weights - 权重数组10个元素对应数字0-9
* @returns {string}
*/
weightedRandomDigit(weights) {
const total = weights.reduce((sum, w) => sum + w, 0);
let random = Math.random() * total;
for (let i = 0; i < weights.length; i++) {
random -= weights[i];
if (random <= 0) return i.toString();
}
return randomInt(0, 9).toString();
}
/**
* 计算Luhn校验位
* @param {string} partial - 不含校验位的卡号
* @returns {string}
*/
calculateLuhnCheckDigit(partial) {
let sum = 0;
let isEven = true;
for (let i = partial.length - 1; i >= 0; i--) {
let digit = parseInt(partial[i]);
if (isEven) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
isEven = !isEven;
}
return ((10 - (sum % 10)) % 10).toString();
}
/**
* 生成有效期
* @returns {{month: string, year: string}}
*/
generateExpiry() {
const month = randomInt(this.expiryConfig.minMonth, this.expiryConfig.maxMonth);
const year = randomInt(this.expiryConfig.minYear, this.expiryConfig.maxYear);
return {
month: padZero(month, 2),
year: padZero(year, 2)
};
}
/**
* 生成CVV安全码
* @param {string} type - 卡类型
* @returns {string}
*/
generateCVV(type) {
const config = this.cardTypes[type];
const cvvLength = config.cvvLength;
const maxValue = Math.pow(10, cvvLength) - 1;
const cvv = randomInt(0, maxValue);
return padZero(cvv, cvvLength);
}
/**
* 生成完整的信用卡信息
* @param {string} type - 卡类型,默认'unionpay'
* @returns {Promise<Object>}
*/
async generate(type = 'unionpay') {
const number = await this.generateCardNumber(type);
const expiry = this.generateExpiry();
const cvv = this.generateCVV(type);
return {
number,
month: expiry.month,
year: expiry.year,
cvv,
type: this.cardTypes[type].name
};
}
/**
* 批量生成
* @param {number} count - 数量
* @param {string} type - 卡类型
* @returns {Promise<Array>}
*/
async generateBatch(count, type = 'unionpay') {
const cards = [];
for (let i = 0; i < count; i++) {
cards.push(await this.generate(type));
}
return cards;
}
/**
* 获取所有支持的卡类型
* @returns {Array}
*/
getSupportedTypes() {
return Object.keys(this.cardTypes).map(key => ({
id: key,
name: this.cardTypes[key].name
}));
}
}
module.exports = CardGenerator;