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 7906f3c36d
commit 14f46cf318
20 changed files with 2453 additions and 452 deletions
+40
View File
@@ -165,6 +165,44 @@ test('getSpace includes schedule snapshot when schedule service is available', a
},
];
},
async listUpcomingItems({ userId }) {
assert.equal(userId, 'user-1');
return [
{
id: 'item-2',
kind: 'event',
title: '早起跑步',
description: null,
status: 'active',
startAt: Date.UTC(2026, 6, 1, 22, 0, 0),
endAt: Date.UTC(2026, 6, 1, 22, 30, 0),
dueAt: null,
allDay: false,
timezone: 'Asia/Shanghai',
location: null,
createdAt: 11,
updatedAt: 21,
},
];
},
async listUpcomingReminders({ userId }) {
assert.equal(userId, 'user-1');
return [
{
id: 'rem-1',
itemId: 'item-2',
itemTitle: '早起跑步',
itemKind: 'event',
itemTimezone: 'Asia/Shanghai',
itemStartAt: Date.UTC(2026, 6, 1, 22, 0, 0),
itemEndAt: Date.UTC(2026, 6, 1, 22, 30, 0),
remindAt: Date.UTC(2026, 6, 1, 21, 55, 0),
offsetMinutes: 5,
channel: 'wechat',
status: 'pending',
},
];
},
async listDigestSubscriptions({ userId, digestType, status }) {
assert.equal(userId, 'user-1');
assert.equal(digestType, 'todo_day');
@@ -189,6 +227,8 @@ test('getSpace includes schedule snapshot when schedule service is available', a
const space = await service.getSpace('user-1');
assert.equal(space.schedule.todayTodoItems[0].title, '跟进报价单');
assert.equal(space.schedule.upcomingItems[0].title, '早起跑步');
assert.equal(space.schedule.upcomingReminders[0].itemTitle, '早起跑步');
assert.equal(space.schedule.digestSubscriptions[0].hour, 7);
});