0.0.8 提醒修复(补):已镜像的本由系统日历全权负责,且判据改为"镜像真实存在"

用户要求:如果用户已把自己的日历同步到系统日历里,那么本应用就**完全不再提醒**了
—— 因为那必然是重复提醒。

采纳。同时修正上一版实现的一个**会漏提醒的弱点**:

问题:上一版按"**用户勾选了要镜像的本**"来跳过 App 提醒。但"勾选"只是**意图**,
本可能还没真正写进系统日历(如刚勾选未点「立即执行镜像」、系统日历里还没有对应账户)
→ 按意图跳过会导致**两边都不提醒(漏提醒)**。

改法:判据改为「**镜像账户真实存在**」,新增 `SystemCalendarMirror.mirroredCalKeys(context)`:
  1. 镜像开关未开 / 无系统日历权限 → 直接返回**空集**(不跳过任何本);
  2. 读系统日历全部账户,取出 name 以 `synccalendar.mirror.` 开头者的 **bookId**;
  3. 与 `listBooks()` 的 `bookId → calKey` 对照 → 只有**账户真实存在**的本才计入。
  安全阀:任何异常 → 空集 → 不跳过 → **宁可多提醒,绝不漏提醒**。

`ReminderService.mirroredKeys()` 改为委托给它(保留 30 秒缓存与 `invalidateMirrorCache()`);
`refreshReminders` / `tickReminders` 两处仍照旧跳过这些本(跳过即"App 完全不再提醒")。

交互完整性(不变):镜像开关开启/关闭、勾选本变化、权限被回收 四处
均 `invalidateMirrorCache()` + 立即 `refreshReminders()`,
确保**关闭镜像后这些本立刻恢复由 App 提醒**。

验证:编译通过(BUILD SUCCESSFUL);手机未连接 → 未装机。
This commit is contained in:
2026-10-14 14:28:42 +08:00
parent 19cfe3cf34
commit d890a2b053
2 changed files with 58 additions and 19 deletions
+7 -18
View File
@@ -12,7 +12,7 @@ import { BusinessError } from '@kit.BasicServicesKit';
import { EventDb, LocalEvent } from './EventDb';
import { AppSettings } from './AppSettings';
import { AccountStore } from './AccountStore';
import { SystemCalendarMirror, BookRef } from './SystemCalendarMirror';
import { SystemCalendarMirror } from './SystemCalendarMirror';
import { LogUtil } from './LogUtil';
export class ReminderService {
@@ -34,32 +34,21 @@ export class ReminderService {
/**
* ⭐⭐ 取"已镜像到系统日历"的日历本 calKey 集合(0.0.8 修复双通道重复提醒)。
*
* 背景:镜像到系统日历后,**系统日历自己会提醒**(条目的 reminderTime)。
* 镜像到系统日历后,**系统日历自己会提醒**(条目的 reminderTime);
* 若 App 再为同一个本排代理提醒 → 同一条日程提醒两次。
* 因此这里返回"该由系统日历负责提醒"的本,App 侧跳过它们。
* 因此这里返回"该由系统日历负责提醒"的本,App 侧**完全不再提醒**它们。
*
* ⚠️ 判据是"**镜像账户真实存在**"(`SystemCalendarMirror.mirroredCalKeys`),
* 而不是"用户勾选了要镜像"—— 后者只是意图,本可能还没被真正写进系统日历;
* 按意图跳过会导致"两边都不提醒"(漏提醒)。
* ⚠️ 返回空集 = 不跳过任何本(保守:宁可多提醒,不可漏提醒)。
* 仅在「镜像开关已开 + 有系统日历权限 + 有选中本」时才真正跳过。
*/
private static async mirroredKeys(context: common.Context): Promise<Set<string>> {
if (Date.now() - ReminderService.mirroredLoadedAt < 30000
&& ReminderService.mirroredCalKeys.size > 0) {
return ReminderService.mirroredCalKeys;
}
const out: Set<string> = new Set<string>();
try {
const on: boolean = await AppSettings.getMirrorEnabled(context);
if (on && await SystemCalendarMirror.hasPermission()) {
const refs: BookRef[] = await SystemCalendarMirror.selectedBookRefs(context);
for (const r of refs) {
if (r.calKey !== '') {
out.add(r.calKey);
}
}
}
} catch (err) {
// 判定失败 → 返回空集(不跳过,避免漏提醒)
}
const out: Set<string> = await SystemCalendarMirror.mirroredCalKeys(context);
ReminderService.mirroredCalKeys = out;
ReminderService.mirroredLoadedAt = Date.now();
return out;
@@ -239,6 +239,56 @@ export class SystemCalendarMirror {
return name.startsWith(SystemCalendarMirror.ACCOUNT_PREFIX);
}
/**
* ⭐⭐ 取"**确实已经镜像到系统日历**"的日历本 calKey 集合(按镜像账户的**真实存在性**判定)。
*
* 与"用户勾选了要镜像的本"不同 —— 后者只是**意图**,本可能还没被真正写进系统日历
* (如刚勾选但没点「立即执行镜像」、或根本没开镜像开关)。
* 若按"意图"跳过 App 提醒,而系统日历里其实还没有 → **两边都不提醒 = 漏提醒**。
* 故这里只认**镜像账户真实存在**这一条。
*
* ⚠️ 返回**空集**=不跳过任何本(读不到账户时保守处理,宁可多提醒)。
* ⚠️ 未开启镜像开关时直接返回空集(省一次系统查询)。
*/
static async mirroredCalKeys(context: common.Context): Promise<Set<string>> {
const out: Set<string> = new Set<string>();
try {
if (!await AppSettings.getMirrorEnabled(context)) {
return out; // 未开启镜像 → 没有任何本由系统日历负责
}
if (!await SystemCalendarMirror.hasPermission()) {
return out; // 无权限 → 系统日历里不可能有我们的条目
}
const cm: calendarManager.CalendarManager = calendarManager.getCalendarManager(context);
const all: calendarManager.Calendar[] = [];
const cals: calendarManager.Calendar[] = await cm.getAllCalendars();
for (const c of cals) {
all.push(c);
}
// 存在镜像账户 → 其 bookId 对应的本由系统日历负责
const presentBookIds: Set<string> = new Set<string>();
for (const c of all) {
const name: string = c.getAccount().name;
if (name.startsWith(SystemCalendarMirror.ACCOUNT_PREFIX)) {
presentBookIds.add(name.substring(SystemCalendarMirror.ACCOUNT_PREFIX.length));
}
}
if (presentBookIds.size === 0) {
return out;
}
// bookId → calKey(从当前账号配置解析)
const refs: BookRef[] = await SystemCalendarMirror.listBooks(context);
for (const r of refs) {
if (presentBookIds.has(r.bookId) && r.calKey !== '') {
out.add(r.calKey);
}
}
} catch (err) {
LogUtil.write(`判定"已镜像本"失败(按未镜像处理): ${(err as BusinessError).message}`);
}
return out;
}
/* ==================== 账户身份判定(0.0.8) ==================== */
/**