save
This commit is contained in:
parent
359fd38bd6
commit
0ad25ce261
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/*
|
||||
~$*
|
||||
11
Readme.md
Normal file
11
Readme.md
Normal file
@ -0,0 +1,11 @@
|
||||
## 开票业务
|
||||
excle 结算状态过滤
|
||||
登录
|
||||
总进度
|
||||
|
||||
|
||||
|
||||
## 避免锁屏
|
||||
打开目录:D:\Cheney\2\geely-kaipiao\
|
||||
地址栏删空,输入 cmd,打开黑框。
|
||||
输入命令:bun.exe keep-awake.js
|
||||
@ -62,7 +62,13 @@ function readTeamIds() {
|
||||
const teamIds = [];
|
||||
for (const row of rows) {
|
||||
const val = row['团号'];
|
||||
if (val && /^TEAM-\d+$/.test(String(val).trim())) teamIds.push(String(val).trim());
|
||||
const kaipiaoStatus = row['开票状态'];
|
||||
if (!val || !/^TEAM-\d+$/.test(String(val).trim())) continue;
|
||||
if (kaipiaoStatus && String(kaipiaoStatus).trim() !== '未开票') {
|
||||
console.log(` ⚠ ${String(val).trim()} 开票状态: "${kaipiaoStatus}",跳过`);
|
||||
continue;
|
||||
}
|
||||
teamIds.push(String(val).trim());
|
||||
}
|
||||
return teamIds;
|
||||
}
|
||||
|
||||
164
package-lock.json
generated
Normal file
164
package-lock.json
generated
Normal file
@ -0,0 +1,164 @@
|
||||
{
|
||||
"name": "geely-kaipiao",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "geely-kaipiao",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"playwright": "^1.61.0",
|
||||
"xlsx": "^0.18.5"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.61.0",
|
||||
"resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.61.0.tgz",
|
||||
"integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.61.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.61.0",
|
||||
"resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.61.0.tgz",
|
||||
"integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
|
||||
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"cfb": "~1.2.1",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.1",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
read_team_ids.js
Normal file
39
read_team_ids.js
Normal file
@ -0,0 +1,39 @@
|
||||
const XLSX = require('xlsx');
|
||||
const path = require('path');
|
||||
|
||||
// 读取 Excel 文件(项目根目录)
|
||||
const filePath = path.join(__dirname, '新建 XLSX 工作表.xlsx');
|
||||
const workbook = XLSX.readFile(filePath);
|
||||
|
||||
// 目标 sheet
|
||||
const sheetName = '2026';
|
||||
const sheet = workbook.Sheets[sheetName];
|
||||
|
||||
if (!sheet) {
|
||||
console.error(`Sheet "${sheetName}" 不存在`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 将 sheet 转为 JSON 数组(以第一行为表头)
|
||||
const rows = XLSX.utils.sheet_to_json(sheet, { defval: null });
|
||||
|
||||
// 查找"团号"列(兼容表头可能的空格差异)
|
||||
const headerRow = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: null })[0];
|
||||
const tuanHaoIndex = headerRow.findIndex(h => h && String(h).trim() === '团号');
|
||||
|
||||
if (tuanHaoIndex === -1) {
|
||||
console.error('未找到"团号"列');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 提取所有 TEAM-xxxx 格式的 ID
|
||||
const teamIds = [];
|
||||
for (const row of rows) {
|
||||
const val = row['团号'];
|
||||
if (val && /^TEAM-\d+$/.test(String(val).trim())) {
|
||||
teamIds.push(String(val).trim());
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`找到 ${teamIds.length} 个 TEAM ID:`);
|
||||
teamIds.forEach(id => console.log(id));
|
||||
65
src/login.js
Normal file
65
src/login.js
Normal file
@ -0,0 +1,65 @@
|
||||
const { chromium } = require('playwright');
|
||||
const path = require('path');
|
||||
|
||||
const LOGIN_URL = 'https://superstar.geelytravel.com/new-tmc/login';
|
||||
const STATE_PATH = process.env.STATE_PATH || path.resolve(__dirname, 'state.json');
|
||||
const USER_AGENT =
|
||||
process.env.USER_AGENT ||
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
||||
|
||||
async function run() {
|
||||
// 1. 启动本地 Chrome 浏览器,并添加排除自动化检测的参数
|
||||
const browser = await chromium.launch({
|
||||
headless: false,
|
||||
channel: 'chrome',
|
||||
args: [
|
||||
'--disable-blink-features=AutomationControlled',
|
||||
'--start-maximized',
|
||||
],
|
||||
});
|
||||
|
||||
// 2. 设置上下文,伪造 User-Agent
|
||||
const context = await browser.newContext({
|
||||
noViewport: true,
|
||||
userAgent: USER_AGENT,
|
||||
});
|
||||
|
||||
const page = await context.newPage();
|
||||
|
||||
try {
|
||||
// 3. 访问登录页
|
||||
await page.goto(LOGIN_URL);
|
||||
|
||||
// 输入信息:如需自动填写,可通过环境变量 USERNAME / PASSWORD 传入
|
||||
if (process.env.USERNAME) {
|
||||
await page.getByPlaceholder('用户名').fill(process.env.USERNAME);
|
||||
}
|
||||
|
||||
if (process.env.PASSWORD) {
|
||||
await page.getByPlaceholder('密码').fill(process.env.PASSWORD);
|
||||
}
|
||||
|
||||
// 4. 进入暂停状态,此时手动滑动验证码并完成登录
|
||||
console.log('请在浏览器中手动完成滑块验证,登录进入系统首页后回到终端按 Enter...');
|
||||
await page.pause();
|
||||
|
||||
// 5. 关键:等待页面跳转完成,确保已进入登录后的状态
|
||||
// 如需按业务页面确认登录成功,可打开下面一行并按实际 URL 片段调整。
|
||||
// await page.waitForURL('**/report/**', { timeout: 60000 });
|
||||
|
||||
// 额外等待 3 秒,确保 Cookie 和 LocalStorage 完整写入内存
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// 6. 保存状态
|
||||
await context.storageState({ path: STATE_PATH });
|
||||
console.log(`登录状态已保存至:${STATE_PATH}`);
|
||||
} finally {
|
||||
await context.close();
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
270
src/state.json
Normal file
270
src/state.json
Normal file
File diff suppressed because one or more lines are too long
BIN
新建 XLSX 工作表.xlsx
Normal file
BIN
新建 XLSX 工作表.xlsx
Normal file
Binary file not shown.
72
脚本/unlock.js
Normal file
72
脚本/unlock.js
Normal file
@ -0,0 +1,72 @@
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const CHECK_INTERVAL = 30 * 1000; // 每 30 秒检查一次
|
||||
const IDLE_LIMIT = 60 * 1000; // 空闲超过 60 秒则触发
|
||||
|
||||
// 封装执行 PowerShell 的方法(使用 UTF-16LE 编码避免引号转义问题)
|
||||
function runPs(script) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
||||
exec(`powershell -NoProfile -EncodedCommand "${encoded}"`, (err, stdout) => {
|
||||
if (err) reject(err);
|
||||
else resolve(stdout.trim());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 获取当前系统空闲毫秒数
|
||||
async function getIdleTime() {
|
||||
const ps = `
|
||||
Add-Type @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public class IdleChecker {
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
|
||||
public struct LASTINPUTINFO {
|
||||
public uint cbSize;
|
||||
public uint dwTime;
|
||||
}
|
||||
public static uint GetIdleMilliseconds() {
|
||||
LASTINPUTINFO lii = new LASTINPUTINFO();
|
||||
lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
|
||||
GetLastInputInfo(ref lii);
|
||||
return (uint)Environment.TickCount - lii.dwTime;
|
||||
}
|
||||
}
|
||||
"@
|
||||
[IdleChecker]::GetIdleMilliseconds()
|
||||
`;
|
||||
const result = await runPs(ps);
|
||||
return parseInt(result, 10);
|
||||
}
|
||||
|
||||
// 模拟按下 F15 键(无任何副作用)
|
||||
async function pressF15() {
|
||||
const ps = `
|
||||
$wshell = New-Object -ComObject wscript.shell;
|
||||
$wshell.SendKeys('{F15}');
|
||||
`;
|
||||
await runPs(ps);
|
||||
}
|
||||
|
||||
console.log('✅ 防锁屏脚本已启动 (PowerShell 模式)');
|
||||
console.log(`⏱️ 检测到空闲 ${IDLE_LIMIT/1000} 秒后,自动模拟按键防止锁屏。`);
|
||||
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const idleMs = await getIdleTime();
|
||||
if (idleMs > IDLE_LIMIT) {
|
||||
console.log(`🔄 已空闲 ${Math.round(idleMs/1000)} 秒,模拟 F15 重置计时器...`);
|
||||
await pressF15();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('⚠️ 执行出错:', e.message);
|
||||
}
|
||||
}, CHECK_INTERVAL);
|
||||
|
||||
// 按 Ctrl+C 退出时提示
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n🛑 脚本已停止');
|
||||
process.exit(0);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user