功能完成
Docker Build and Push / build-image (push) Successful in 2m53s

This commit is contained in:
cheney
2026-08-07 14:56:27 +08:00
parent b9ec6fbe0b
commit a5bc8ef1f1
5 changed files with 77 additions and 23 deletions
+36
View File
@@ -0,0 +1,36 @@
// 定时任务失败报警: 向 Alert 表插入一条错误记录
// 供 server/cron.js 与 scripts/scheduler/index.js 共用
// 首页报警 tab 会展示 Alert 表, 字段按错误语义填充 (indicatorName=任务+错误摘要)
import 'dotenv/config'
import mysql from 'mysql2/promise'
function parseUrl(url) {
const u = new URL(url)
return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\//, '') }
}
export async function insertCronAlert(taskName, message) {
if (!process.env.DATABASE_URL) {
console.error('[cron-alert] 缺少 DATABASE_URL, 跳过报警写入')
return
}
let conn = null
try {
conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
// 用 Asia/Shanghai 时区的当天日期, 避免 UTC 偏移导致 period 显示成昨天
const today = new Date().toLocaleDateString('en-CA', { timeZone: 'Asia/Shanghai' })
const cleanMsg = String(message || '').replace(/\s+/g, ' ').trim()
const name = ('[cron] ' + taskName + ' 失败: ' + cleanMsg).slice(0, 180)
await conn.execute(
'INSERT INTO `Alert` (`indicatorName`, `period`, `valueA`, `valueB`, `sourceA`, `sourceB`, `diff`, `diffRatio`, `createdAt`) ' +
'VALUES (?, ?, 0, 0, ?, ?, 0, 0, NOW(3))',
[name, today, 'cron', 'error'],
)
console.error('[cron-alert] 已写入报警:', name)
} catch (e) {
// 报警写入本身失败 (如 DB 不可达) 不能反噬主流程/再次抛错, 只记录
console.error('[cron-alert] 写入报警失败:', e.message)
} finally {
if (conn) await conn.end().catch(() => {})
}
}
+11 -8
View File
@@ -1,13 +1,14 @@
import cron from 'node-cron';
import { trigger as triggerUSDCNYRate } from '../scraper/usd-cny-rate.scraper.js';
import { trigger as triggerSH300Data } from '../scraper/sh300.scraper.js';
import { insertCronAlert } from '../lib/cron-alert.js';
console.log('='.repeat(70));
console.log('🚀 启动定时任务调度器');
console.log('='.repeat(70));
// 注册定时任务 - USD-CNY 汇率数据
const jobUSDCNY = cron.schedule('0 0 * * *', async () => {
// 注册定时任务 - USD-CNY 汇率数据 (每天 6:20, 出错生成报警)
const jobUSDCNY = cron.schedule('20 6 * * *', async () => {
console.log('\n' + '='.repeat(70));
console.log('⏰ 执行定时任务: USD-CNY 汇率数据更新');
console.log('📅 执行时间:', new Date().toISOString());
@@ -18,6 +19,7 @@ const jobUSDCNY = cron.schedule('0 0 * * *', async () => {
console.log('\n✅ 任务执行成功!');
} catch (error) {
console.error('\n❌ 任务执行失败:', error.message);
await insertCronAlert('USD-CNY 汇率更新', error.message);
} finally {
console.log('='.repeat(70));
}
@@ -26,8 +28,8 @@ const jobUSDCNY = cron.schedule('0 0 * * *', async () => {
timezone: 'Asia/Shanghai'
});
// 注册定时任务 - 沪深300指数数据
const jobSH300 = cron.schedule('0 0 * * *', async () => {
// 注册定时任务 - 沪深300指数数据 (每天 6:30, 出错生成报警)
const jobSH300 = cron.schedule('30 6 * * *', async () => {
console.log('\n' + '='.repeat(70));
console.log('⏰ 执行定时任务: 沪深300指数数据更新');
console.log('📅 执行时间:', new Date().toISOString());
@@ -38,6 +40,7 @@ const jobSH300 = cron.schedule('0 0 * * *', async () => {
console.log('\n✅ 任务执行成功!');
} catch (error) {
console.error('\n❌ 任务执行失败:', error.message);
await insertCronAlert('沪深300指数更新', error.message);
} finally {
console.log('='.repeat(70));
}
@@ -47,12 +50,12 @@ const jobSH300 = cron.schedule('0 0 * * *', async () => {
});
console.log('✅ 定时任务已启动: USD-CNY 汇率数据更新');
console.log('📅 执行表达式: 0 0 * * * (每天 0 点)');
console.log('📅 下次执行时间: 明天 00:00');
console.log('📅 执行表达式: 20 6 * * * (每天 6:20)');
console.log('📅 下次执行时间: 明天 06:20');
console.log('');
console.log('✅ 定时任务已启动: 沪深300指数数据更新');
console.log('📅 执行表达式: 0 0 * * * (每天 0 点)');
console.log('📅 下次执行时间: 明天 00:00');
console.log('📅 执行表达式: 30 6 * * * (每天 6:30)');
console.log('📅 下次执行时间: 明天 06:30');
console.log('');
console.log('按 Ctrl+C 停止...');
console.log('='.repeat(70));