Linux设置定时作业执行node.js脚本

需求

定时作业使用puppeteer工具后台打开sapui5 web应用,截图发送到企业微信

1.timer和service文件路径/etc/systemd/system/

service代码

复制代码
[Unit]
Description=1000DayPushWx Service
After=network.target

[Service]
Type=oneshot
User=root
Group=root
WorkingDirectory=/opt/zauto
ExecStart=/usr/bin/node /opt/zauto/1000DayPushWx.js
StandardOutput=journal
StandardError=journal
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

js脚本路径

timer代码

复制代码
[Unit]
Description=Run 1000DayPushWx daily
Requires=1000DayPushWx.service

[Timer]
# 每天上午 9:10 执行
OnCalendar=*-*-* 09:10:00
Persistent=true

[Install]
WantedBy=timers.target 

2.node.js脚本代码

javascript 复制代码
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const os = require('os');

// 日志写入函数
function writeLog(message) {
    const logPath = path.join(__dirname, '1000_day_push.log');
    const time = new Date().toISOString();
    fs.appendFileSync(logPath, `[${time}] ${message}\n`);
    console.log(`[${time}] ${message}`);
}

// 根据操作系统确定Chrome路径
function getChromePath() {
    const platform = os.platform();
    
    if (platform === 'win32') {
        return 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe';
    } else if (platform === 'linux') {
        // Linux 系统下的 Chrome 路径
        const possiblePaths = [
            '/usr/bin/google-chrome',
            '/usr/bin/google-chrome-stable',
            '/usr/bin/chromium',
            '/usr/bin/chromium-browser'
        ];
        
        for (const chromePath of possiblePaths) {
            if (fs.existsSync(chromePath)) {
                return chromePath;
            }
        }
        
        // 如果找不到,让 Puppeteer 使用内置的 Chromium
        return null;
    }
    
    return null;
}

(async () => {
    const chromePath = getChromePath();
    const launchOptions = {
        headless: true,
        args: [
            '--window-size=800,1700',
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--no-first-run',
            '--no-zygote',
            '--single-process'
        ]
    };
    
    // 如果找到了 Chrome 路径,则使用它
    if (chromePath) {
        launchOptions.executablePath = chromePath;
        writeLog(`使用 Chrome 路径: ${chromePath}`);
    } else {
        writeLog('使用 Puppeteer 内置的 Chromium');
    }
    
    const browser = await puppeteer.launch(launchOptions);
    
    const page = await browser.newPage();
    await page.setViewport({ width: 800, height: 1700 });
    
    // 设置更长的超时时间
    await page.setDefaultTimeout(60000);
    
    // 设置 Basic Auth 账号密码
    await page.authenticate({
        username: 账号,
        password: 密码
    });
    writeLog('已设置Basic Auth');
    
    // 访问页面,等待网络空闲
    await page.goto('http://ip/zfiori_fi06/index.html', {
        waitUntil: 'networkidle2',
        timeout: 60000
    });
    writeLog('页面打开成功');
    
    // 等待 UI5 应用完全初始化
    writeLog('等待UI5应用初始化...');
    await page.waitForFunction(() => {
        return window.sap && 
               window.sap.ui && 
               window.sap.ui.getCore() && 
               window.sap.ui.getCore().getComponent;
    }, { timeout: 30000 });
    writeLog('UI5应用初始化完成');
    
    // 等待页面元素出现,增加重试机制
    writeLog('查找page元素...');
    let pageElement = null;
    let retryCount = 0;
    const maxRetries = 3;
    
    while (!pageElement && retryCount < maxRetries) {
        try {
            pageElement = await page.waitForSelector('[id$="page"]', {
                timeout: 20000,
                visible: true
            });
            writeLog('page元素加载完成');
            break;
        } catch (error) {
            retryCount++;
            writeLog(`第 ${retryCount} 次尝试失败,等待 3 秒后重试...`);
            await page.waitForTimeout(3000);
        }
    }
    
    if (!pageElement) {
        writeLog('ERROR: 无法找到page元素,保存调试截图');
        await page.screenshot({ path: 'debug-page-not-found.png' });
        throw new Error('无法找到page元素');
    }
    
    // 等待页面完全渲染
    await new Promise(resolve => setTimeout(resolve, 5000));
    writeLog('页面渲染完成');
    
    // 等待 VizFrame 元素加载
    writeLog('等待day viz空间加载开始');
    
    const vizSelectors = [
        '[id$="idVizFrame5"]',
        '[id$="idVizFrame6"]',
        '[id$="idVizFrame7"]'
    ];
    
    for (const selector of vizSelectors) {
        try {
            await page.waitForSelector(selector, { 
                timeout: 30000,
                visible: true 
            });
            writeLog(`VizFrame元素 ${selector} 加载完成`);
        } catch (error) {
            writeLog(`ERROR: VizFrame元素 ${selector} 加载失败`);
            // 保存调试截图
            await page.screenshot({ path: `debug-vizframe-${selector}.png` });
            throw error;
        }
    }
    
    writeLog('等待day viz空间加载完成');
    
    // 等待额外时间确保图表完全渲染
    await new Promise(resolve => setTimeout(resolve, 3000));
    writeLog('图表渲染完成');
    
    // 查找并点击截图按钮
    writeLog('查找截图按钮...');
    try {
        await page.waitForSelector('[id$="idjtButton"]', { 
            timeout: 15000,
            visible: true 
        });
        await page.click('[id$="idjtButton"]');
        writeLog('已点击截图按钮');
    } catch (error) {
        writeLog('ERROR: 无法找到或点击截图按钮');
        await page.screenshot({ path: 'debug-button-not-found.png' });
        throw error;
    }
    
    writeLog('已发送日数据');
    
    // 等待操作完成
    await new Promise(resolve => setTimeout(resolve, 5000));
    writeLog('操作完成');
    
    await browser.close();
    writeLog('浏览器已关闭');
    
})().catch(error => {
    writeLog(`脚本执行失败: ${error.message}`);
    process.exit(1);
});

3.启动定时作业的linux命令

启用定时器(开机自启)

systemctl enable 1000DayPushWx.timer

启动定时器

systemctl start 1000DayPushWx.timer

检查状态

systemctl status 1000DayPushWx.timer

看到下图结果,说明启动成功

相关推荐
David猪大卫5 小时前
【Linux修炼】环境变量、进程地址空间
linux·c语言·经验分享·笔记·学习·ubuntu·centos
叼烟扛炮7 小时前
Linux 学习笔记(五十六):IO 多路复用初探——select 的“三宗罪”
linux·服务器·数据库·笔记·学习·php
Ruiery7 小时前
Linux 6.6内核内存管理深度解析(十五):CMA 连续内存分配器
linux·运维·服务器
测试界的小码农8 小时前
【Linux 命令实战(01):认识 Linux 终端,从零掌握第一个命令】
linux·运维·服务器
布裘8 小时前
【银河麒麟】重启后时区丢失?排查发现 localtime 被业务程序替换成硬链接
linux·运维·网络
GeW11 小时前
高端化转型进入“补底层”阶段:Linux与数据库的再定位
linux
silent11 小时前
龙芯3B6000M运行Yolov8+FastApi
linux·loongarch·loongnix·龙芯
西西弗Sisyphus12 小时前
理解 Linux 动态库加载:LD_LIBRARY_PATH
linux
AR-26710-12 小时前
Linux Day31——实操启停服务改端口重启
linux·运维
叼烟扛炮13 小时前
Linux 学习笔记(五十八):IO 多路复用封神——epoll 底层原理揭秘
linux·运维·服务器·网络·笔记·学习·php