定时任务性能优化
This commit is contained in:
@@ -54,11 +54,16 @@ export async function GET(
|
||||
}
|
||||
|
||||
async function cronJob() {
|
||||
// 先刷新配置,确保其他任务使用最新配置
|
||||
await refreshConfig();
|
||||
await refreshAllLiveChannels();
|
||||
await refreshOpenList();
|
||||
await refreshRecordAndFavorites();
|
||||
await checkAnimeSubscriptions();
|
||||
|
||||
// 其余任务并行执行
|
||||
await Promise.all([
|
||||
refreshAllLiveChannels(),
|
||||
refreshOpenList(),
|
||||
refreshRecordAndFavorites(),
|
||||
checkAnimeSubscriptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
async function refreshAllLiveChannels() {
|
||||
@@ -164,26 +169,28 @@ async function refreshRecordAndFavorites() {
|
||||
const key = `${source}+${id}`;
|
||||
let promise = detailCache.get(key);
|
||||
if (!promise) {
|
||||
// 立即缓存Promise,避免并发时的竞态条件
|
||||
promise = fetchVideoDetail({
|
||||
source,
|
||||
id,
|
||||
fallbackTitle: fallbackTitle.trim(),
|
||||
})
|
||||
.then((detail) => {
|
||||
// 成功时才缓存结果
|
||||
const successPromise = Promise.resolve(detail);
|
||||
detailCache.set(key, successPromise);
|
||||
return detail;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`获取视频详情失败 (${source}+${id}):`, err);
|
||||
// 失败时从缓存中移除,下次可以重试
|
||||
detailCache.delete(key);
|
||||
return null;
|
||||
});
|
||||
detailCache.set(key, promise);
|
||||
}
|
||||
return promise;
|
||||
};
|
||||
|
||||
for (const user of users) {
|
||||
// 处理单个用户的函数
|
||||
const processUser = async (user: string) => {
|
||||
console.log(`开始处理用户: ${user}`);
|
||||
const storage = getStorage();
|
||||
|
||||
@@ -342,44 +349,54 @@ async function refreshRecordAndFavorites() {
|
||||
|
||||
console.log(`收藏处理完成: ${processedFavorites}/${totalFavorites}`);
|
||||
|
||||
// 如果有更新,发送汇总邮件
|
||||
// 如果有更新,异步发送汇总邮件(不阻塞主流程)
|
||||
if (userUpdates.length > 0) {
|
||||
try {
|
||||
const userEmail = storage.getUserEmail ? await storage.getUserEmail(user) : null;
|
||||
const emailNotifications = storage.getEmailNotificationPreference
|
||||
? await storage.getEmailNotificationPreference(user)
|
||||
: false;
|
||||
(async () => {
|
||||
try {
|
||||
const userEmail = storage.getUserEmail ? await storage.getUserEmail(user) : null;
|
||||
const emailNotifications = storage.getEmailNotificationPreference
|
||||
? await storage.getEmailNotificationPreference(user)
|
||||
: false;
|
||||
|
||||
if (userEmail && emailNotifications) {
|
||||
const config = await getConfig();
|
||||
const emailConfig = config?.EmailConfig;
|
||||
if (userEmail && emailNotifications) {
|
||||
const config = await getConfig();
|
||||
const emailConfig = config?.EmailConfig;
|
||||
|
||||
if (emailConfig?.enabled) {
|
||||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
|
||||
const siteName = config?.SiteConfig?.SiteName || 'MoonTVPlus';
|
||||
if (emailConfig?.enabled) {
|
||||
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
|
||||
const siteName = config?.SiteConfig?.SiteName || 'MoonTVPlus';
|
||||
|
||||
await EmailService.send(emailConfig, {
|
||||
to: userEmail,
|
||||
subject: `📺 收藏更新汇总 - ${userUpdates.length} 部影片有更新`,
|
||||
html: getBatchFavoriteUpdateEmailTemplate(
|
||||
user,
|
||||
userUpdates,
|
||||
siteUrl,
|
||||
siteName
|
||||
),
|
||||
});
|
||||
await EmailService.send(emailConfig, {
|
||||
to: userEmail,
|
||||
subject: `📺 收藏更新汇总 - ${userUpdates.length} 部影片有更新`,
|
||||
html: getBatchFavoriteUpdateEmailTemplate(
|
||||
user,
|
||||
userUpdates,
|
||||
siteUrl,
|
||||
siteName
|
||||
),
|
||||
});
|
||||
|
||||
console.log(`邮件汇总已发送至: ${userEmail} (${userUpdates.length} 个更新)`);
|
||||
console.log(`邮件汇总已发送至: ${userEmail} (${userUpdates.length} 个更新)`);
|
||||
}
|
||||
}
|
||||
} catch (emailError) {
|
||||
console.error(`发送邮件汇总失败 (${user}):`, emailError);
|
||||
}
|
||||
} catch (emailError) {
|
||||
console.error(`发送邮件汇总失败 (${user}):`, emailError);
|
||||
// 邮件发送失败不影响主流程
|
||||
}
|
||||
})().catch(err => console.error(`邮件发送异步任务失败 (${user}):`, err));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`获取用户收藏失败 (${user}):`, err);
|
||||
}
|
||||
};
|
||||
|
||||
// 分批并行处理用户,避免并发过高
|
||||
// 可通过环境变量 CRON_USER_BATCH_SIZE 配置批处理大小,默认为 3
|
||||
const BATCH_SIZE = parseInt(process.env.CRON_USER_BATCH_SIZE || '3', 10);
|
||||
for (let i = 0; i < users.length; i += BATCH_SIZE) {
|
||||
const batch = users.slice(i, i + BATCH_SIZE);
|
||||
console.log(`处理用户批次 ${Math.floor(i / BATCH_SIZE) + 1}/${Math.ceil(users.length / BATCH_SIZE)}: ${batch.join(', ')}`);
|
||||
await Promise.all(batch.map(user => processUser(user)));
|
||||
}
|
||||
|
||||
console.log('刷新播放记录/收藏任务完成');
|
||||
|
||||
@@ -361,31 +361,51 @@ export async function checkSubscription(subscription: AnimeSubscription) {
|
||||
* 检查所有订阅(定时任务调用)
|
||||
*/
|
||||
export async function checkAnimeSubscriptions() {
|
||||
console.log('[AnimeSubscription] 开始检查动漫订阅');
|
||||
|
||||
const config = await getConfig();
|
||||
const animeConfig = config.AnimeSubscriptionConfig;
|
||||
|
||||
if (!animeConfig?.Enabled) {
|
||||
console.log('[AnimeSubscription] 动漫订阅功能未启用,跳过检查');
|
||||
return;
|
||||
}
|
||||
|
||||
const subscriptions = animeConfig.Subscriptions || [];
|
||||
console.log(`[AnimeSubscription] 共有 ${subscriptions.length} 个订阅`);
|
||||
|
||||
const now = Date.now();
|
||||
const MIN_CHECK_INTERVAL = 30 * 60 * 1000; // 30分钟
|
||||
let configChanged = false;
|
||||
let checkedCount = 0;
|
||||
let skippedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const sub of subscriptions) {
|
||||
if (!sub.enabled) continue;
|
||||
if (!sub.enabled) {
|
||||
console.log(`[AnimeSubscription] 跳过已禁用的订阅: ${sub.title}`);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否距离上次检查超过30分钟
|
||||
if (now - sub.lastCheckTime < MIN_CHECK_INTERVAL) {
|
||||
const timeSinceLastCheck = now - sub.lastCheckTime;
|
||||
if (timeSinceLastCheck < MIN_CHECK_INTERVAL) {
|
||||
const remainingMinutes = Math.ceil((MIN_CHECK_INTERVAL - timeSinceLastCheck) / 60000);
|
||||
console.log(`[AnimeSubscription] 跳过 ${sub.title}: 距离上次检查仅 ${Math.floor(timeSinceLastCheck / 60000)} 分钟,还需等待 ${remainingMinutes} 分钟`);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await checkSubscription(sub);
|
||||
console.log(`[AnimeSubscription] 检查订阅: ${sub.title} (源: ${sub.source}, 上次集数: ${sub.lastEpisode})`);
|
||||
const result = await checkSubscription(sub);
|
||||
console.log(`[AnimeSubscription] ${sub.title}: 找到 ${result.found} 个新集数,成功下载 ${result.downloaded} 个`);
|
||||
configChanged = true;
|
||||
checkedCount++;
|
||||
} catch (error) {
|
||||
console.error(`[AnimeSubscription] ${sub.title}: 检查失败`, error);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,5 +413,8 @@ export async function checkAnimeSubscriptions() {
|
||||
if (configChanged) {
|
||||
await db.saveAdminConfig(config);
|
||||
await setCachedConfig(config);
|
||||
console.log('[AnimeSubscription] 配置已更新并保存');
|
||||
}
|
||||
|
||||
console.log(`[AnimeSubscription] 检查完成 - 总计: ${subscriptions.length}, 已检查: ${checkedCount}, 跳过: ${skippedCount}, 失败: ${errorCount}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user