97 lines
5.0 KiB
JavaScript
97 lines
5.0 KiB
JavaScript
// 只读:订单管理页实时状态探测(隐藏标签页,跑完关闭)
|
|
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);
|