feat(schedule): 待办提醒推送、行事历仅展示提醒与管理 API

单次提醒 worker 投递、local 时间写入校验、未来 7 天提醒列表与忽略/批量删除;行事历去掉事项重复展示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-06-30 02:02:58 +08:00
parent 06eb129a28
commit 2c91689692
20 changed files with 2453 additions and 452 deletions
+44
View File
@@ -17,6 +17,50 @@ export function startScheduleReminderWorker({
if (running || stopped) return;
running = true;
try {
const dueReminders = await scheduleService.listDueReminders({ limit: 50 });
for (const candidate of dueReminders) {
const reminder = await scheduleService.lockReminder(candidate.id);
if (!reminder) continue;
try {
const text = await scheduleService.buildReminderText(reminder);
if (!text) {
await scheduleService.markReminderCancelled(reminder, '事项已失效或不存在');
continue;
}
await scheduleService.createUserNotification?.({
userId: reminder.userId,
channel: 'web',
notificationType: 'schedule_reminder',
title: '待办提醒',
body: text,
data: {
reminderId: reminder.id,
itemId: reminder.itemId,
},
});
if (reminder.channel === 'wechat') {
await sendWechatTextToUser(reminder.userId, text);
}
await scheduleService.logDelivery({
reminderId: reminder.id,
userId: reminder.userId,
channel: reminder.channel,
status: 'success',
});
await scheduleService.markReminderSent(reminder);
} catch (err) {
logger.warn?.('Schedule reminder delivery failed:', err);
await scheduleService.logDelivery({
reminderId: reminder.id,
userId: reminder.userId,
channel: reminder.channel,
status: 'failed',
errorMessage: err instanceof Error ? err.message : String(err),
}).catch(() => {});
await scheduleService.markReminderFailed(reminder, err, { maxAttempts });
}
}
const due = await scheduleService.listDueDigestSubscriptions({ limit: 50 });
for (const candidate of due) {
const subscription = await scheduleService.lockDigestSubscription(candidate.id);