From 5eeedf10134e567b270e2b29bc64fb64414c78fc Mon Sep 17 00:00:00 2001 From: Rodger-Wang <1367893453@qq.com> Date: Sat, 5 Sep 2026 08:26:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=8B=BE=E5=BF=86=E9=A1=B5=E7=9C=8B?= =?UTF-8?q?=E4=B8=8D=E5=88=B0=E5=8A=A9=E6=89=8B=E9=97=B9=E9=92=9F=EF=BC=9A?= =?UTF-8?q?=E6=8D=A2=E6=BA=90=E6=97=B6=E6=8A=8A=E6=9C=AC=E5=9C=B0=E9=82=A3?= =?UTF-8?q?=E4=BB=BD=E7=9A=84=E5=B1=95=E7=A4=BA=E5=87=BA=E5=8F=A3=E5=BC=84?= =?UTF-8?q?=E4=B8=A2=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 真机实测暴露的回归。设备上取到的证据: assistant_reminders: 2 条("南山会议" 16:00、"晚餐时间" 17:00),synced=false assistant_directives: 2 条 SET_clock,status=done memory_items_cache: 0 条 也就是说助手完全正常:听懂了、下发了指令、指令也落地了。但用户在拾忆页 什么都看不到。 ## 原因 _rebuildEvents 换成只读 MemoryService.items 之后,AssistantDirectiveService 的 reminders **全仓再没有任何消费者**(grep 可证)。后端还没部署 memory 模块, memory_add 必然失败,于是本地那两条既进不了 MemoryService、也没有展示出口。 我上一轮的提交说明写着「迁移期双写……即使上传失败,用户至少还能在本机看到 自己刚定的闹钟」——**那句话是错的**,双写的「本地」那一半当时已经没有出口了。 ## 改动 - CalendarController._rebuildEvents 补上 _localReminderEvents():把 **还没同步**的助手闹钟也投影进日历。已同步的跳过,否则 MemoryService 与 本地两边都投影会出现两条。 - 重新订阅 AssistantDirectiveService.reminders(_reminderWorker): 不订阅的话助手刚定完闹钟页面不会刷新,要退出重进才看得到。 - 「提醒 → 记忆项」的换算收敛成 AssistantReminderToMemory 扩展, 上传路径与日历投影共用同一份,删掉 service 里那份重复实现。 - 本地未同步项的 memoryId 是 0:点开编辑或勾完成时给明确提示 (memLocalOnlyHint,41 语言已补),不再静默无反应——它没有服务端 id, 改了也没处提交。 ## 另一个症状:EMAI 回复没声音(未修,不在本次改动范围) 同一份日志里: [ERROR][PcmStreamPlayer] 播放错误: PlatformException(-11850, Operation Stopped) [ERROR][PcmStreamPlayer] 启动播放失败: (-11850) Operation Stopped 反复几十次。EMAI 的回复音频走 BailianMultimodalService.audioOut → PcmStreamPlayer.feed,**整条链路不经过 TtsService**,也不在本次改动的文件里 (改动清单见 git diff --name-only)。-11850 是 AVFoundation 层的错误, 指向音频会话状态,需要单独排查。 Co-Authored-By: Claude Opus 5 (1M context) --- .../lib/core/translations/language/ar_sa.dart | 1 + .../lib/core/translations/language/bg_bg.dart | 1 + .../lib/core/translations/language/cs_cz.dart | 1 + .../lib/core/translations/language/da_dk.dart | 1 + .../lib/core/translations/language/de_de.dart | 1 + .../lib/core/translations/language/el_gr.dart | 1 + .../lib/core/translations/language/en_us.dart | 1 + .../lib/core/translations/language/es_es.dart | 1 + .../lib/core/translations/language/et_ee.dart | 1 + .../lib/core/translations/language/fa_ir.dart | 1 + .../lib/core/translations/language/fi_fi.dart | 1 + .../lib/core/translations/language/fr_fr.dart | 1 + .../lib/core/translations/language/hi_in.dart | 1 + .../lib/core/translations/language/hr_hr.dart | 1 + .../lib/core/translations/language/hu_hu.dart | 1 + .../lib/core/translations/language/id_id.dart | 1 + .../lib/core/translations/language/is_is.dart | 1 + .../lib/core/translations/language/it_it.dart | 1 + .../lib/core/translations/language/ja_jp.dart | 1 + .../lib/core/translations/language/ko_kr.dart | 1 + .../lib/core/translations/language/lt_lt.dart | 1 + .../lib/core/translations/language/lv_lv.dart | 1 + .../lib/core/translations/language/ms_my.dart | 1 + .../lib/core/translations/language/nb_no.dart | 1 + .../lib/core/translations/language/nl_nl.dart | 1 + .../lib/core/translations/language/pl_pl.dart | 1 + .../lib/core/translations/language/pt_pt.dart | 1 + .../lib/core/translations/language/ro_ro.dart | 1 + .../lib/core/translations/language/ru_ru.dart | 1 + .../lib/core/translations/language/sk_sk.dart | 1 + .../lib/core/translations/language/sl_si.dart | 1 + .../lib/core/translations/language/sr_rs.dart | 1 + .../lib/core/translations/language/sv_se.dart | 1 + .../lib/core/translations/language/th_th.dart | 1 + .../lib/core/translations/language/tl_ph.dart | 1 + .../lib/core/translations/language/tr_tr.dart | 1 + .../lib/core/translations/language/uk_ua.dart | 1 + .../lib/core/translations/language/vi_vn.dart | 1 + .../lib/core/translations/language/zh_cn.dart | 1 + .../lib/core/translations/language/zh_hk.dart | 1 + .../lib/core/translations/language/zh_tw.dart | 1 + .../lib/data/models/assistant_directive.dart | 52 +++++++++++++++++++ .../services/assistant_directive_service.dart | 41 +-------------- .../home/controllers/calendar_controller.dart | 37 ++++++++++++- .../views/widgets/todo_scope_list.dart | 18 ++++++- 45 files changed, 147 insertions(+), 42 deletions(-) diff --git a/apps/client/lib/core/translations/language/ar_sa.dart b/apps/client/lib/core/translations/language/ar_sa.dart index 658e4b1b..c007d1d9 100644 --- a/apps/client/lib/core/translations/language/ar_sa.dart +++ b/apps/client/lib/core/translations/language/ar_sa.dart @@ -1,5 +1,6 @@ //阿拉伯-沙特阿拉伯语 const Map arSA = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/bg_bg.dart b/apps/client/lib/core/translations/language/bg_bg.dart index b2b82cd9..d9bdc523 100644 --- a/apps/client/lib/core/translations/language/bg_bg.dart +++ b/apps/client/lib/core/translations/language/bg_bg.dart @@ -1,5 +1,6 @@ //保加利亚-保加利亚语 const Map bgBG = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/cs_cz.dart b/apps/client/lib/core/translations/language/cs_cz.dart index 1e05335e..ef04f08d 100644 --- a/apps/client/lib/core/translations/language/cs_cz.dart +++ b/apps/client/lib/core/translations/language/cs_cz.dart @@ -1,5 +1,6 @@ //捷克-捷克语 const Map csCZ = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/da_dk.dart b/apps/client/lib/core/translations/language/da_dk.dart index c1d79c16..f9c93e49 100644 --- a/apps/client/lib/core/translations/language/da_dk.dart +++ b/apps/client/lib/core/translations/language/da_dk.dart @@ -1,5 +1,6 @@ //丹麦-丹麦语 const Map daDK = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/de_de.dart b/apps/client/lib/core/translations/language/de_de.dart index cac570d6..0a6fdf93 100644 --- a/apps/client/lib/core/translations/language/de_de.dart +++ b/apps/client/lib/core/translations/language/de_de.dart @@ -1,5 +1,6 @@ //德语-德语 const Map deDE = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/el_gr.dart b/apps/client/lib/core/translations/language/el_gr.dart index 1c8b5d5f..9d7337d7 100644 --- a/apps/client/lib/core/translations/language/el_gr.dart +++ b/apps/client/lib/core/translations/language/el_gr.dart @@ -1,5 +1,6 @@ //希腊-希腊语 const Map elGR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/en_us.dart b/apps/client/lib/core/translations/language/en_us.dart index 00926cda..46faeff7 100644 --- a/apps/client/lib/core/translations/language/en_us.dart +++ b/apps/client/lib/core/translations/language/en_us.dart @@ -1,5 +1,6 @@ //美国-英语 const Map enUS = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/es_es.dart b/apps/client/lib/core/translations/language/es_es.dart index 18238a0b..aeb4ea61 100644 --- a/apps/client/lib/core/translations/language/es_es.dart +++ b/apps/client/lib/core/translations/language/es_es.dart @@ -1,5 +1,6 @@ //西班牙-西班牙语 const Map esES = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/et_ee.dart b/apps/client/lib/core/translations/language/et_ee.dart index 8ae656f0..da1efd18 100644 --- a/apps/client/lib/core/translations/language/et_ee.dart +++ b/apps/client/lib/core/translations/language/et_ee.dart @@ -1,5 +1,6 @@ //爱沙尼亚-爱沙尼亚语 const Map etEE = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/fa_ir.dart b/apps/client/lib/core/translations/language/fa_ir.dart index f4feb28e..e24b17b6 100644 --- a/apps/client/lib/core/translations/language/fa_ir.dart +++ b/apps/client/lib/core/translations/language/fa_ir.dart @@ -1,5 +1,6 @@ //伊朗-波斯语 const Map faIR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/fi_fi.dart b/apps/client/lib/core/translations/language/fi_fi.dart index 1b487996..0d33b0ec 100644 --- a/apps/client/lib/core/translations/language/fi_fi.dart +++ b/apps/client/lib/core/translations/language/fi_fi.dart @@ -1,5 +1,6 @@ //芬兰-芬兰语 const Map fiFI = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/fr_fr.dart b/apps/client/lib/core/translations/language/fr_fr.dart index be40cfea..c591e412 100644 --- a/apps/client/lib/core/translations/language/fr_fr.dart +++ b/apps/client/lib/core/translations/language/fr_fr.dart @@ -1,5 +1,6 @@ // 法国-法语 const Map frFR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/hi_in.dart b/apps/client/lib/core/translations/language/hi_in.dart index dde8daea..6f12935a 100644 --- a/apps/client/lib/core/translations/language/hi_in.dart +++ b/apps/client/lib/core/translations/language/hi_in.dart @@ -1,5 +1,6 @@ // 印地-印地语 const Map hiIN = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/hr_hr.dart b/apps/client/lib/core/translations/language/hr_hr.dart index 9c07e28d..1e798ee1 100644 --- a/apps/client/lib/core/translations/language/hr_hr.dart +++ b/apps/client/lib/core/translations/language/hr_hr.dart @@ -1,5 +1,6 @@ // 克罗地亚-克罗地亚语 const Map hrHR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/hu_hu.dart b/apps/client/lib/core/translations/language/hu_hu.dart index a33deaad..6443dc7a 100644 --- a/apps/client/lib/core/translations/language/hu_hu.dart +++ b/apps/client/lib/core/translations/language/hu_hu.dart @@ -1,5 +1,6 @@ //匈牙利-匈牙利语 const Map huHU = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/id_id.dart b/apps/client/lib/core/translations/language/id_id.dart index 1545682f..ec7a5b8b 100644 --- a/apps/client/lib/core/translations/language/id_id.dart +++ b/apps/client/lib/core/translations/language/id_id.dart @@ -1,5 +1,6 @@ //印尼-印尼语 const Map idID = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/is_is.dart b/apps/client/lib/core/translations/language/is_is.dart index 2a0d0767..d8a80cc4 100644 --- a/apps/client/lib/core/translations/language/is_is.dart +++ b/apps/client/lib/core/translations/language/is_is.dart @@ -1,5 +1,6 @@ // 冰岛-冰岛语 const Map isIS = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/it_it.dart b/apps/client/lib/core/translations/language/it_it.dart index 69d166cf..a5887969 100644 --- a/apps/client/lib/core/translations/language/it_it.dart +++ b/apps/client/lib/core/translations/language/it_it.dart @@ -1,5 +1,6 @@ // 意大利-意大利语 const Map itIT = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/ja_jp.dart b/apps/client/lib/core/translations/language/ja_jp.dart index 14754f5e..374ec0a1 100644 --- a/apps/client/lib/core/translations/language/ja_jp.dart +++ b/apps/client/lib/core/translations/language/ja_jp.dart @@ -1,5 +1,6 @@ // 日本-日本語 const Map jaJP = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/ko_kr.dart b/apps/client/lib/core/translations/language/ko_kr.dart index 80268ea1..a6f2de4d 100644 --- a/apps/client/lib/core/translations/language/ko_kr.dart +++ b/apps/client/lib/core/translations/language/ko_kr.dart @@ -1,5 +1,6 @@ // 韩国-韩语 const Map koKR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/lt_lt.dart b/apps/client/lib/core/translations/language/lt_lt.dart index 4d098168..e33aecd6 100644 --- a/apps/client/lib/core/translations/language/lt_lt.dart +++ b/apps/client/lib/core/translations/language/lt_lt.dart @@ -1,4 +1,5 @@ const Map ltLT = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/lv_lv.dart b/apps/client/lib/core/translations/language/lv_lv.dart index 6bf59e91..1eea049f 100644 --- a/apps/client/lib/core/translations/language/lv_lv.dart +++ b/apps/client/lib/core/translations/language/lv_lv.dart @@ -1,4 +1,5 @@ const Map lvLV = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/ms_my.dart b/apps/client/lib/core/translations/language/ms_my.dart index 95ed50c3..26bc7292 100644 --- a/apps/client/lib/core/translations/language/ms_my.dart +++ b/apps/client/lib/core/translations/language/ms_my.dart @@ -1,5 +1,6 @@ // 马来西亚-马来语 const Map msMY = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/nb_no.dart b/apps/client/lib/core/translations/language/nb_no.dart index 12f432f3..e7b922bf 100644 --- a/apps/client/lib/core/translations/language/nb_no.dart +++ b/apps/client/lib/core/translations/language/nb_no.dart @@ -1,5 +1,6 @@ // 挪威-挪威语 const Map nbNO = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/nl_nl.dart b/apps/client/lib/core/translations/language/nl_nl.dart index 78681dd3..7acba09f 100644 --- a/apps/client/lib/core/translations/language/nl_nl.dart +++ b/apps/client/lib/core/translations/language/nl_nl.dart @@ -1,5 +1,6 @@ // 荷兰-荷兰语 const Map nlNL = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/pl_pl.dart b/apps/client/lib/core/translations/language/pl_pl.dart index 8b804573..84ff763d 100644 --- a/apps/client/lib/core/translations/language/pl_pl.dart +++ b/apps/client/lib/core/translations/language/pl_pl.dart @@ -1,5 +1,6 @@ // 波兰-波兰语 const Map plPL = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/pt_pt.dart b/apps/client/lib/core/translations/language/pt_pt.dart index 7d5e67a5..6505dd3c 100644 --- a/apps/client/lib/core/translations/language/pt_pt.dart +++ b/apps/client/lib/core/translations/language/pt_pt.dart @@ -1,5 +1,6 @@ // 葡萄牙-葡萄牙语 const Map ptPT = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/ro_ro.dart b/apps/client/lib/core/translations/language/ro_ro.dart index 615527bc..2c2fbe77 100644 --- a/apps/client/lib/core/translations/language/ro_ro.dart +++ b/apps/client/lib/core/translations/language/ro_ro.dart @@ -1,5 +1,6 @@ // 罗马尼亚-罗马尼亚语 const Map roRO = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/ru_ru.dart b/apps/client/lib/core/translations/language/ru_ru.dart index f6d3100e..c9361d1d 100644 --- a/apps/client/lib/core/translations/language/ru_ru.dart +++ b/apps/client/lib/core/translations/language/ru_ru.dart @@ -1,5 +1,6 @@ // 俄罗斯-俄语 const Map ruRU = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/sk_sk.dart b/apps/client/lib/core/translations/language/sk_sk.dart index 037ec1b8..ab8ba4f7 100644 --- a/apps/client/lib/core/translations/language/sk_sk.dart +++ b/apps/client/lib/core/translations/language/sk_sk.dart @@ -1,5 +1,6 @@ // 斯洛伐克-斯洛伐克语 const Map skSK = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/sl_si.dart b/apps/client/lib/core/translations/language/sl_si.dart index 7b25fc1b..a9285f5a 100644 --- a/apps/client/lib/core/translations/language/sl_si.dart +++ b/apps/client/lib/core/translations/language/sl_si.dart @@ -1,5 +1,6 @@ // 斯洛文尼亚-斯洛文尼亚语 const Map slSI = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/sr_rs.dart b/apps/client/lib/core/translations/language/sr_rs.dart index 7868b136..349ebb17 100644 --- a/apps/client/lib/core/translations/language/sr_rs.dart +++ b/apps/client/lib/core/translations/language/sr_rs.dart @@ -1,5 +1,6 @@ // 塞尔维亚-塞尔维亚语 const Map srRS = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/sv_se.dart b/apps/client/lib/core/translations/language/sv_se.dart index 7992fcdf..fd2b52cd 100644 --- a/apps/client/lib/core/translations/language/sv_se.dart +++ b/apps/client/lib/core/translations/language/sv_se.dart @@ -1,5 +1,6 @@ //瑞典-瑞典语 const Map svSE = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/th_th.dart b/apps/client/lib/core/translations/language/th_th.dart index 36b1bbc9..0a7bc5c3 100644 --- a/apps/client/lib/core/translations/language/th_th.dart +++ b/apps/client/lib/core/translations/language/th_th.dart @@ -1,5 +1,6 @@ //泰国-泰语 const Map thTH = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/tl_ph.dart b/apps/client/lib/core/translations/language/tl_ph.dart index 4128b842..7c2b2bcc 100644 --- a/apps/client/lib/core/translations/language/tl_ph.dart +++ b/apps/client/lib/core/translations/language/tl_ph.dart @@ -1,5 +1,6 @@ //菲律宾-塔加路语 const Map tlPH = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/tr_tr.dart b/apps/client/lib/core/translations/language/tr_tr.dart index cedc6c1c..03219662 100644 --- a/apps/client/lib/core/translations/language/tr_tr.dart +++ b/apps/client/lib/core/translations/language/tr_tr.dart @@ -1,5 +1,6 @@ //土耳其-土耳其语 const Map trTR = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/uk_ua.dart b/apps/client/lib/core/translations/language/uk_ua.dart index fe01f752..e3d26777 100644 --- a/apps/client/lib/core/translations/language/uk_ua.dart +++ b/apps/client/lib/core/translations/language/uk_ua.dart @@ -1,5 +1,6 @@ // 乌克兰-乌克兰语 const Map ukUA = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/vi_vn.dart b/apps/client/lib/core/translations/language/vi_vn.dart index 669dc159..5769b131 100644 --- a/apps/client/lib/core/translations/language/vi_vn.dart +++ b/apps/client/lib/core/translations/language/vi_vn.dart @@ -1,5 +1,6 @@ //越南-越南语 const Map viVN = { + 'memLocalOnlyHint': 'Not synced to the server yet, cannot edit', 'memDailyCount': 'You have @n item(s) today.', 'memCatAll': 'All', 'memCatTodo': 'To-do', diff --git a/apps/client/lib/core/translations/language/zh_cn.dart b/apps/client/lib/core/translations/language/zh_cn.dart index 189066c0..2fc3c45c 100644 --- a/apps/client/lib/core/translations/language/zh_cn.dart +++ b/apps/client/lib/core/translations/language/zh_cn.dart @@ -1,5 +1,6 @@ //中国-简体中文 const Map zhCN = { + 'memLocalOnlyHint': '这条还没同步到服务端,暂时不能编辑', 'memDailyCount': '今天有 @n 件事。', 'memCatAll': '全部', 'memCatTodo': '待办', diff --git a/apps/client/lib/core/translations/language/zh_hk.dart b/apps/client/lib/core/translations/language/zh_hk.dart index 65713b03..fa94e118 100644 --- a/apps/client/lib/core/translations/language/zh_hk.dart +++ b/apps/client/lib/core/translations/language/zh_hk.dart @@ -1,5 +1,6 @@ //香港-繁体中文 const Map zhHK = { + 'memLocalOnlyHint': '這條還沒同步到伺服器,暫時不能編輯', 'memDailyCount': '今天有 @n 件事。', 'memCatAll': '全部', 'memCatTodo': '待辦', diff --git a/apps/client/lib/core/translations/language/zh_tw.dart b/apps/client/lib/core/translations/language/zh_tw.dart index 803561d4..eb8ad42e 100644 --- a/apps/client/lib/core/translations/language/zh_tw.dart +++ b/apps/client/lib/core/translations/language/zh_tw.dart @@ -1,5 +1,6 @@ //台湾-繁体中文 const Map zhTW = { + 'memLocalOnlyHint': '這條還沒同步到伺服器,暫時不能編輯', 'memDailyCount': '今天有 @n 件事。', 'memCatAll': '全部', 'memCatTodo': '待辦', diff --git a/apps/client/lib/data/models/assistant_directive.dart b/apps/client/lib/data/models/assistant_directive.dart index eeb94d0d..7bcb2358 100644 --- a/apps/client/lib/data/models/assistant_directive.dart +++ b/apps/client/lib/data/models/assistant_directive.dart @@ -1,5 +1,9 @@ import 'dart:convert'; +import 'package:get/get.dart'; + +import 'memory_item.dart'; + /// 助手指令:百炼多模态在 `RespondingContent.extra_info.tool_calls` 里下发的 /// 端侧函数调用。 /// @@ -389,3 +393,51 @@ class AssistantReminder { synced: j['synced'] == true, ); } + + +/// 提醒 → 记忆项的换算。 +/// +/// 收敛在这里而不是各写一份:上传服务端(AssistantDirectiveService._uploadReminder) +/// 与拾忆页的本地投影(CalendarController)都要用,两处各写一遍必然漂移。 +extension AssistantReminderToMemory on AssistantReminder { + MemoryItem toMemoryItem() { + final now = DateTime.now(); + final today = '${now.year}-' + '${now.month.toString().padLeft(2, '0')}-' + '${now.day.toString().padLeft(2, '0')}'; + return MemoryItem( + category: MemoryCategory.alarm, + source: MemorySource.assistant, + sourceId: directiveId, + clientKey: directiveId.isNotEmpty ? directiveId : id, + title: content.isNotEmpty ? content : 'memCatAlarm'.tr, + happenDate: date ?? today, + happenTime: time, + repeatRule: _repeatRuleToRaw(repeat), + repeatRaw: repeatRaw, + weekday: weekday ?? 0, + remindAhead: 5, + state: done ? MemoryState.done : MemoryState.pending, + ); + } +} + +/// 本地 RepeatRule 枚举 → 服务端的字符串口径。 +/// +/// unknown 按单次处理:认不出的重复规则宁可只响一次, +/// 也不要按猜的规则天天响。 +String _repeatRuleToRaw(RepeatRule r) { + switch (r) { + case RepeatRule.daily: + return MemoryRepeat.daily; + case RepeatRule.weekly: + return MemoryRepeat.weekly; + case RepeatRule.weekdays: + return MemoryRepeat.weekdays; + case RepeatRule.weekend: + return MemoryRepeat.weekend; + case RepeatRule.once: + case RepeatRule.unknown: + return MemoryRepeat.once; + } +} diff --git a/apps/client/lib/data/services/assistant_directive_service.dart b/apps/client/lib/data/services/assistant_directive_service.dart index 6ce18b6b..484b4e3f 100644 --- a/apps/client/lib/data/services/assistant_directive_service.dart +++ b/apps/client/lib/data/services/assistant_directive_service.dart @@ -5,7 +5,6 @@ import 'package:get_storage/get_storage.dart'; import '../../core/utils/logger.dart'; import '../models/assistant_directive.dart'; -import '../models/memory_item.dart'; import '../models/user_Info.dart'; import 'memory_service.dart'; @@ -139,19 +138,8 @@ class AssistantDirectiveService extends GetxService { if (!Get.isRegistered()) return; if (!User.isLoggedIn()) return; try { - final item = MemoryItem( - category: MemoryCategory.alarm, - source: MemorySource.assistant, - sourceId: r.directiveId, - clientKey: r.directiveId.isNotEmpty ? r.directiveId : r.id, - title: r.content.isNotEmpty ? r.content : 'memCatAlarm'.tr, - happenDate: r.date ?? _todayString(), - happenTime: r.time, - repeatRule: _repeatToRaw(r.repeat), - repeatRaw: r.repeatRaw, - weekday: r.weekday ?? 0, - remindAhead: 5, - ); + // 换算收敛在 AssistantReminderToMemory 扩展里,与拾忆页的本地投影共用同一份 + final item = r.toMemoryItem(); final saved = await MemoryService.to.add(item); if (saved != null) { r.synced = true; @@ -163,31 +151,6 @@ class AssistantDirectiveService extends GetxService { } } - static String _todayString() { - final d = DateTime.now(); - return '${d.year}-${d.month.toString().padLeft(2, '0')}-' - '${d.day.toString().padLeft(2, '0')}'; - } - - /// 本地 RepeatRule 枚举 → 服务端的字符串口径 - static String _repeatToRaw(RepeatRule r) { - switch (r) { - case RepeatRule.daily: - return MemoryRepeat.daily; - case RepeatRule.weekly: - return MemoryRepeat.weekly; - case RepeatRule.weekdays: - return MemoryRepeat.weekdays; - case RepeatRule.weekend: - return MemoryRepeat.weekend; - // unknown 按单次处理——与上面 d.note 的口径一致, - // 认不出的重复规则宁可只响一次,也不要按猜的规则天天响。 - case RepeatRule.once: - case RepeatRule.unknown: - return MemoryRepeat.once; - } - } - /// 用户在代办页勾选完成 void toggleReminderDone(String id, bool done) { final r = reminders.firstWhereOrNull((e) => e.id == id); diff --git a/apps/client/lib/modules/home/controllers/calendar_controller.dart b/apps/client/lib/modules/home/controllers/calendar_controller.dart index 86ce2423..c363d768 100644 --- a/apps/client/lib/modules/home/controllers/calendar_controller.dart +++ b/apps/client/lib/modules/home/controllers/calendar_controller.dart @@ -4,7 +4,9 @@ import 'package:flutter/material.dart'; import 'package:lunar/lunar.dart'; import '../../../core/utils/logger.dart'; +import '../../../data/models/assistant_directive.dart'; import '../../../data/models/memory_item.dart'; +import '../../../data/services/assistant_directive_service.dart'; import '../../../data/services/memory_service.dart'; import '../views/widgets/calendar/calendar_event.dart'; @@ -51,6 +53,7 @@ class CalendarController extends GetxController /// 监听助手提醒变化的 worker,onClose 要停掉 Worker? _memoryWorker; + Worker? _reminderWorker; // 折叠态横向条的代办/花销筛选:null=全部,非空=只看该类别。 // 点击"代办"/"花销"标签设置;点击日期(onDaySelected)重置为全部。 @@ -120,6 +123,14 @@ class CalendarController extends GetxController (_) => _rebuildEvents(), ); } + // 迁移期还要盯着本地那份:服务端没收下时(后端未部署 / 断网), + // 助手刚定的闹钟只存在于本地 reminders 里,不订阅它页面就不会刷新。 + if (Get.isRegistered()) { + _reminderWorker = ever>( + AssistantDirectiveService.to.reminders, + (_) => _rebuildEvents(), + ); + } } /// 从 [MemoryService] 单一来源投影成日历事件。 @@ -136,20 +147,41 @@ class CalendarController extends GetxController for (final item in MemoryService.to.items) { list.addAll(_expandItem(item)); } + list.addAll(_localReminderEvents()); events.assignAll(list); } + /// 迁移期的本地兜底:把**还没同步到服务端**的助手闹钟也投影出来。 + /// + /// ⚠️ 不做这个,「双写」就是假的。SET_clock 会把闹钟写进本地 reminders, + /// 但换源之后页面只读 MemoryService —— 后端没部署或断网时 memory_add 失败, + /// 本地那份没有任何展示出口,用户对助手说完「定个会议提醒」, + /// 助手答应了、指令也落地了,拾忆页却空空如也(真机实测踩到)。 + /// + /// 已同步的跳过:那时 MemoryService 里的才是权威,两边都投影会出现两条。 + List _localReminderEvents() { + if (!Get.isRegistered()) return const []; + final out = []; + for (final r in AssistantDirectiveService.to.reminders) { + if (r.synced) continue; + // id 前缀区分来源:本地项的 memoryId 是 0,点开编辑时要能识别出 + // 「这条还没上服务端」而不是去 MemoryService 里查一个不存在的 id。 + out.addAll(_expandItem(r.toMemoryItem(), idPrefix: 'local:${r.id}:')); + } + return out; + } + /// 把一条记忆项摊成日历事件。 /// /// 窗口取「今天前 7 天 ~ 后 60 天」:往前留几天是为了让用户还能看到刚过去的 /// 事项,往后 60 天覆盖月视图翻两页。 - List _expandItem(MemoryItem item) { + List _expandItem(MemoryItem item, {String idPrefix = ''}) { final anchor = item.happenDateTime; final cat = CalendarEventCategoryX.fromRaw(item.category); final color = MemoryCategory.colorOf(item.category); CalendarEvent make(DateTime day, String suffix) => CalendarEvent( - id: '${item.id}$suffix', + id: '$idPrefix${item.id}$suffix', memoryId: item.id, title: item.title, // 负责人与截止原文合成一行副标题;两者都空时留空而不是塞占位符 @@ -213,6 +245,7 @@ class CalendarController extends GetxController @override void onClose() { _memoryWorker?.dispose(); + _reminderWorker?.dispose(); _snapAnimation.dispose(); monthPageController.dispose(); weekPageController.dispose(); diff --git a/apps/client/lib/modules/main_tab/views/widgets/todo_scope_list.dart b/apps/client/lib/modules/main_tab/views/widgets/todo_scope_list.dart index e957f276..4a2030e5 100644 --- a/apps/client/lib/modules/main_tab/views/widgets/todo_scope_list.dart +++ b/apps/client/lib/modules/main_tab/views/widgets/todo_scope_list.dart @@ -325,12 +325,28 @@ class TodoScopeList extends GetView { /// 先按 memoryId 回查真实的 MemoryItem——日历事件是投影出来的(重复项会摊成 /// 多天,id 带了日期后缀),不能拿它直接当记忆项用。 Future _openEdit(CalendarEvent event) async { + // memoryId == 0 表示这条还没同步到服务端(迁移期的本地助手闹钟)。 + // 它没有服务端 id,改了也没处提交,先明确告诉用户而不是静默无反应。 + if (event.memoryId == 0) { + Get.snackbar('memCatAlarm'.tr, 'memLocalOnlyHint'.tr, + snackPosition: SnackPosition.TOP); + return; + } final item = MemoryService.to.items .firstWhereOrNull((e) => e.id == event.memoryId); if (item == null) return; await MemoryEditSheet.show(item: item); } + Future _toggleDoneGuarded(CalendarEvent event) async { + if (event.memoryId == 0) { + Get.snackbar('memCatAlarm'.tr, 'memLocalOnlyHint'.tr, + snackPosition: SnackPosition.TOP); + return; + } + await _toggleDone(event); + } + Future _toggleDone(CalendarEvent event) async { final item = MemoryService.to.items .firstWhereOrNull((e) => e.id == event.memoryId); @@ -371,7 +387,7 @@ class TodoScopeList extends GetView { GestureDetector( // 勾选框自己吃掉点击,不要冒泡到卡片的「打开编辑」 behavior: HitTestBehavior.opaque, - onTap: () => _toggleDone(event), + onTap: () => _toggleDoneGuarded(event), child: Padding( padding: EdgeInsets.only(right: AppKidsDimens.space8, top: 2.h), child: Icon(