const ProductApiClient = require("../src/services/productApiClient"); const axios = require("axios"); // 本地服务端实例 const localClient = new ProductApiClient({ // 根据实际部署环境修改baseURL baseURL: "http://localhost:8991", }); // 外网服务端实例 const serverClient = { // baseURL: "http://192.168.1.107:8080", // 本地 baseURL: "https://digital.sohomall.jp/prod-api", // 外网 timeout: 10000 * 30, // 30秒 params: { pageNum: 1, pageSize: 500, }, }; axios.defaults.baseURL = serverClient.baseURL; axios.defaults.timeout = serverClient.timeout; // 获取商品列表和抓取配置的执行频率 const frequency = 1000 * 60 * 60 * 24; // 24小时 // 设置抓取配置 const config = { platform: "amazon", // 平台 needScreenshot: true, // 是否需要截图 monitorFrequency: 2, // 监控频率(小时) goodsList: [], // 商品列表 isRunning: false, // 是否正在执行抓取任务 timer: null, // 定时器 }; /** * 获取商品信息 * @param {Object} goods - 商品对象 * @param {boolean} isRetry - 是否为重试操作 * @returns {Promise} - 返回商品信息或null */ async function fetchProductInfo(goods, isRetry = false) { try { console.log(`${isRetry ? "重试" : "开始"}抓取商品: ${goods.goodsSkuSn}`); const productInfo = await localClient.getProductInfo({ url: goods.goodsSkuUrl, platform: goods.platform, needScreenshot: config.needScreenshot, }); console.log( `${isRetry ? "重试" : "商品"} 抓取成功: ${goods.goodsSkuSn} - ${new Date().toLocaleString()}` ); console.log(productInfo); return productInfo; } catch (error) { console.error( `抓取失败: ${goods.goodsSkuSn} - ${new Date().toLocaleString()}`, error.message ); return null; } } /** * 保存商品信息到服务器 * @param {Object} goods - 商品对象 * @param {Object} productInfo - 抓取到的商品信息 * @returns {Promise} - 是否保存成功 */ async function saveProductInfo(goods, productInfo) { try { console.log( `开始保存商品信息: ${goods.goodsSkuSn} - ${new Date().toLocaleString()}` ); const { title, price, sku, screenshotUrl } = productInfo[0]; const res = await axios.post( serverClient.baseURL + "/system/operationWarnresult/receiveLatestGoodsInfo", { title, price: price.toString(), sku, url: goods.goodsSkuUrl, screenshotUrl: screenshotUrl, } ); console.log(res.data); return true; } catch (saveError) { console.error( `商品信息保存失败: ${goods.goodsSkuSn} - ${new Date().toLocaleString()}`, saveError.message ); return false; } } /** * 处理单个商品的抓取和保存 * @param {Object} goods - 商品对象 * @returns {Promise} */ async function processProduct(goods) { // 第一次尝试抓取 let productInfo = await fetchProductInfo(goods); // 如果第一次抓取成功,保存结果 if (productInfo) { await saveProductInfo(goods, productInfo); return; } // 第一次失败,进行重试 productInfo = await fetchProductInfo(goods, true); // 如果重试成功,保存结果 if (productInfo) { await saveProductInfo(goods, productInfo); } // 重试失败,跳过该商品 } /** * 获取抓取配置 * @returns {Promise} */ async function fetchConfig() { try { console.log(`开始获取抓取配置: ${new Date().toLocaleString()}`); const res = await axios.get(serverClient.baseURL + "/system/operationWarnconfig/noVerifyList", { params: serverClient.params, }); console.log(res.data); const { rows } = res.data; if (rows.length > 0) { config.monitorFrequency = rows[0].monitorFrequency; } else { config.monitorFrequency = 2; // 默认2小时 } console.log(`抓取频率设置为 ${config.monitorFrequency} 小时`); return true; } catch (error) { console.error(`获取抓取配置失败: ${new Date().toLocaleString()}`, error.message); return false; } } /** * 获取商品列表并处理 * @returns {Promise} */ async function fetchGoodsListAndProcess() { if (config.isRunning) { console.log(`上一次任务尚未完成,跳过本次执行: ${new Date().toLocaleString()}`); return; } config.isRunning = true; console.log(`开始执行抓取任务: ${new Date().toLocaleString()}`); try { const res = await axios.get(serverClient.baseURL + "/system/operationGoods/noVerifyList", { params: { ...serverClient.params, isDisabled: 1, }, }); console.log(res.data); const { rows } = res.data; config.goodsList = rows; // 使用for...of循环按顺序处理每个商品 for (const goods of config.goodsList) { await processProduct(goods); } console.log("所有商品抓取完成", new Date().toLocaleString()); } catch (error) { console.error(`获取商品列表失败: ${new Date().toLocaleString()}`, error.message); } finally { config.isRunning = false; } } /** * 启动定时任务 */ async function startScheduler() { // 先获取配置 await fetchConfig(); // 立即执行一次 await fetchGoodsListAndProcess(); // 清除之前的定时器(如果存在) if (config.timer) { clearInterval(config.timer); } // 设置定时器,根据monitorFrequency的小时数定时执行 const intervalMs = config.monitorFrequency * 60 * 60 * 1000; // 转换为毫秒 console.log(`设置定时任务,每 ${config.monitorFrequency} 小时执行一次,下次执行时间: ${new Date(Date.now() + intervalMs).toLocaleString()}`); config.timer = setInterval(async () => { console.log(`定时任务触发: ${new Date().toLocaleString()}`); // 重新获取配置(频率可能会改变) await fetchConfig(); // 执行抓取处理 await fetchGoodsListAndProcess(); // 如果monitorFrequency发生变化,重新设置定时器 const newIntervalMs = config.monitorFrequency * 60 * 60 * 1000; if (newIntervalMs !== intervalMs) { console.log(`监控频率已变更为 ${config.monitorFrequency} 小时,重新设置定时器`); clearInterval(config.timer); startScheduler(); // 重新启动调度器 } }, intervalMs); // 添加防止程序崩溃的错误处理 process.on('uncaughtException', (error) => { console.error(`未捕获的异常: ${new Date().toLocaleString()}`, error); // 尝试继续执行定时任务 if (!config.timer) { startScheduler(); } }); } // 启动调度器 startScheduler(); // 输出启动信息 console.log(`抓取服务已启动: ${new Date().toLocaleString()}`); console.log(`初始监控频率: ${config.monitorFrequency} 小时`);