Tree:
7ce56f693d
along
along-test
main
memory-center-impl-v1
memory-design-v0.2
${ noResults }
3 Commits (7ce56f693dd3f7c78ac5b64e1f15158a704afaa4)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
f9b9c8c72d |
client: 翻译默认归档进语音纪要;列表改本地优先;登录按包收敛;EMAI 走 A2DP
## 通话/同声/面对面翻译 → 语音纪要 三种翻译模式默认边翻边录,结束时把音频**连同已有转写**一起归档 (rtype=VOICETRANSLAT),服务端跳过识别、直接用智能模板(General-Autopilot)总结, 在会议纪要里就能看到。转写段的构造见 translation_export.dart(纯函数,带金测试): - 时间是**相对录音起点的毫秒**并扣掉暂停区间——面对面是逐句按住说话、松开就暂停的, 不扣的话越往后偏得越多;写成绝对时间戳会让点一句跳到音频末尾之外; - 说话人用 Speaker_0/Speaker_1,与服务端转写同一口径(转写页直接把这个字段当文案显示, 裸 0/1 会和别的纪要长得不一样,喂给 LLM 抽待办时也会混出 Speaker2 这种负责人); - AST socket 断开后 _terminateCallOnError 会取消事件订阅,重开时收不到任何转写 (真机上表现为「有日志但转写 0 段」),改成重新订阅。 ## 语音纪要列表:服务端是真源、本地 sqlite 是缓存 列表接口改回骨架字段后(服务端同批),客户端改成本地优先渲染 + 后台对齐: - Synchrodata 重写:拉骨架 → upsert → 删掉「本地有服务端没有」的记录(连本地音频、 上传队列、轮询任务一起清)。**业务错误时 data 是 null 不是空列表**,判空错了 会把本地整个清空; - 详情进页面时才拉整条;离线操作队列那套上行是死代码(服务端接口整个注释掉了),删除。 ## 登录按包收敛 苹果包只留苹果+游客、安卓包只留微信+游客、谷歌包只留邮箱+游客。 判定抽成 resolveLoginMethods 纯函数(login_methods.dart + 8 条测试), 邮箱包还要锁死国家区号选择。 ## EMAI 与其它 - 手机侧触发的对话走 A2DP 而不是语音处理通道:iOS 上 A2DP 输出与 voiceChat 模式 互斥,开了后者蓝牙耳机会当场切到手机扬声器。按当前实际输出路由决定要不要开 voiceProcessing/回声消除; - EMAI「开始对话」闪退:探测输入格式时 new 了一个一次性 AVAudioEngine 去读 inputNode, 配置变更时崩在 AVAudioIONodeImpl(EXC_BAD_ACCESS)。改成直接问 AVAudioSession; - 语音纪要文件名可改:每行右侧加铅笔图标,复用已有的重命名弹窗(原来只有长按菜单, 实际入口已不可达); - 拾忆上报 IANA 时区名:DateTime.now().timeZoneName 给的是「CST」这种缩写, 服务端 LoadLocation 解析不了、静默退回容器时区,海外用户的提醒时刻会全按北京时间算。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
3 weeks ago |
|
|
46c815eb87 |
修拾忆页第一次打开就报错:解包口径写错 + 红条抑制漏了 memory_list
两个都是我上一轮引入的,表现叠在一起就是「第一次打开拾忆界面抛异常」。
## 1) MemoryService 的解包口径写错(严重,后端一部署就全线失效)
DioManager.request 结尾是 `return response.data;` —— 返回的是 **data 本身**,
不是 Response。全仓既有调用方都直接下标取字段(my_devices 的 response['devices']、
bes_device_auth 的 resp['device'])。
而 MemoryService._data 写的是 `resp.data`。因为 request 的返回类型是 dynamic,
**编译与 dart analyze 都不报错**;后端还没部署时返回 null,正好走空分支,
所以本地一路绿灯。一旦服务端真的返回成功,每一次 memory 调用都会抛
NoSuchMethodError —— 也就是说这个功能在部署之后才会开始全面失效。
已改成 `if (resp is Map) return Map<String, dynamic>.from(resp);`,
并加 test/memory_service_unwrap_test.dart 把四种形状(成功有数据 / 成功空 {} /
业务错误 null / 传进来个对象)都钉住,任何人改回 .data 都会红。
## 2) 红条抑制名单漏了 memory_list
上一轮只加了 memory_today / memory_upcoming,**漏了 memory_list** —— 而它正是
打开拾忆页第一个调的接口(TodoTab.initState → refreshFromServer)。
后端没部署时网关返回 NoFindServiceHandleFunc,AuthInterceptor 就把
"[Handle Http] no found handle memory_list" 原样弹给用户。
改成按 `/api/home/memory_` 前缀整条抑制,读写两种情况都成立:
读在打开页面与切前台时自动调,页面自己有行内空态与「显示的是本地缓存」提示;
写的调用点各自弹了本地化的 memSaveFailed / memDeleteFailed / memCompleteFailed,
不挡的话会弹两条。逐个列举正是漏掉 memory_list 的原因,改前缀。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
|
|
66463e53f6 |
记忆中心客户端:拾忆页接服务端 + 写操作 UI + 本地提醒 + 每日播报
dart analyze 零错误,flutter build apk --debug 通过。 零期修缮 - tabTodo 补齐 41 个语言。此前只有 11 个文件有这个 key,其中 8 个的值是 'Remin'(不是任何语言里的词,像 Reminisce 打了一半),缺 key 的 30 个经 GetX 回退到 en_US 同样显示 'Remin' —— 41 个语言里 38 个是坏的,只是不报错。 - i18n_audit.py 补「缺失 key」检查。它此前**结构上无法发现缺失的 key**:循环遍历 的是该文件已有的 key,且对 baseline 里没有的直接 continue。这正是 tabTodo 在 30 个语言里悄悄丢掉而没人发现的原因。现以 zh_cn 为基准,实测报出 7243 处历史缺失 (40 个语言只有 1 个不缺)。加了 --fail-on-missing/--allow-missing 给 CI 卡「只增 不减」,默认不开——一上来就红会让人直接把检查关掉。 - pubspec 引入 timezone + flutter_timezone。zonedSchedule 只接受 tz.TZDateTime, 没有这两个依赖,提醒功能一行都写不了。 拾忆页从纯展示改成可写(此前最大的工作量遗漏) 页面原来没有 RefreshIndicator、没有 FloatingActionButton、卡片没有 onTap, 于是服务端的 add/update/del/complete 四个写接口在客户端一个调用点都没有, user_edited 那套「保护用户修改」的机制完全空转。本次补齐: - MemoryEditSheet:新增/编辑弹层,按分类分流表单(统一表存储 ≠ 统一表单) - 卡片 onTap 打开编辑、左侧勾选框标记完成(灵感不给勾选框,它没有完成一说) - 右下角 + 新增、RefreshIndicator 下拉刷新、带引导的空态页 - date_certain=false 的项单独归到「待定日期」小节。一次会抽出 8 条没写截止时间的 待办全堆在会议当天,不分开那一格就没法看了。 数据层 - MemoryItem / MemoryRemindSlot / MemoryReport 手写镜像服务端 pb。 **每个字段都经宽松解析器**(memInt/memBool/memStr),不直接 as int: 服务端删字段或老记录缺列时,non-nullable 会让整个 fromJson 抛 TypeError, 表现是整页空白而不是少一个字段(appconfig_model 已经为此踩过坑)。 单条解析失败只跳过那一条。 - 分类保留原始字符串而不是 enum 解析。服务端加分类只改常量不发包, 客户端 switch 不到就抛等于服务端一加分类就打崩所有存量 App。 CalendarEventCategory 加了 other 作为未知兜底,筛选与卡片分流都改成值比较 而不是穷举 switch。 - MemoryService 只缓存最近 90 天。GetStorage 是把整个 box 读进内存的 JSON, 一年三千多条全量缓存会让每次启动越来越慢。 - 拉取失败保留本地缓存并标 staleFromCache,页面显示行内「当前显示的是本地缓存」, 不清空、不弹红条。 - 勾选完成是乐观更新 + 失败回滚。不回滚会让用户以为已经勾上,下次进页面又变回来。 - memory_today / memory_upcoming 加进 AuthInterceptor 的红条抑制名单: 它们在启动与前台恢复时自动调,用户什么都没点就吃一条英文内部状态词很差。 删除 mock mockCalendarEvents() 与它引用的 44 个 demo* i18n key 一并删除(41 个文件共 484 行)。 真实数据上线后混着演示数据会让人以为系统出错。 本地提醒(MemoryReminderScheduler) - 通知 id 占 920000 起的固定区间,**逐条 cancel,绝不 cancelAll()** —— 仓库里已有 两个各自 new 的 FlutterLocalNotificationsPlugin 实例,cancelAll 会顺手干掉正在 录音的前台通知,表现是录音悄悄失去前台身份被系统回收。 - 单独的通知通道:录音提示是常驻低优先级通道,共用会让提醒被降级成静默。 - **只查权限不申请**。申请必须走 PermissionUtil(它收敛了过审要求的 48 小时冷却), 且只允许用户在权限管理页主动发起,这里主动申请等于绕过合规规则。 无权限时降级为「打开 App 才提醒」,不阻塞。 - 精确排期失败(Android 14 起 SCHEDULE_EXACT_ALARM 默认不授予)自动退化成 inexact 再试一次——不精确的提醒也比没有强,日志会记下来。 - 拿不到系统时区时按本机偏移找等价区兜底,不落回 UTC(那会让国内用户全部偏 8 小时)。 每日播报(MemoryDailySheet) - **不 await 在启动路径上**。冷启动已串四次网络往返,仓库刚做过相反方向的优化 (6a64c3d0 把会议模板拉取改成 unawaited)。这里是进主页后异步拉、拿到再弹。 - 排在隐私政策与强更弹窗之后,且必须在登录态确认之后。 - 通话翻译进行中一律不播报:两者共用同一个 AVAudioSession,在对方刚调好的会话上 再设一次会把通话音频路由弄乱(iOS 那条 phone_call 分支被改坏过三次)。 - 关窗必须真停:speakStream 是队列式播放,只 stop 不清队列会「关了还在念下一句」。 - 播报文案本地拼,不调 LLM。 生命周期与账号 - 切前台的刷新挂在 AppLifecycleManager(仓库已有 emai/translation/permissions 三个各自实现的 observer,本类就是收敛点),全部 unawaited。 - 登出清空记忆数据与已排提醒。不清的话换账号会看到上一个人的日程花销灵感, 提醒也会继续按上一个账号的安排响。 助手闹钟迁移 SET_clock 改为双写:本地 reminders 保留一个版本(上传失败时本机仍可用), 同时写服务端 memory_item(category=alarm)。**幂等键用指令 id** —— 本类的按 id 去重只在本进程内存里,换机/重装/重启后全失效,服务端靠 (uid, client_key) 唯一索引兜住。认不出的重复规则按单次处理,宁可只响一次也不按猜的规则天天响。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
1 month ago |