fix(schedule): route daily set-remind phrases through ITL with recurrence
Memind CI / Test, build, and release guards (push) Failing after 3m4s

Support colloquial forms like「设置每天18:00提醒打卡」, persist daily metadata on
commit, and schedule the next reminder after each delivery.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-24 17:42:08 +08:00
parent 4100062ba8
commit 2319c9c808
6 changed files with 77 additions and 2 deletions
+34
View File
@@ -29,6 +29,16 @@ function nowMs() {
function rowToItem(row) {
if (!row) return null;
let metadata = null;
if (row.metadata_json != null && row.metadata_json !== '') {
try {
metadata = typeof row.metadata_json === 'object'
? row.metadata_json
: JSON.parse(row.metadata_json);
} catch {
metadata = null;
}
}
return {
id: row.id,
userId: row.user_id,
@@ -42,6 +52,7 @@ function rowToItem(row) {
allDay: Boolean(row.all_day),
timezone: row.timezone || DEFAULT_TIMEZONE,
location: row.location ?? null,
metadata,
createdAt: Number(row.created_at),
updatedAt: Number(row.updated_at),
};
@@ -660,6 +671,28 @@ export function createScheduleService(pool, options = {}) {
return { ...reminder, status: 'sent', sentAt: now, lockedUntil: null };
};
const scheduleNextDailyReminder = async (reminder) => {
if (!reminder?.userId || !reminder?.itemId) return null;
const item = await getItem({ userId: reminder.userId, itemId: reminder.itemId });
if (!item || item.status !== 'active' || item.metadata?.recurrence !== 'daily') return null;
const hour = Number(item.metadata.dailyHour);
const minute = Number(item.metadata.dailyMinute ?? 0);
if (!Number.isInteger(hour) || hour < 0 || hour > 23) return null;
if (!Number.isInteger(minute) || minute < 0 || minute > 59) return null;
const remindAt = nextDailyRunAt({
hour,
minute,
timezone: item.timezone || defaultTimezone,
now: clock.now() + 1000,
});
return createReminder({
userId: reminder.userId,
itemId: item.id,
remindAt,
channel: reminder.channel ?? 'wechat',
});
};
const markReminderCancelled = async (reminder, reason, { now = clock.now() } = {}) => {
const lastError = String(reason ?? '已取消').slice(0, 500);
await pool.query(
@@ -978,6 +1011,7 @@ export function createScheduleService(pool, options = {}) {
listDueReminders,
lockReminder,
markReminderSent,
scheduleNextDailyReminder,
markReminderCancelled,
markReminderFailed,
buildReminderText,