diff --git a/backend/src/timeflow/business/calendar/service.py b/backend/src/timeflow/business/calendar/service.py index 3ef4d95a..60d94e14 100644 --- a/backend/src/timeflow/business/calendar/service.py +++ b/backend/src/timeflow/business/calendar/service.py @@ -17,6 +17,7 @@ FindSchedulesQuery, OccurrenceOverrideAction, RecurringDeleteScope, + ReminderDispositionState, ReminderType, ScheduleBusinessError, ScheduleCategory, @@ -556,6 +557,14 @@ def _delete_recurring_range( def _matches_static_query(schedule: ScheduleSnapshot, query: FindSchedulesQuery) -> bool: title = None if query.title is None else query.title.casefold() location = None if query.location_name is None else query.location_name.casefold() + # 地点型日程的提醒一旦被确认,就跟客户端地图/日历的展示口径保持一致, + # 语音搜索里也不再当作"还需要处理"的日程返回——否则用户已经处理完的 + # 地点提醒还会被助手反复提起。时间型日程没有这个约束,确认后照常可搜。 + if ( + schedule.schedule_type is ScheduleType.LOCATION + and schedule.reminder_disposition_state is ReminderDispositionState.CONFIRMED + ): + return False return ( (query.category is None or schedule.category is query.category) and (title is None or title in schedule.title.casefold()) diff --git a/backend/tests/test_schedule_application_service.py b/backend/tests/test_schedule_application_service.py index 17f232d6..ed6767e4 100644 --- a/backend/tests/test_schedule_application_service.py +++ b/backend/tests/test_schedule_application_service.py @@ -1073,6 +1073,47 @@ def test_find_schedules_filters_without_leaking_other_accounts_or_deleted_rows() assert with_deleted.schedules[0].status is ScheduleStatus.DELETED +def test_find_schedules_excludes_a_confirmed_location_reminder() -> None: + # 地点型提醒一旦被用户确认,就跟移动端地图/日历的展示口径对齐——语音搜索 + # 也不应该再把它当"还需要处理"的日程报出来,否则助手会反复提起已经 + # 处理完的地点提醒。时间型日程没有这条约束,确认后依然可搜。 + service, store = _service() + location = service.create_schedule( + account_id="account-a", + command=CreateScheduleCommand( + schedule_type=ScheduleType.LOCATION, + schedule_kind=ScheduleKind.ONCE, + title="去机场", + timezone="Asia/Shanghai", + location_name="虹桥机场", + latitude=31.2304, + longitude=121.4737, + reminder_type=ReminderType.ARRIVE_LOCATION, + reminder_strength=ReminderStrength.MEDIUM, + ), + ).schedules[0] + confirmed_location = replace( + location, + reminder_disposition_state=ReminderDispositionState.CONFIRMED, + ) + store.schedules[location.id] = confirmed_location + + time_schedule = service.create_schedule( + account_id="account-a", + command=_time_command(title="项目复盘"), + ).schedules[0] + confirmed_time = replace( + time_schedule, + reminder_disposition_state=ReminderDispositionState.CONFIRMED, + ) + store.schedules[time_schedule.id] = confirmed_time + + matches = service.find_schedules(account_id="account-a", query=FindSchedulesQuery()) + + assert confirmed_location not in matches.schedules + assert confirmed_time in matches.schedules + + def test_find_schedules_filters_category_and_combines_it_with_a_time_window() -> None: service, store = _service() template = service.create_schedule(