dasdasd
This commit is contained in:
parent
26988d3de8
commit
fe15b877ab
@ -7,10 +7,25 @@ const CARD_TYPES = {
|
|||||||
unionpay: {
|
unionpay: {
|
||||||
name: '中国银联 (UnionPay)',
|
name: '中国银联 (UnionPay)',
|
||||||
// 固定前缀(用户提供的成功案例都是这个前缀)
|
// 固定前缀(用户提供的成功案例都是这个前缀)
|
||||||
prefix: '622836754',
|
prefix: '622836754', // 固定BIN前缀
|
||||||
length: 16,
|
length: 16,
|
||||||
cvvLength: 3,
|
cvvLength: 3,
|
||||||
useLuhn: true // 使用Luhn算法校验
|
useLuhn: true,
|
||||||
|
|
||||||
|
// 成功案例的后7位模式(用于智能生成)
|
||||||
|
successfulPatterns: [
|
||||||
|
'5055812', '8774419', '6781457', '2738949',
|
||||||
|
'2602400', '8575105', '6496080', '0057649',
|
||||||
|
'9574719', '8435128', '2797374', '5956423',
|
||||||
|
'7237848', '0385107', '4252006', '7562054'
|
||||||
|
],
|
||||||
|
|
||||||
|
// 生成策略配置
|
||||||
|
generation: {
|
||||||
|
mutationRate: 0.5, // 50% 使用变异策略
|
||||||
|
randomRate: 0.5, // 50% 使用纯随机
|
||||||
|
mutationDigits: [1, 2] // 变异时改变1-2个数字
|
||||||
|
}
|
||||||
},
|
},
|
||||||
visa: {
|
visa: {
|
||||||
name: 'Visa',
|
name: 'Visa',
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class CardGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成卡号
|
* 生成卡号(混合策略:变异真实案例 + 纯随机)
|
||||||
* @param {string} type - 卡类型
|
* @param {string} type - 卡类型
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
@ -23,29 +23,94 @@ class CardGenerator {
|
|||||||
throw new ValidationError('card-generator', `Unknown card type: ${type}`);
|
throw new ValidationError('card-generator', `Unknown card type: ${type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { length, useLuhn } = config;
|
const { prefix, length, useLuhn, successfulPatterns, generation } = config;
|
||||||
|
|
||||||
// 支持多个前缀(随机选择)或单个前缀
|
// 如果有成功案例和生成策略配置,使用混合策略
|
||||||
let prefix;
|
if (successfulPatterns && generation) {
|
||||||
if (Array.isArray(config.prefixes)) {
|
const useMutation = Math.random() < generation.mutationRate;
|
||||||
// 从数组中随机选择一个前缀
|
|
||||||
const randomIndex = randomInt(0, config.prefixes.length - 1);
|
if (useMutation) {
|
||||||
prefix = config.prefixes[randomIndex];
|
// 策略1:基于真实案例变异
|
||||||
} else {
|
return this.generateByMutation(prefix, successfulPatterns, generation.mutationDigits);
|
||||||
// 兼容旧配置(单个prefix)
|
}
|
||||||
prefix = config.prefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 策略2:纯随机生成(默认或回退)
|
||||||
if (useLuhn) {
|
if (useLuhn) {
|
||||||
// 使用Luhn算法生成
|
|
||||||
return generateLuhnNumber(prefix, length);
|
return generateLuhnNumber(prefix, length);
|
||||||
} else {
|
} else {
|
||||||
// 纯随机生成
|
|
||||||
const remainingLength = length - prefix.length;
|
const remainingLength = length - prefix.length;
|
||||||
return prefix + randomDigits(remainingLength);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算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}}
|
* @returns {{month: string, year: string}}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user