// 只读:订单管理页实时状态探测(隐藏标签页,跑完关闭) import fs from 'node:fs'; import path from 'node:path'; const OUT = path.resolve('.cdp/out'); fs.mkdirSync(OUT, { recursive: true }); const UDD = 'C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data'; const lines = fs.readFileSync(path.join(UDD, 'DevToolsActivePort'), 'utf8') .split('\n').map(s => s.trim()).filter(Boolean); class Cdp { constructor(ws) { this.ws = ws; this.seq = 0; this.pending = new Map(); } static async attach(url) { const ws = new WebSocket(url); await new Promise((res, rej) => { const t = setTimeout(() => rej(new Error('ws timeout')), 60000); ws.addEventListener('open', () => { clearTimeout(t); res(); }, { once: true }); ws.addEventListener('error', () => { clearTimeout(t); rej(new Error('ws error')); }, { once: true }); }); const c = new Cdp(ws); ws.addEventListener('message', ev => { let m; try { m = JSON.parse(ev.data); } catch { return; } if (m.id && c.pending.has(m.id)) { const p = c.pending.get(m.id); c.pending.delete(m.id); clearTimeout(p.timer); m.error ? p.reject(new Error(m.error.message)) : p.resolve(m.result); } else if (m.method) c.onEvent?.(m.method, m.params, m.sessionId); }); return c; } send(method, params = {}, sid, t = 90000) { const id = ++this.seq; return new Promise((res, rej) => { const timer = setTimeout(() => { this.pending.delete(id); rej(new Error('timeout ' + method)); }, t); this.pending.set(id, { resolve: res, reject: rej, timer }); const msg = { id, method, params }; if (sid) msg.sessionId = sid; this.ws.send(JSON.stringify(msg)); }); } close() { try { this.ws.close(); } catch {} } } const cdp = await Cdp.attach(`ws://127.0.0.1:${lines[0]}${lines[1]}`); const { targetId } = await cdp.send('Target.createTarget', { url: 'https://seller.goofish.com/?site=COMMONPRO#/seller-trade/order-manage', background: true, }); const { sessionId: S } = await cdp.send('Target.attachToTarget', { targetId, flatten: true }); await cdp.send('Page.enable', {}, S); const sleep = ms => new Promise(r => setTimeout(r, ms)); let last = Date.now(); cdp.onEvent = (m, p, sid) => { if (sid === S) last = Date.now(); }; const t0 = Date.now(); while (Date.now() - t0 < 30000) { await sleep(500); if (Date.now() - last > 4000 && Date.now() - t0 > 9000) break; } await sleep(2500); const EXPR = `(() => { const q = s => Array.from(document.querySelectorAll(s)); const txt = e => (e.innerText || '').replace(/\\s+/g,' ').trim(); const raw = q('.ant-table-tbody tr').map(tr => Array.from(tr.querySelectorAll('td')).map(txt)); const orders = []; let cur = null; for (const r of raw) { const joined = r.filter(Boolean).join(' | '); const m = joined.match(/(待发货|待付款|已发货|待收货|交易成功|交易关闭|退款中|退款成功)\\s*订单编号\\s*(\\d+)/); if (m) { cur = { status: m[1], orderNo: m[2], _head: joined }; orders.push(cur); continue; } if (cur && !cur._detail && r.length >= 5 && r[1]) { cur._detail = { item: r[1], state: r[2], price: r[3], total: r[4], buyer: r[5] }; } } const pick = (s, re) => { const m = String(s || '').match(re); return m ? m[1].trim() : null; }; for (const o of orders) { const d = o._detail || {}, h = o._head || ''; o.orderTime = pick(h, /下单时间\\s*([\\d\\-: ]+)/); o.deadline = pick((d.state || '') + ' ' + h, /需在\\s*([^前]{2,20})前发货/); o.countdown = pick((d.state || '') + ' ' + h, /([\\d]+时[\\d]*分?|[\\d]+分)\\s*后自动关闭/); o.amount = pick(d.total || '', /¥\\s*([\\d.]+)/); o.listPrice = pick(d.price || '', /¥\\s*([\\d.]+)/); o.spec = pick(d.item || '', /规格\\s*(.+)$/); o.title = d.item ? String(d.item).split('规格')[0].trim() : null; o.buyerMasked = pick(d.buyer || '', /^([^ ]+)/); o.hasShipBtn = /去发货/.test(JSON.stringify(raw.filter(x => x.join('').includes(o.orderNo)))); delete o._head; delete o._detail; } const counts = {}; for (const o of orders) counts[o.status] = (counts[o.status] || 0) + 1; return { rowCount: raw.length, counts, orders }; })()`; const r = await cdp.send('Runtime.evaluate', { expression: EXPR, returnByValue: true, awaitPromise: true }, S, 60000); const v = r.exceptionDetails ? { __error: r.exceptionDetails.text } : r.result?.value; fs.writeFileSync(path.join(OUT, 'orders-probe.json'), JSON.stringify(v, null, 2), 'utf8'); console.log('rowCount=' + (v && v.rowCount) + ' counts=' + JSON.stringify(v && v.counts)); const need = (v && v.orders || []).filter(o => o.status === '待发货' || o.status === '待付款'); console.log('--- 需处理 ' + need.length + ' 单 ---'); for (const o of need) console.log([o.status, o.orderNo, o.orderTime, '限' + (o.deadline || o.countdown || '-'), '¥' + o.amount, String(o.title || '').slice(0, 26), '/', String(o.spec || '').slice(0, 24)].join(' | ')); await cdp.send('Target.closeTarget', { targetId }); cdp.close(); process.exit(0);