您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
根据多重条件自动审核大额订单
// ==UserScript== // @name CP统计 // @namespace http://tampermonkey.net/ // @version 1.0.2 // @description 根据多重条件自动审核大额订单 // @author Cisco // @match https://7777m.topcms.org/* // @match https://111bet22.topcms.org/* // @match https://888bet.topcms.org/* // @match https://hkgame.topcms.org/* // @match https://666bet.topcms.org/* // @match https://111bet.topcms.org/* // @match https://k9.topcms.org/* // @match https://34jogo.topcms.org/* // @match https://555x.topcms.org/* // @icon https://7777m.topcms.org/favicon.ico // @license MIT // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // ==/UserScript== (function () { "use strict"; const NS = "cpStats"; // 唯一前缀,多个脚本用不同的值即可 const gmGet = (k, def) => GM_getValue(`${NS}_${k}`, def); const gmSet = (k, v) => GM_setValue(`${NS}_${k}`, v); // 配置参数 const rawConfigCp = { currentPage: 1, // 当前页码 totalPages: 1, // 总页数 pageSize: 200, // 每页订单数量 isLastPage: false, // 是否是最后一页 minSameIPUsers: 2, // 最大相同IP用户数 minTotalRecharge: 10, // 总充值金额 >= maxTotalRecharge: 100, // 总充值金额 <= rechargeToProfitRatio: 10, // 总充值金额 > 游戏盈亏指定倍数 password: "", // 充值密码 processedOrders: gmGet("processedOrders", {}), // 已处理订单记录 payOutOrders: gmGet("payOutOrders", {}), // 已出款订单记录 cancelledOrders: gmGet("cancelledOrders", {}), // 已取消出款订单记录 currentOrderId: null, // 当前处理的订单ID isProcessing: false, // 是否正在处理中 isReturning: false, // 是否正在返回订单页面 panelCollapsed: false, // 面板是否收起 completedOneRound: false, // 是否完成了一轮处理 processingOrderId: null, // 正在处理的订单ID totalBetAmount: 0, // 当前订单的总投注额 profitAmount: 0, // 当前订单的游戏盈亏 startedProcessing: false, // 新的一轮处理 }; // 使用 Proxy 监听 config 变化 const config = new Proxy(rawConfigCp, { set(target, prop, value) { target[prop] = value; if (prop === "currentOrderId") { const el = document.getElementById(`${NS}_currentOrderId`); if (el) el.textContent = value || ""; } return true; } }); // 控制面板样式 GM_addStyle(` .${NS}-monitor-panel { position: fixed; top: 20px; right: 200px; z-index: 9999; background: white; padding: 15px; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); font-family: Arial, sans-serif; width: 320px; max-height: 90vh; overflow-y: auto; transition: all 0.3s ease; } .${NS}-monitor-panel.${NS}_collapsed { width: 40px; height: 40px; overflow: hidden; padding: 5px; } .${NS}-toggle-panel { position: absolute; top: 5px; right: 5px; width: 30px; height: 30px; border: none; background: #f0f0f0; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; z-index: 10000; } .${NS}-toggle-panel:hover { background: #e0e0e0; } .${NS}_collapsed .${NS}-panel-content { display: none; } .monitor-header { margin: 0 0 15px 0; color: #409EFF; font-size: 16px; font-weight: bold; border-bottom: 1px solid #eee; padding-bottom: 10px; } /* 优化统计字段样式 */ .${NS}-stat-container { background: #fafafa; border: 1px solid #eee; border-radius: 5px; padding: 12px; margin-bottom: 15px; } .${NS}-stat-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding: 6px 8px; background: white; border-radius: 4px; border-left: 3px solid #409EFF; transition: all 0.2s ease; } .${NS}-stat-row:hover { background: #f5f7fa; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .${NS}-stat-row:nth-child(2) { border-left-color: #67C23A; } .${NS}-stat-row:nth-child(3) { border-left-color: #E6A23C; } .${NS}-stat-row:nth-child(4) { border-left-color: #909399; } .${NS}-stat-row:nth-child(5) { border-left-color: #F56C6C; } .${NS}-stat-row:nth-child(6) { border-left-color: #9b59b6; } .${NS}-stat-row span { font-size: 13px; color: #606266; } .${NS}-stat-row span.value { font-weight: bold; color: #303133; background: #f0f2f5; padding: 3px 8px; border-radius: 3px; min-width: 50px; text-align: center; } .button-container { display: flex; flex-direction: column; margin-bottom: 15px; } .${NS}-monitor-button { width: 100%; padding: 10px; background: #409EFF; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; transition: background 0.3s; margin-bottom: 10px; } .${NS}-monitor-button:disabled { background: #C0C4CC; cursor: not-allowed; } .${NS}-monitor-button.stop { background: #F56C6C; } .monitor-stats { margin-top: 15px; font-size: 12px; color: #666; border-top: 1px solid #eee; padding-top: 10px; } .monitor-stat-row { display: flex; justify-content: space-between; margin-bottom: 5px; } .monitor-progress-container { margin: 10px 0; height: 10px; background: #f0f0f0; border-radius: 5px; overflow: hidden; } .monitor-progress-bar { height: 100%; background: linear-gradient(to right, #67C23A, #409EFF); transition: width 0.3s; } #${NS}_statusText { font-weight: bold; color: #409EFF; } #${NS}_processedCount { font-weight: bold; color: #67C23A; } .${NS}-monitor-button.hidden { display: none; } /* 清理缓存按钮样式 */ .${NS}-monitor-button.clear { background: #909399; margin-top: 5px; } .${NS}-monitor-button.clear:hover { background: #82848a; } `); // ==================== 工具函数 ==================== /** * 延迟执行 * @param {number} ms 毫秒数 * @returns {Promise<void>} */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 等待指定元素出现(可选检测其文本内容),超时返回 null 而不是抛异常 * @param {string} selector - CSS 选择器 * @param {number} [timeout=10000] - 等待的最长时间(毫秒) * @param {ParentNode} [parent=document] - 查找的父容器(默认 document) * @param {AbortSignal|null} [signal=null] - 可选的 AbortSignal,用于提前中断等待 * @param {Object} [options={}] - 额外选项 * @param {boolean} [options.requireText=false] - 是否要求元素有非空文本才算找到 * @returns {Promise<Element|null>} - 找到则返回元素,没找到或超时返回 null */ function waitForElement( selector, timeout = 10000, parent = document, signal = null, options = {} ) { // 如果 parent 为 null,则兜底使用 document if (!parent) parent = document; // 解析选项 const { requireText = false } = options; return new Promise(resolve => { // 如果调用时就已经中断,直接返回 null if (signal?.aborted) return resolve(null); // 计算超时时间点 const endTime = Date.now() + timeout; // 定时检测函数 const check = () => { // 如果外部中断,直接返回 null if (signal?.aborted) return resolve(null); // 查找元素 const element = parent.querySelector(selector); if (element) { // 如果要求有文本内容,但当前元素文本为空,继续等待 if (requireText && !element.textContent.trim()) { if (Date.now() < endTime) { setTimeout(check, 200); // 每 200ms 再检测一次 return; } // 超时依然没内容 return resolve(null); } // 找到符合条件的元素 return resolve(element); } // 元素还没出现,检查是否超时 if (Date.now() >= endTime) { return resolve(null); // 超时返回 null } // 继续等待 setTimeout(check, 200); }; check(); // 启动首次检测 }); } /** * 等待页面跳转完成 * @param {string} targetHash 目标页面hash * @param {number} timeout 超时时间(ms) * @returns {Promise<void>} */ function waitForPageChange(targetHash, timeout = 10000) { return new Promise((resolve, reject) => { if (window.location.hash.includes(targetHash)) { return resolve(); } const timer = setTimeout(() => { window.removeEventListener('hashchange', handler); reject(new Error(`Timeout waiting for page change to ${targetHash}`)); }, timeout); const handler = () => { if (window.location.hash.includes(targetHash)) { clearTimeout(timer); window.removeEventListener('hashchange', handler); resolve(); } }; window.addEventListener('hashchange', handler); }); } /** * 带重试的操作 * @param {Function} operation 操作函数 * @param {number} retries 重试次数 * @param {number} delayMs 重试间隔(ms) * @param {AbortSignal} signal 取消信号 * @returns {Promise<any>} */ async function retryOperation(operation, retries = 3, delayMs = 1000, signal = null) { let lastError; for (let i = 0; i < retries; i++) { if (signal?.aborted) { throw new DOMException('Aborted', 'AbortError'); } try { return await operation(); } catch (err) { lastError = err; if (i < retries - 1) await delay(delayMs); } } throw lastError; } /** * 检查是否在订单页面 * @returns {boolean} */ function isOrderPage() { return window.location.hash.includes("#/order/unread-withdraw"); } /** * 检查是否在代理页面 * @returns {boolean} */ function isAgentPage() { return window.location.hash.includes("#/agent/agent-list"); } /** * 检查是否在提现记录页面 * @returns {boolean} */ function isWithdrawPage() { return window.location.hash.includes("#/order/order-withdraw"); } /** * 检查是否在充值记录页面 * @returns {boolean} */ function isTopupPage() { return window.location.hash.includes("#/order/order-topup"); } /** * 更新状态显示 * @param {string} text 状态文本 */ function updateStatus(text) { const statusEl = document.getElementById(`${NS}_statusText`); if (statusEl) { statusEl.textContent = text; } } /** * 更新按钮显示状态 */ function updateButtonVisibility() { const startBtn = document.getElementById(`${NS}_startBtn`); const stopBtn = document.getElementById(`${NS}_stopBtn`); if (config.isProcessing) { startBtn?.classList.add("hidden"); stopBtn?.classList.remove("hidden"); } else { startBtn?.classList.remove("hidden"); stopBtn?.classList.add("hidden"); } } /** * 添加控制面板 */ function addControlPanel() { const panel = document.createElement("div"); panel.className = `${NS}-monitor-panel`; panel.id = `${NS}_autoWithdrawPanel`; // 添加收起/展开按钮 const toggleBtn = document.createElement("button"); toggleBtn.className = `${NS}-toggle-panel`; toggleBtn.innerHTML = "×"; toggleBtn.title = "收起/展开控制面板"; toggleBtn.addEventListener("click", togglePanel); // 面板内容 const panelContent = document.createElement("div"); panelContent.className = `${NS}-panel-content`; panelContent.innerHTML = ` <h3 class="monitor-header">📊 数据实时统计</h3> <div class="stats-container"> <div class="${NS}-stat-row"><span>今日CP出款比例</span><span class="value" id="${NS}_cpRatio">0%</span></div> <div class="${NS}-stat-row"><span>今日充提差</span><span class="value" id="${NS}_diffRatio">0%</span></div> <div class="${NS}-stat-row"><span>今日充值总金额</span><span class="value" id="${NS}_rechargeAmount">0</span></div> <div class="${NS}-stat-row"><span>今日充值人数</span><span class="value" id="${NS}_rechargeUsers">0</span></div> <div class="${NS}-stat-row"><span>今日提现总金额</span><span class="value" id="${NS}_withdrawAmount">0</span></div> <div class="${NS}-stat-row"><span>今日提现人数</span><span class="value" id="${NS}_withdrawUsers">0</span></div> </div> <div class="button-container"> <button id="${NS}_startBtn" class="${NS}-monitor-button ${config.isProcessing ? "hidden" : ""}">开始统计</button> <button id="${NS}_stopBtn" class="${NS}-monitor-button stop ${!config.isProcessing ? "hidden" : ""}">停止统计</button> </div> <div class="monitor-stats"> <div class="monitor-stat-row"> <span>📶 状态:</span> <span id="${NS}_statusText">待命</span> </div> <div class="progress-container"> <div class="progress-bar" style="width: 0%"></div> </div> </div> `; panel.appendChild(toggleBtn); panel.appendChild(panelContent); document.body.appendChild(panel); // 恢复面板状态 config.panelCollapsed = gmGet("panelCollapsed", false); if (config.panelCollapsed) { panel.classList.add(`${NS}_collapsed`); toggleBtn.innerHTML = "≡"; } // 事件监听 document.getElementById(`${NS}_startBtn`).addEventListener("click", function () { processor.start(); }); document.getElementById(`${NS}_stopBtn`).addEventListener("click", function () { processor.stop(); }); } /** * 收起/展开面板 */ function togglePanel() { const panel = document.getElementById(`${NS}_autoWithdrawPanel`); config.panelCollapsed = !panel.classList.contains(`${NS}_collapsed`); if (config.panelCollapsed) { panel.classList.add(`${NS}_collapsed`); this.innerHTML = "≡"; } else { panel.classList.remove(`${NS}_collapsed`); this.innerHTML = "×"; } gmSet("panelCollapsed", config.panelCollapsed); } /** * 更新控制面板中的充提差相关数据 * @param {number} rechargeAmount 今日充值总额 * @param {number} withdrawAmount 今日提现总额 */ function updateControlPanelStats(actualDiffRatioCoinPay, rechargeAmount, withdrawAmount, diffRatio, todayWithdrawPeople, totalTopupPeople) { const cpRatioEl = document.getElementById(`${NS}_cpRatio`); const rechargeEl = document.getElementById(`${NS}_rechargeAmount`); const withdrawEl = document.getElementById(`${NS}_withdrawAmount`); const todayWithdrawPeopleEl = document.getElementById(`${NS}_withdrawUsers`); const totalTopupPeopleEl = document.getElementById(`${NS}_rechargeUsers`); const diffRatioEl = document.getElementById(`${NS}_diffRatio`); if (cpRatioEl) cpRatioEl.textContent = actualDiffRatioCoinPay + "%"; if (rechargeEl) rechargeEl.textContent = rechargeAmount; if (withdrawEl) withdrawEl.textContent = withdrawAmount; if (todayWithdrawPeopleEl) todayWithdrawPeopleEl.textContent = todayWithdrawPeople; if (totalTopupPeopleEl) totalTopupPeopleEl.textContent = totalTopupPeople; if (diffRatioEl) diffRatioEl.textContent = diffRatio + "%"; } // ==================== 主流程控制器 ==================== class OrderProcessor { constructor(config) { this.config = config; this.abortController = new AbortController(); this.currentTask = null; } /** * 开始处理流程 */ async start() { if (this.currentTask) return; this.config.startedProcessing = true; // 开始新的轮次 this.config.isProcessing = true; updateButtonVisibility(); updateStatus("开始统计..."); this.currentTask = this.runProcessingLoop() .catch(err => { console.error('Processing error:', err); updateStatus(`处理出错: ${err.message}`); }) .finally(() => { this.currentTask = null; updateButtonVisibility(); }); } /** * 停止处理流程 */ stop() { if (!this.config.isProcessing) return; this.abortController.abort(); // 中止当前任务 this.config.isProcessing = false; this.closeAllDialogs(this.abortController.signal); // 先执行需要旧 signal 的方法 this.abortController = new AbortController(); // 再重置 updateStatus("正在停止..."); } /** * 返回到提现订单页面 * @param {AbortSignal} signal 取消信号 */ async goBackToWithdrawOrderPage(signal) { updateStatus("返回提现订单页面..."); // 跳转到提现订单页面 await this.navigateTo("#/order/order-withdraw", signal); // 等待查询结果 await delay(1500); } /** * 主处理循环 */ async runProcessingLoop() { const { signal } = this.abortController; while (this.config.isProcessing && !signal.aborted) { try { if (isWithdrawPage()) { console.log('当前在提现订单页面'); await this.processOrderPage(signal); } else { console.log('不在目标页面,跳转到提现订单页面'); await this.goBackToWithdrawOrderPage(); } } catch (err) { if (err.name === 'AbortError') break; console.error('Loop error:', err); await delay(3000); } } } /** * 处理提现订单页面 * @param {AbortSignal} signal 取消信号 */ async processOrderPage(signal) { if (signal.aborted) return; if (!isWithdrawPage()) { // 如果当前不是提现订单页面就跳转过来 await this.goBackToWithdrawOrderPage(signal); } this.closeAllDialogs(signal); const emptyText = document.querySelector(".el-table__empty-text"); if (emptyText?.textContent.includes("暂无数据")) { console.log('暂无数据'); // 无数据隔5秒重开 await delay(5000); await this.startNewRound(signal); return; } // 1. 确保页面完全加载 const el = await waitForElement('.el-table--scrollable-x .el-table__body .el-table__row .cell > span', 10000, null, signal, { requireText: true }); if (!el) { console.warn('没找到元素或超时'); await this.processOrderPage(signal); return; } // 2. 计算实际充提差比率 const {todayCount, todayWithdrawPeople, totalTopupAmount, totalTopupPeople, actualDiffRatioCoinPay, actualDiffRatio} = await this.getActualDiffRatio(signal); updateControlPanelStats(actualDiffRatioCoinPay, totalTopupAmount, todayCount, actualDiffRatio, todayWithdrawPeople, totalTopupPeople); // 隔2秒重开 await delay(2000); } /** * 获取今日充值总额 * @param {AbortSignal} signal 取消信号 */ async getTodayRecharge(signal) { this.closeAllDialogs(signal); if (signal.aborted) return; // 跳转到充值记录页面 if (!isTopupPage()) { // 如果当前不是充值订单页面就跳转过来 await this.navigateTo("#/order/order-topup", signal); } try { // 1. 确保页面完全加载 await waitForElement('.el-table__body', 10000, null, signal); // 4. 设置订单状态为"已支付" await this.selectDropdownOption("全部状态", "已支付", signal); // 5. 点击查询按钮 const queryBtn = await this.findQueryButton(signal); queryBtn.click(); // 6. 等待查询结果 await delay(3000); // 7. 获取当日总充值记录和人数 const totalTopupAmount = await this.getTodayRechargeTotalAmount(signal); const totalTopupPeople = await this.getTodayRechargeTotalPeople(signal); return {totalTopupAmount, totalTopupPeople}; } catch (err) { console.error('处理充值页面出错:', err); markOrderAsProcessed(this.config.currentOrderId, `充值记录检查失败: ${err.message}`); await this.navigateToOrderPage(); throw err; } } /** * 调整充值记录查询日期范围(最近7天) */ async adjustTopupDateRange(signal) { const dateInputs = await waitForElement( ".el-range-input", 5000, null, signal ); if (!dateInputs || dateInputs.length < 2) { throw new Error("找不到日期范围输入框"); } const now = new Date(); const endDate = new Date(now); endDate.setHours(23, 59, 59, 999); endDate.setHours(endDate.getHours() - 8); // 转为GMT+0 const startDate = new Date(endDate); startDate.setDate(startDate.getDate() - 6); // 7天范围 startDate.setHours(0, 0, 0, 0); const formatDate = (date) => { const pad = (num) => num.toString().padStart(2, '0'); return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; }; const setter = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, "value" ).set; setter.call(dateInputs[0], formatDate(startDate)); dateInputs[0].dispatchEvent(new Event("input", { bubbles: true })); setter.call(dateInputs[1], formatDate(endDate)); dateInputs[1].dispatchEvent(new Event("input", { bubbles: true })); } /** * 获取当天已支付的充值总人数 * @param {AbortSignal} signal 取消信号 * @returns {Promise<number>} 当天充值总人数 */ async getTodayRechargeTotalPeople(signal) { const rows = document.querySelectorAll(".el-table__body .el-table__row"); if (rows.length <= 1) return 0; // 没有数据 const cells = rows[0].querySelectorAll("td"); // 统计行固定位置第 5 列(索引4)为总充值金额 const amountCell = cells[2]?.querySelector(".cell"); if (!amountCell) return 0; const amountText = amountCell.textContent.trim(); if (!amountText) return 0; // 数值解析函数 const parseValue = (text) => { // 移除所有非数字字符(保留小数点和负号) const numStr = text.replace(/[^\d.-]/g, ""); return parseFloat(numStr) || 0; }; return parseValue(amountText); } /** * 获取当天已支付的充值总金额 * @param {AbortSignal} signal 取消信号 * @returns {Promise<number>} 当天充值总金额 */ async getTodayRechargeTotalAmount(signal) { const rows = document.querySelectorAll(".el-table__body .el-table__row"); if (rows.length <= 1) return 0; // 没有数据 const cells = rows[0].querySelectorAll("td"); // 统计行固定位置第 5 列(索引4)为总充值金额 const amountCell = cells[4]?.querySelector(".cell"); if (!amountCell) return 0; const amountText = amountCell.textContent.trim(); if (!amountText) return 0; // 数值解析函数 const parseValue = (text) => { // 移除所有非数字字符(保留小数点和负号) const numStr = text.replace(/[^\d.-]/g, ""); return parseFloat(numStr) || 0; }; return parseValue(amountText); } /** * 查找查询按钮(带重试机制) */ async findQueryButton(signal) { return await retryOperation( async () => { const buttons = document.querySelectorAll('.filter-container button.el-button'); const btn = Array.from(buttons).find(b => b.textContent.includes('查询') || (b.querySelector('span')?.textContent.includes('查询')) ); if (!btn) throw new Error('找不到查询按钮'); if (btn.disabled) throw new Error('查询按钮不可用'); return btn; }, 3, // 重试3次 1000, // 间隔1秒 signal ); } /** * 获取实际充提差比率 * @description 实际充提差比率 = (实际充提差 / 实际充值金额) * 100% * @param {AbortSignal} signal 取消信号 * @returns {Promise<boolean>} */ async getActualDiffRatio(signal) { this.closeAllDialogs(signal); // 1. 获取当前页订单行 updateStatus(`开始统计充提差`); // 2. 获取当天提现差 const {todayCount, todayWithdrawPeople, todayWithdrawCoinPayPeople, balance} = await this.getTotalWithdrawAmountRecords(signal); console.log(`系统当前提现差 ${balance}`) if (balance < 0) { return "本轮计算失败"; } // 5. 获取当天充值总额 const {totalTopupAmount, totalTopupPeople} = await this.getTodayRecharge(signal); console.log(`系统当前充值总额 ${totalTopupAmount}`) if (totalTopupAmount < 0) { return "本轮计算失败"; } // 计算实际充提差比率,保留10位小数 const actualDiffRatio = Number(((balance / totalTopupAmount) * 100).toFixed(10)); const actualDiffRatioCoinPay = Number(((todayWithdrawCoinPayPeople / totalTopupPeople) * 100).toFixed(5)); console.log("当天提现总额:", todayCount); console.log("当天提现总人数:", todayWithdrawPeople); console.log("当天充值总额:", totalTopupAmount); console.log("当天充值总人数:", totalTopupPeople); console.log("计算实际充提差比率:", actualDiffRatio); return { todayCount, todayWithdrawPeople, totalTopupAmount, totalTopupPeople, actualDiffRatioCoinPay, actualDiffRatio }; } /** * 获取当日提现金额差 * @description 当日总提现金额-当日coinPay提现金额 * @param {AbortSignal} signal 取消信号 * @returns {Promise<boolean>} */ async getTotalWithdrawAmountRecords(signal) { if (!isWithdrawPage()) { // 如果当前不是提现订单页面就跳转过来 await this.goBackToWithdrawOrderPage(signal); } // 4. 设置订单状态为"已支付" await this.selectDropdownOption("全部状态", "已支付", signal); // 4. 设置第三方为"全部" await this.selectDropdownOption("全部", "全部", signal); // 5. 点击查询按钮 const queryBtn = await this.findQueryButton(signal); if (!queryBtn) { updateStatus(`用户 ${userId} 找不到查询按钮`); return -1; } queryBtn.click(); // 6. 等待查询结果 await delay(3000); // 7. 获取当日总提现金额 const todayCount = await this.getTodayWithdrawTotalAmount(signal); console.log(`系统当前提现总额 ${todayCount}`) if (todayCount < 0) { updateStatus(`当天没有提现金额`); return -1; } // 7. 获取当日总提现人数 const todayWithdrawPeople = await this.getTodayWithdrawTotalPeople(signal); console.log(`系统当前总提现人数 ${todayWithdrawPeople }`) // 8. 获取当日coinPay提现金额 const coinPayCount = await this.getTodayCoinPayWithdrawAmount(signal); console.log(`系统当前coinPay提现总额 ${coinPayCount}`) if (coinPayCount < 0) { console.log(`系统当前没有coinpay提现金额`) updateStatus(`当天没有coinPay提现金额`); return -1; } // 7. 获取当日总coinpay提现人数 const todayWithdrawCoinPayPeople = await this.getTodayWithdrawTotalPeople(signal); console.log(`系统当前coinpay总提现人数 ${todayWithdrawCoinPayPeople}`) // 计算 当日总提现金额-当日coinPay提现金额 const balance = todayCount - coinPayCount; return { todayCount, todayWithdrawPeople, todayWithdrawCoinPayPeople, balance }; } /** * 跳转到指定页面 * @param {string} hash 页面hash * @param {AbortSignal} signal 取消信号 */ async navigateTo(hash, signal) { if (window.location.hash.includes(hash)) { await delay(1000); return; } window.location.hash = hash; await waitForPageChange(hash, 10000); await delay(1000); // 额外等待确保页面稳定 } /** * 跳转到订单页面 */ async navigateToWithdrawPage() { await this.navigateTo("#/order/unread-withdraw"); } /** * 开始新一轮处理 */ async startNewRound(signal) { updateStatus("所有订单已处理完成"); config.completedOneRound = true; gmSet("completedOneRound", true); // 点击查询按钮重新开始 await this.clickSearchButton(signal); } /** * 点击查询按钮 */ async clickSearchButton(signal) { // 获取所有filter-container元素 const filterContainers = document.querySelectorAll(".filter-container"); // 第三个filter-container包含查询按钮 if (filterContainers.length >= 3) { const searchContainer = filterContainers[2]; // 查找查询按钮 const searchBtn = Array.from( searchContainer.querySelectorAll("button.el-button") ).find((btn) => !btn.disabled && btn.textContent.includes("查询")); if (searchBtn) { updateStatus("点击查询按钮重新开始处理..."); // 先清空所有搜索条件 clearAllSearchInputs(); // 点击查询按钮 searchBtn.click(); // 等待查询完成 await delay(1500); config.completedOneRound = false; gmSet("completedOneRound", false); // 重新开始处理 await delay(500); await this.processOrderPage(signal); return; } } // 如果没找到按钮,尝试其他方式 updateStatus("未找到查询按钮,3秒后尝试重新开始"); await delay(3000); await this.processOrderPage(signal); } /** * 获取订单统计行(根据表头内容动态定位) * @param {AbortSignal} signal 取消信号 * @returns {Promise<NodeListOf<HTMLElement>>} */ async getOrderCountRows(signal, title) { return await retryOperation( () => { if (signal.aborted) return; // 遍历所有 el-table const tables = document.querySelectorAll(".el-table"); for (const table of tables) { const headerCells = table.querySelectorAll(".el-table__header .cell"); let hasTotalWithdraw = false; for (const cell of headerCells) { if (cell.textContent.includes("总提现金额")) { hasTotalWithdraw = true; break; } } if (!hasTotalWithdraw) continue; const rows = table.querySelectorAll(".el-table__body tbody tr"); if (rows.length === 0) { console.log("表格已找到,但还未渲染数据行"); return null; } return rows; // 找到表格就返回行 } console.log("未找到包含 '总提现金额' 的表格"); return null; }, 3, // 重试次数 1000, // 每次间隔 1 秒 signal ); } /** * 统计当天已支付提现总人数 * @param {AbortSignal} signal 取消信号 * @returns {Promise<number>} */ async getTodayWithdrawTotalPeople(signal) { // 检查是否有"暂无数据"提示 const emptyText = document.querySelector(".el-table__empty-text"); if (emptyText?.textContent.includes("暂无数据")) { console.log("暂无提现数据") return -1; } // 获取表格的所有行 const rows = await this.getOrderCountRows(signal, "总人数"); if (!rows || rows.length == 0) { console.log("暂无提现数据") return -1; } const cells = rows[0].querySelectorAll("td"); // 统计行固定位置第 5 列(索引4)为提现金额 const amountCell = cells[2]?.querySelector(".cell"); if (!amountCell) return 0; const amountText = amountCell.textContent.trim(); if (!amountText) return 0; // 数值解析函数 const parseValue = (text) => { // 移除所有非数字字符(保留小数点和负号) const numStr = text.replace(/[^\d.-]/g, ""); return parseFloat(numStr) || 0; }; return parseValue(amountText); } /** * 统计当天已支付提现总金额 * @param {AbortSignal} signal 取消信号 * @returns {Promise<number>} */ async getTodayWithdrawTotalAmount(signal) { // 检查是否有"暂无数据"提示 const emptyText = document.querySelector(".el-table__empty-text"); if (emptyText?.textContent.includes("暂无数据")) { console.log("暂无提现数据") return -1; } // 获取表格的所有行 const rows = await this.getOrderCountRows(signal, "总提现金额"); if (!rows || rows.length == 0) { console.log("暂无提现数据") return -1; } const cells = rows[0].querySelectorAll("td"); // 统计行固定位置第 5 列(索引4)为提现金额 const amountCell = cells[4]?.querySelector(".cell"); if (!amountCell) return 0; const amountText = amountCell.textContent.trim(); if (!amountText) return 0; // 数值解析函数 const parseValue = (text) => { // 移除所有非数字字符(保留小数点和负号) const numStr = text.replace(/[^\d.-]/g, ""); return parseFloat(numStr) || 0; }; return parseValue(amountText); } /** * 统计当天 CoinPay 已支付提现总金额 * @param {AbortSignal} signal 取消信号 * @returns {Promise<boolean>} */ async getTodayCoinPayWithdrawAmount(signal) { await this.selectDropdownOption("全部", "CoinPay", signal); // 点击查询按钮 const queryBtn = await this.findQueryButton(signal); if (!queryBtn) { return -1; } queryBtn.click(); // 等待查询结果 await delay(3000); // 检查是否有数据 const emptyText = document.querySelector(".el-table__empty-text"); if (emptyText?.textContent.includes("暂无数据")) { console.log("暂无CoinPay提现数据") return -1; } // 获取表格行 const rows = document.querySelectorAll(".el-table__body .el-table__row"); if (!rows || rows.length == 0) { console.log("暂无CoinPay提现数据") return -1; } const cells = rows[0].querySelectorAll("td"); // 统计行固定位置第 5 列(索引4)为提现金额 const amountCell = cells[4]?.querySelector(".cell"); if (!amountCell) return 0; const amountText = amountCell.textContent.trim(); if (!amountText) return 0; // 数值解析函数 const parseValue = (text) => { // 移除所有非数字字符(保留小数点和负号) const numStr = text.replace(/[^\d.-]/g, ""); return parseFloat(numStr) || 0; }; return parseValue(amountText); } /** * 选择下拉选项 * @param {string} placeholder 下拉框placeholder * @param {string} optionText 选项文本 * @param {AbortSignal} signal 取消信号 */ async selectDropdownOption(placeholder, optionText, signal) { const selectInput = await waitForElement( `.el-select input[placeholder="${placeholder}"]`, 5000, null, signal ); selectInput.click(); const dropdown = await waitForElement( '.el-select-dropdown.el-popper:not([style*="display: none"])', 5000, null, signal ); const options = dropdown.querySelectorAll(".el-select-dropdown__item"); const targetOption = Array.from(options).find(option => option.textContent.trim().toLowerCase() === optionText.toLowerCase() ); if (!targetOption) { throw new Error(`找不到选项: ${optionText}`); } targetOption.click(); await delay(1000); // 等待选择生效 } /** * 关闭所有弹窗 */ async closeAllDialogs(signal) { const dialogs = document.querySelectorAll('.el-dialog__wrapper:not([style*="display: none"])'); if (!dialogs) { processor.processOrderPage(signal); return; } for (const dialog of dialogs) { const closeBtn = dialog.querySelector('.el-dialog__headerbtn'); if (closeBtn) closeBtn.click(); await delay(300); } } } // 创建处理器实例 const processor = new OrderProcessor(config); // 初始化 function init() { // 初始化控制面板 addControlPanel(); // 检查是否已完成一轮处理 config.completedOneRound = gmGet("completedOneRound", false); if (config.completedOneRound) { updateStatus("检测到已完成一轮处理,开始新一轮处理"); gmSet("completedOneRound", false); config.processedOrders = {}; gmSet("processedOrders", {}); document.getElementById(`${NS}_processedCount`).textContent = "0"; } updateStatus("准备就绪"); // 如果当前已经在订单页面且正在处理中,直接开始处理 if (config.isProcessing) { updateStatus("开始处理中..."); processor.processOrderPage(processor.abortController.signal); } } // 页面加载完成后执行 if (document.readyState === "complete") { init(); } else { window.addEventListener("load", init); } })();