From 1c642a8236624b1a463fd6b0474f91bfaff97cd1 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Wed, 19 Nov 2025 16:48:59 +0800 Subject: [PATCH] dasdasd --- add-card-fields.sql | 15 ++ src/shared/libs/card-generator/config.js | 8 +- src/shared/libs/card-generator/generator.js | 149 +++++++++++++++--- .../libs/database/account-repository.js | 23 ++- src/tools/account-register/sites/windsurf.js | 3 + .../sites/windsurf-adapter.js | 15 +- src/tools/database/init.sql | 16 +- 7 files changed, 199 insertions(+), 30 deletions(-) create mode 100644 add-card-fields.sql diff --git a/add-card-fields.sql b/add-card-fields.sql new file mode 100644 index 0000000..47a72d2 --- /dev/null +++ b/add-card-fields.sql @@ -0,0 +1,15 @@ +-- 添加卡片有效期和CVV字段到 windsurf_accounts 表 +-- 执行时间: 2025-11-19 +-- 作用: 保存完整的支付卡信息(有效期月份、年份、CVV安全码) + +ALTER TABLE windsurf_accounts +ADD COLUMN payment_card_expiry_month VARCHAR(2) COMMENT '支付卡有效期-月份(01-12)', +ADD COLUMN payment_card_expiry_year VARCHAR(2) COMMENT '支付卡有效期-年份(26-30)', +ADD COLUMN payment_card_cvv VARCHAR(3) COMMENT '支付卡CVV安全码(3位数字)'; + +-- 添加索引(提升卡号查询性能,用于去重) +CREATE INDEX idx_payment_card_number ON windsurf_accounts(payment_card_number); + +-- 验证字段和索引已添加 +DESCRIBE windsurf_accounts; +SHOW INDEX FROM windsurf_accounts; diff --git a/src/shared/libs/card-generator/config.js b/src/shared/libs/card-generator/config.js index 3b4df07..10ebfa8 100644 --- a/src/shared/libs/card-generator/config.js +++ b/src/shared/libs/card-generator/config.js @@ -12,7 +12,8 @@ const CARD_TYPES = { cvvLength: 3, useLuhn: true, - // 成功案例的后7位模式(43个真实案例,统计分析显示接近均匀随机分布) + // 成功案例的后7位模式(60个真实成功支付案例) + // 统计分析显示:位置4的数字0占20%,位置2的数字4占18.3% successfulPatterns: [ '1130577', '0744030', '9888788', '9131205', '1450744', '7238010', '7300364', '0814288', '6042579', '6361755', @@ -22,7 +23,10 @@ const CARD_TYPES = { '2464926', '2487000', '5452860', '8491592', '5022853', '5864858', '4742832', '0023658', '7416988', '7093159', '9198576', '8160064', '6223252', '4873785', '1299976', - '2940032', '6998937', '5800241' + '2940032', '6998937', '5800241', '3770784', '5055812', + '8774419', '6781457', '2738949', '2602400', '8575105', + '6496080', '0057649', '9574719', '8435128', '2797374', + '5956423', '7237848', '0385107', '4252006', '7562054' ], // 生成策略配置 diff --git a/src/shared/libs/card-generator/generator.js b/src/shared/libs/card-generator/generator.js index 8391820..26104fd 100644 --- a/src/shared/libs/card-generator/generator.js +++ b/src/shared/libs/card-generator/generator.js @@ -7,35 +7,96 @@ const { CARD_TYPES, EXPIRY_CONFIG } = require('./config'); const { ValidationError } = require('../../errors'); class CardGenerator { - constructor() { + constructor(database = null) { this.cardTypes = CARD_TYPES; this.expiryConfig = EXPIRY_CONFIG; + this.usedNumbers = new Set(); // 去重机制:记录已生成的卡号 + this.database = database; // 数据库连接(可选) } /** - * 生成卡号(混合策略:变异真实案例 + 纯随机) + * 生成卡号(带去重机制 + 数据库查重) * @param {string} type - 卡类型 - * @returns {string} + * @returns {Promise} */ - generateCardNumber(type) { + async generateCardNumber(type) { const config = this.cardTypes[type]; if (!config) { throw new ValidationError('card-generator', `Unknown card type: ${type}`); } - const { prefix, length, useLuhn, successfulPatterns, generation } = config; + // 尝试生成唯一卡号(最多100次) + let attempts = 0; + const maxAttempts = 100; - // 如果有成功案例和生成策略配置,使用混合策略 - if (successfulPatterns && generation) { - const useMutation = Math.random() < generation.mutationRate; + while (attempts < maxAttempts) { + const cardNumber = this._generateCardNumberInternal(type, config); - if (useMutation) { - // 策略1:基于真实案例变异 - return this.generateByMutation(prefix, successfulPatterns, generation.mutationDigits); + // 检查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; } - // 策略2:纯随机生成(默认或回退) + throw new ValidationError('card-generator', `无法生成唯一卡号(${maxAttempts}次尝试后仍重复)`); + } + + /** + * 检查卡号是否在数据库中已存在 + * @param {string} cardNumber - 卡号 + * @returns {Promise} + */ + async checkCardNumberInDatabase(cardNumber) { + try { + 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) { + // 如果数据库查询失败,记录错误但不影响生成(降级为仅内存去重) + console.warn('数据库查询失败,仅使用内存去重:', error.message); + 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(); + + // 策略1(30%):加权生成(基于统计分布) + if (rand < 0.3) { + return this.generateByWeights(prefix, length); + } + // 策略2(40%):变异真实案例 + else if (rand < 0.7) { + return this.generateByMutation(prefix, successfulPatterns, generation.mutationDigits); + } + // 策略3(30%):完全随机 + } + + // 策略3:纯随机生成(默认或回退) if (useLuhn) { return generateLuhnNumber(prefix, length); } else { @@ -87,6 +148,58 @@ class CardGenerator { 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 - 不含校验位的卡号 @@ -141,10 +254,10 @@ class CardGenerator { /** * 生成完整的信用卡信息 * @param {string} type - 卡类型,默认'unionpay' - * @returns {Object} + * @returns {Promise} */ - generate(type = 'unionpay') { - const number = this.generateCardNumber(type); + async generate(type = 'unionpay') { + const number = await this.generateCardNumber(type); const expiry = this.generateExpiry(); const cvv = this.generateCVV(type); @@ -161,12 +274,12 @@ class CardGenerator { * 批量生成 * @param {number} count - 数量 * @param {string} type - 卡类型 - * @returns {Array} + * @returns {Promise} */ - generateBatch(count, type = 'unionpay') { + async generateBatch(count, type = 'unionpay') { const cards = []; for (let i = 0; i < count; i++) { - cards.push(this.generate(type)); + cards.push(await this.generate(type)); } return cards; } diff --git a/src/shared/libs/database/account-repository.js b/src/shared/libs/database/account-repository.js index 6d581c7..38480d4 100644 --- a/src/shared/libs/database/account-repository.js +++ b/src/shared/libs/database/account-repository.js @@ -24,6 +24,9 @@ class AccountRepository { billingDays, billingDate, paymentCardNumber, + paymentCardExpiryMonth, + paymentCardExpiryYear, + paymentCardCvv, paymentCountry, status, isOnSale @@ -33,8 +36,9 @@ class AccountRepository { INSERT INTO windsurf_accounts ( email, password, first_name, last_name, registration_time, quota_used, quota_total, billing_days, billing_date, - payment_card_number, payment_country, status, is_on_sale - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + payment_card_number, payment_card_expiry_month, payment_card_expiry_year, payment_card_cvv, + payment_country, status, is_on_sale + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `; const params = [ @@ -48,6 +52,9 @@ class AccountRepository { billingDays || null, billingDate || null, paymentCardNumber || null, + paymentCardExpiryMonth || null, + paymentCardExpiryYear || null, + paymentCardCvv || null, paymentCountry || 'MO', status || 'active', isOnSale !== undefined ? isOnSale : false @@ -110,6 +117,18 @@ class AccountRepository { updates.push('payment_card_number = ?'); params.push(accountData.paymentCardNumber); } + if (accountData.paymentCardExpiryMonth !== undefined) { + updates.push('payment_card_expiry_month = ?'); + params.push(accountData.paymentCardExpiryMonth); + } + if (accountData.paymentCardExpiryYear !== undefined) { + updates.push('payment_card_expiry_year = ?'); + params.push(accountData.paymentCardExpiryYear); + } + if (accountData.paymentCardCvv !== undefined) { + updates.push('payment_card_cvv = ?'); + params.push(accountData.paymentCardCvv); + } if (accountData.paymentCountry !== undefined) { updates.push('payment_country = ?'); params.push(accountData.paymentCountry); diff --git a/src/tools/account-register/sites/windsurf.js b/src/tools/account-register/sites/windsurf.js index 121731c..6e5dbc4 100644 --- a/src/tools/account-register/sites/windsurf.js +++ b/src/tools/account-register/sites/windsurf.js @@ -2285,6 +2285,9 @@ class WindsurfRegister { billingDays: this.billingInfo ? parseInt(this.billingInfo.days) : null, billingDate: this.billingInfo ? this.billingInfo.date : null, paymentCardNumber: this.cardInfo ? this.cardInfo.number : null, + paymentCardExpiryMonth: this.cardInfo ? this.cardInfo.month : null, + paymentCardExpiryYear: this.cardInfo ? this.cardInfo.year : null, + paymentCardCvv: this.cardInfo ? this.cardInfo.cvv : null, paymentCountry: this.cardInfo ? this.cardInfo.country : 'MO', status: 'active', isOnSale: false diff --git a/src/tools/automation-framework/sites/windsurf-adapter.js b/src/tools/automation-framework/sites/windsurf-adapter.js index dbdec13..de4d57e 100644 --- a/src/tools/automation-framework/sites/windsurf-adapter.js +++ b/src/tools/automation-framework/sites/windsurf-adapter.js @@ -1,6 +1,7 @@ const SiteAdapter = require('../core/site-adapter'); const AccountDataGenerator = require('../../../shared/libs/account-generator'); const CardGenerator = require('../../../shared/libs/card-generator'); +const database = require('../../../shared/libs/database'); /** * Windsurf 站点适配器 @@ -11,7 +12,8 @@ class WindsurfAdapter extends SiteAdapter { // 数据生成器 this.dataGen = new AccountDataGenerator(); - this.cardGen = new CardGenerator(); + // 卡号生成器(传入数据库连接以实现数据库去重) + this.cardGen = new CardGenerator(database); } /** @@ -25,7 +27,7 @@ class WindsurfAdapter extends SiteAdapter { // 生成账户数据 const accountData = this.dataGen.generateAccount(); - const cardInfo = this.cardGen.generate(); + const cardInfo = await this.cardGen.generate(); // 存储到上下文 this.context.data = { @@ -245,12 +247,12 @@ class WindsurfAdapter extends SiteAdapter { /** * 重新生成银行卡(用于重试) */ - regenerateCard() { - const newCard = this.cardGen.generate(); + async regenerateCard() { + const newCard = await this.cardGen.generate(); this.context.data.card = newCard; const bin = newCard.number.substring(0, 9); this.log('info', `重新生成卡号: ${newCard.number} (BIN: ${bin})`); - return newCard; + return { card: newCard }; } /** @@ -478,6 +480,9 @@ class WindsurfAdapter extends SiteAdapter { billingDays: billingInfo ? parseInt(billingInfo.days) : null, billingDate: billingInfo ? billingInfo.date : null, paymentCardNumber: card ? card.number : null, + paymentCardExpiryMonth: card ? card.month : null, + paymentCardExpiryYear: card ? card.year : null, + paymentCardCvv: card ? card.cvv : null, paymentCountry: card ? card.country : 'MO', status: 'active', isOnSale: false diff --git a/src/tools/database/init.sql b/src/tools/database/init.sql index d2f530c..2bf3104 100644 --- a/src/tools/database/init.sql +++ b/src/tools/database/init.sql @@ -22,6 +22,9 @@ CREATE TABLE IF NOT EXISTS `windsurf_accounts` ( `billing_days` INT COMMENT '下次账单天数', `billing_date` VARCHAR(50) COMMENT '账单日期', `payment_card_number` VARCHAR(20) COMMENT '支付卡号', + `payment_card_expiry_month` VARCHAR(2) COMMENT '支付卡有效期-月份(01-12)', + `payment_card_expiry_year` VARCHAR(2) COMMENT '支付卡有效期-年份(26-30)', + `payment_card_cvv` VARCHAR(3) COMMENT '支付卡CVV安全码(3位数字)', `payment_country` VARCHAR(10) DEFAULT 'MO' COMMENT '支付国家代码', `status` ENUM('active', 'expired', 'error') DEFAULT 'active' COMMENT '账号状态', `is_on_sale` BOOLEAN DEFAULT FALSE COMMENT '是否已上架销售', @@ -30,9 +33,16 @@ CREATE TABLE IF NOT EXISTS `windsurf_accounts` ( UNIQUE KEY `uk_email` (`email`), KEY `idx_status` (`status`), KEY `idx_registration_time` (`registration_time`), - KEY `idx_created_at` (`created_at`) + KEY `idx_created_at` (`created_at`), + KEY `idx_payment_card_number` (`payment_card_number`) COMMENT '卡号索引-用于去重查询' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Windsurf 账号表'; -- 插入示例数据(可选) --- INSERT INTO `windsurf_accounts` (email, password, first_name, last_name, quota_total) --- VALUES ('test@example.com', 'Password123!', 'John', 'Doe', 600.00); +-- INSERT INTO `windsurf_accounts` ( +-- email, password, first_name, last_name, +-- quota_total, payment_card_number, payment_card_expiry_month, +-- payment_card_expiry_year, payment_card_cvv +-- ) VALUES ( +-- 'test@example.com', 'Password123!', 'John', 'Doe', +-- 600.00, '6228367541234567', '12', '28', '123' +-- );