Files
memind/scheduled-task-worker.mjs
T
john 005612029f
Memind CI / Test, build, and release guards (push) Failing after 5s
Fix scheduled task page delivery unlock and notification links.
Finalize page delivery contracts after worker execution, fetch session messages via tkmindProxy, and append public page URLs to WeChat notifications.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 09:06:07 +08:00

135 lines
4.3 KiB
JavaScript

import {
executeScheduledTask,
formatScheduledTaskDeliveryMessage,
} from './scheduled-task-executor.mjs';
export function startScheduledTaskWorker({
scheduledTaskService,
scheduleService = null,
executeTask = executeScheduledTask,
userAuth = null,
tkmindProxy = null,
sessionSnapshotService = null,
notificationDispatcher = null,
pool = null,
h5Root = null,
logger = console,
intervalMs = Number(process.env.H5_SCHEDULED_TASK_SCAN_INTERVAL_MS ?? 30_000),
maxAttempts = Number(process.env.H5_SCHEDULED_TASK_MAX_ATTEMPTS ?? 3),
executionTimeoutMs = Number(process.env.H5_SCHEDULED_TASK_EXECUTION_TIMEOUT_MS ?? 15 * 60 * 1000),
runOnStart = true,
} = {}) {
const sendScheduleNotification =
notificationDispatcher?.sendScheduleNotification?.bind(notificationDispatcher) ??
null;
const canExecute =
scheduledTaskService
&& typeof executeTask === 'function'
&& userAuth
&& tkmindProxy;
if (!canExecute) {
return { stop() {} };
}
let stopped = false;
let running = false;
const deliverTaskResult = async (task, deliveryText) => {
const text = formatScheduledTaskDeliveryMessage(task, deliveryText);
const notifyChannel = task.notifyChannel ?? 'both';
if (scheduleService?.createUserNotification && (notifyChannel === 'web' || notifyChannel === 'both')) {
await scheduleService.createUserNotification({
userId: task.userId,
channel: 'web',
notificationType: 'scheduled_task_result',
title: `定时任务完成:${task.title}`,
body: text,
data: {
taskId: task.id,
recurrence: task.recurrence,
},
});
}
if (
(notifyChannel === 'wechat' || notifyChannel === 'both')
&& typeof sendScheduleNotification === 'function'
) {
await sendScheduleNotification({ userId: task.userId, text });
}
};
const runOnce = async () => {
if (running || stopped) return;
running = true;
try {
const dueTasks = await scheduledTaskService.listDueTasks({ limit: 10 });
for (const candidate of dueTasks) {
const task = await scheduledTaskService.lockTask(candidate.id);
if (!task) continue;
try {
await scheduledTaskService.markTaskRunning(task);
const result = await executeTask(task, {
userAuth,
tkmindProxy,
sessionSnapshotService,
pool,
h5Root,
timeoutMs: executionTimeoutMs,
logger,
});
await deliverTaskResult(task, result.deliveryText);
await scheduledTaskService.markTaskSucceeded(task, {
result: {
deliveryText: result.deliveryText,
},
deliveryText: result.deliveryText,
sessionId: result.sessionId,
requestId: result.requestId,
});
} catch (err) {
logger.warn?.('Scheduled task execution failed:', err);
const failedTask = await scheduledTaskService.markTaskFailed(task, err, {
maxAttempts,
});
if (failedTask.status === 'failed' && scheduleService?.createUserNotification) {
await scheduleService.createUserNotification({
userId: task.userId,
channel: 'web',
notificationType: 'scheduled_task_failed',
title: `定时任务失败:${task.title}`,
body: String(failedTask.lastError ?? '执行失败'),
data: { taskId: task.id },
}).catch(() => {});
}
if (
failedTask.status === 'failed'
&& (task.notifyChannel === 'wechat' || task.notifyChannel === 'both')
&& typeof sendScheduleNotification === 'function'
) {
await sendScheduleNotification({
userId: task.userId,
text: `定时任务失败:${task.title}\n${failedTask.lastError ?? '执行失败'}`,
}).catch(() => {});
}
}
}
} catch (err) {
logger.warn?.('Scheduled task worker failed:', err);
} finally {
running = false;
}
};
const timer = setInterval(runOnce, Math.max(1000, Number(intervalMs) || 30_000));
timer.unref?.();
if (runOnStart) void runOnce();
return {
runOnce,
stop() {
stopped = true;
clearInterval(timer);
},
};
}