Rodger-Wang
|
18a7160183
|
清理:QQ 音乐整体下线(插件、Dart 服务、灵犀 AI 里的 QQ 音乐工具、iOS 回跳)
用户决定整个删掉(2026-09-24)。
- 删 local_plugins/qq_music 整个插件(含 QPlayAutoSDK 与 Android aidl aar,约 3.5MB);
pubspec、android settings.gradle、根 build.gradle 与 app build.gradle 里的 flatDir 引用同步去掉
- Dart:QqMusicService(qq_music.dart)删除;翻译页 suspend/restoreOtherServices 里暂停/恢复 QQ 音乐的分支去掉;
main.dart 的 deepvoice://qqmusic 深链接分支与 SplashController.handleQQMusicLogin 去掉
- iOS Runner:AppDelegate 里把 eaimar:// 转给 "qq_music" 通道的那段去掉(该通道名本来就和插件
实际注册的 com.yunqiinnovation.qq_music 对不上,回跳一直是空转);LSApplicationQueriesSchemes 去掉 qqmusic
- 灵犀/移动精灵 AI(原生):
- chat_api 两端删 QQMusicSystemFunctionHandler(9 个 qqmusic_* 工具);MCPClient 的 "QQ"/未指定/AUTO
一律走网易/系统音乐工具
- agent_service 两端删 qqmusic_searchandplay_songs 的播放处理、Android 的 QQMusicSingleton 初始化与
autopause/resume 的 QQ 分支;Package.swift / build.gradle 的 qq_music 依赖去掉
- 行为变化:Dart 从未传 musicServiceType,Android 原本就不注册音乐工具、QQ 分支走不到,无变化;
iOS 原本「装了 QQ 音乐就给 AI 用 QQ 工具」,现在一律用网易/系统音乐工具
保留:MusicManager(翻译时暂停其它 App 的音乐,走 music_service,与 QQ 无关);
eaimar:// URL scheme 注册(App 级深链接入口,只去掉转发给 QQ 的那步)。
验证:dart analyze 无 error、无 unused_import;flutter test 221 全过;flutter build ios --no-codesign 通过。
Android 编译同第 4 批,受本机缺 Rosetta 影响未验证。
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 362a3ac70aac2d79d0483076997949c8b2dac2a9)
|
2 weeks ago |
Rodger-Wang
|
8aa2c9bef7
|
清理:第 4 批,本地插件与原生层的空壳插件、未链接二进制与残留配置(约 12MB)
- 整个插件:local_plugins/test(模板插件,agent_service 的 Package.swift 声明了却没 import)、
local_plugins/bytedance_speech(不在任何 pubspec,Kotlin 零 import);连带改 pubspec、
agent_service 的 Package.swift 两行、android/settings.gradle.kts 两行、agent_service build.gradle.kts 一行
- 不再链接:device_jieli/android(pubspec 只剩 ios)、根目录 SpeexKit.xcframework、
ble_service 里注释写明已移除的 JLAudioUnitKit.xcframework、app res/raw 三个无引用的 mp3、
两份多余的 GoogleService-Info.plist、swift_sdk_local/Tests(连同 Package.swift 的 testTarget)
- 示例/模板残留:qq_music 的 ApiSample.kt / PcmExampleActivityNew.kt、azure_speech/lib/main.dart、
floating_ui_plugin 的模板测试、recorder_holder/sdk/example
- azure_speech Android:Dart 从不使用的 azure_speech/translation 方法通道与 translation_events 事件通道、
TranslationMethodHandler,以及因此失去引用的 MicrosoftTranslationAndTtsService 与两个字段
(MicrosoftTranslationServiceImpl 仍被 AzureAsrToAsr 使用,保留)
- agent_service Dart 包装里 13 个业务层零调用的方法(原生侧不动)
- 原生配置:Facebook 登录残留(Android meta-data/strings、iOS Info.plist 键、URL scheme 与 15 个查询 scheme)、
无对应代码的 NSMotionUsageDescription、已无依赖的火山引擎 Maven 仓库
验证:dart analyze 无 error;flutter test 221 全过;flutter build ios --no-codesign 通过。
⚠️ Android 编译本机未验证:azure_speech 的 generateDebugProto 需要执行 protoc-gen-grpc-java 1.57.2,
其 osx-aarch_64 制品实为 x86_64,本机未装 Rosetta 无法运行。已确认在本批改动之前的提交上同样失败,
与本次清理无关;装好 Rosetta 后需补跑一次 flutter build apk。
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 86df62086dc029ed2b2c7199dccc2d8a6e7a340d)
|
2 weeks ago |
Rodger-Wang
|
1f9925773d
|
清理:第 3 批,Flutter 业务代码孤儿文件、死路由、无人用的服务与依赖(约 7500 行 + 24MB 素材)
- 23 个从 main.dart 不可达的孤儿文件(火山 ASR/TTS/声音复刻一组 3635 行、goodsai 页、
siri_orb/global_ai_orb、intro_overlay_builder、杰理 OTA 残留弹窗与 model 等)
- lib/modules/voice_replication 整目录:Routes.voiceReplication 唯一引用在注释里
- 注册但无人 find 的服务:TranslationUsage / MicrosoftTranslation / Navigation / Location
- api.dart 三个服务端已无接口的方法:getAliToken / getMusicUrl / getproducts
- pub 依赖:tencentcloud_cos_sdk_plugin、flutter_tts、flutter_shaders、flutter_svg、synchronized、
markdown(flutter_markdown 仍会传递依赖)、pip;shaders/ 随 flutter_shaders 一起去掉
- 素材:assets/images/agents(19MB)、assets/weather(2MB)、explore*、defaultPicture*、
robot_light/glasses_light/qqmusic/music、两个 lottie、listening.mp3
- dart analyze 报的 unused_import 31 处、unused_element 17 个(含连带暴露的 4 个)、
unused_field 8 个、unused_local_variable 8 处,全部清零
⚠️ pubspec.lock:仓库里这份锁与 pubspec 早已对不上(改动前的 pubspec 跑 pub get 也会重解析 82 行),
本次重解析后 firebase_auth 6.7.0 拉进的 firebase-auth 24.2.0 要求 Kotlin 2.3、工程是 2.1.10,
Android 编不过;故把 firebase_core / firebase_auth 钉回 4.13.0 / 6.5.7(pubspec 里有说明)。
其余 36 项均为小版本升级。
验证:dart analyze 无 error 且 unused_* 为 0;flutter test 221 全过;
flutter build apk --debug 与 flutter build ios --no-codesign 均通过。
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 355632a5b41818d33ff9c36b08440777d955cd62)
|
2 weeks ago |
Rodger-Wang
|
d50edc6635
|
deploy/dl: 下载页改名 download.html;client(ios): 构建号 20
- 下载落地页线上路径改为 dl.ymaikj.com/EAIMAR/download.html(index.html → download.html)
- Xcode 写回的工程改动:CURRENT_PROJECT_VERSION 16 → 20、工程格式升级
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
98e640ecfe
|
client(ios),docs: bundle id 修正、版本号 1.0.2+21,CLAUDE.md 记录本轮四条结论
- GoogleService-Info.plist 三份副本的 BUNDLE_ID 由上一代的 com.saitong.voitrans
改成真实的 com.yimai.eaimar。⚠️ PROJECT_ID 仍是 voitrans:plist 是从 Firebase
控制台下载的,要正的话得在那边注册新的 iOS 应用再重新下载三份。
- pubspec 版本 1.0.2+19 → +21;Xcode 自动改写的工程文件(MARKETING_VERSION 1.0.2)。
- CLAUDE.md 新增/更新四节:通话翻译耳机杂音的四个独立原因;德语/意大利语卡死的
并发初始化竞态与三层修法;版本更新判定的 2026-09-23 新口径;后台上传 APK 报
「必须先选择应用」的原因(X-App-Id 只有绑定应用的账号才有)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
28a6089ded
|
client: 版本号 1.0.1+16 → 1.0.2+19;Xcode 自动改写的工程文件
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
7ce56f693d
|
client: 通话翻译音频链路修复(iOS GATT 写入、后台保活、状态竞态)
iOS 上恒玄走的是 GATT over BR/EDR 而非 BLE,之前按 BLE 模型处理导致一连串问题。
本次改动都有真机数据支撑(2026-09-21 压测)。
GATT 写入(bluetooth_manager)
- 写类型改为优先 .withoutResponse。ATT 是请求-响应协议,带应答写每包等 ACK,
追不上下行 50 包/秒,表现为音频越听越滞后。
- canSendWriteWithoutResponse 只作观测、不作门槛。它是 CoreBluetooth 为 BLE
设计的提示,在 BR/EDR GATT 上不反映真实能力(实测 100% 为 false 却零丢弃);
拿它当闸门会死锁:不写就不回调,不回调就永不翻真,实测 548 包只出去 11 个。
- 控制命令无条件直发、不排队。排到积压音频后面会让 AA 09/AA 06/queryCallState
全部发不出去,曾被误判成「固件换了命令号」。
- 不再对标准电池服务 0x180F 做任何 ATT 操作:它在 Echo-one 上是空壳(值恒为 0),
而挂在它上面的读/订阅会把整条 ATT 通道堵死。
后台存活(translation)
- 通话翻译期间持有 BackgroundKeepalive。两路音频全走耳机 GATT,手机侧没有
AVAudioSession 的实际 I/O,UIBackgroundModes=audio 不成立,切后台几十秒后
上行被掐断:WebSocket 还连着,但服务端收不到语音,靠默认 VAD 每 30 秒强切一句,
吐出空译文或幻觉 —— 表现为「切后台后只剩译文、原文全没」。
- BackgroundKeepalive 不再无条件把音频类别改成 .playback,避免踩掉通话翻译
正在跑的 .playAndRecord 录音链路。
状态竞态(azure_speech / device_hub)
- markReady 的就绪状态记在插件上、建桥时继承。建桥与 AST 异步初始化是两条独立
路径,初始化先完成时那句 optional chaining 打在 nil 上静默丢失,之后 ready
恒为 false、上行帧全积在环形缓冲里,表现为「打开通话翻译没反应,再开一次才好」。
- DeviceHub.inCall 改以会话快照为准。事件会随 worker 在断连时被 dispose 而丢失,
重连后 ever 又不回调当前值,且后续重复赋同值不触发,永远不会自愈。
可观测性
- 下行发送器与 GATT 写入的日志从 NSLog 改为 os_log(release/profile 包里
idevicesyslog 只收得到 os_log),并经 DeviceSession.diagnostics() 暴露给 Dart,
写入文件日志后可用 devicectl 直接拉取,不依赖 usbmuxd。
- 新增 inCall 变化、闸门拦截、通话状态翻转、前后台切换四处观测点。
其它
- DeviceSession 新增 diagnostics()(默认空表);SmartcarSession 因是 implements
而非 extends,同步补了实现。
- CLAUDE.md 记录本次全部实测结论,含 BB 8E 为通话状态权威、BB 02/03 是噪声
(已知问题,本次未改判定)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
8baa082d9e
|
client: 构建号递增;Xcode 自动改写的工程文件
pubspec 1.0.1+14 → +15,iOS CURRENT_PROJECT_VERSION 12 → 14。
project.pbxproj 其余改动是 Xcode 打开工程时自行改写的(objectVersion 54→60、
SPM 本地包引用的注释路径、Pods 脚本阶段补空的 inputPaths/outputPaths),
不影响构建配置。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
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 |
Rodger-Wang
|
41e3a023a3
|
client: 蓝牙名白名单(EaiRec/EaiCar);连接即按账号登记绑定;iOS 崩溃加固;弱网翻译超时
- 设备名闸门:支架只认 EaiRec/EAIMAR/Pad Note,车载香薰只认 EaiCar,固件名对不上直接不连;
一次性清掉此前被归一化名字污染的配对记录;Smartcar 资源与 agent 命名统一改为 EaiCar
- 绑定登记:新增 DeviceBindRegistry(按 uid|MAC 去重),支架也进 verifyConnected,
登录态从无到有时补报一次,换账号不再漏切绑定
- iOS 崩溃加固:补 NSPhotoLibraryAddUsageDescription(保存思维导图/统计图必崩);
StsAgent 先配音频会话再 installTap 并校验格式;支架录音两处 installTap 加 0Hz 守卫;
AzureTtsHelper 播放队列改加锁队列(三线程裸改 Array,release 下崩);
MicrophoneCapture 主线程不再用信号量等权限弹窗(看门狗误杀)
- 网络:connectionError 给中文文案;实时翻译请求 12s 超时,不再跟 30s 默认值等
- 附:AI 助手中枢设计文档 v0.2(未动代码)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
27cabe1bde
|
client: 设备内核抽象、Smartcar 车载香薰接入与语音纪要等一批改动
工作区里累积的 Flutter 客户端改动,随本轮一并入库。主要涉及:
- lib/devices/ 设备内核:DeviceHub / DevicePlugin / DeviceSession 抽象,
恒玄(bes)、EaiRec 支架(holder)、Smartcar 车载香薰(smartcar) 各自的插件实现,
业务层不再直接依赖任何厂商类;分层边界由 test/devices/ 下的测试守着
- local_plugins/device_plugin_interface、device_smartcar、recorder_holder:
纯 Dart 契约与原生侧实现
- lib/modules/ 能力模块注册表、语音纪要、OTA 升级等模块
- test/ 新增设备协议、Ogg Opus 时长、模块注册表、路由等金测试
- ios/sync_spm_platform.sh:修 Xcode 直接构建时 SPM 平台版本被打回 13.0 的问题
验证:dart analyze lib 无 error(264 条 warning 均为 HEAD 上既有的 unused_import /
avoid_print 等 lint,非本次引入)。未跑真机与打包。
注:这批不是本次会话中由 Claude 编写的,此处仅做入库。
|
4 weeks ago |
Rodger-Wang
|
6299920145
|
点结束时自动关掉屏幕直播;拾忆页去掉手动新增的 + 号
1) 多媒体翻译点「结束」后,系统那条屏幕直播不再挂着
主 App 对「用户从系统面板发起的广播」没有任何控制权——RPBroadcastController
只管 App 内 API 起的那种。唯一能终止它的是扩展自己调
finishBroadcastWithError。所以改成让扩展感知主 App 是否还在:
- stopAll() → _teardownCallModeAudio() → stopScreenCapture() 关掉 socket
(这条链原本就有)
- 扩展侧:曾经连上过、又断开超过 3 秒连不回来 → 判定主 App 收工,结束广播。
没连上过就断开不算——那是「先开广播、还没点开始翻译」的正常情况。
宽限期不能太短,主 App 瞬断会误杀用户正在用的广播。
- 另加 1 秒心跳轮询:只靠 processSampleBuffer 里检查是不够的,视频停了或者
没有任何 App 出声时 audioApp 帧可能长时间不来,那样永远发现不了主 App
已经走了,屏幕上那条红条会一直挂着。
⚠️ finishBroadcastWithError 必须传 NSError,系统会把 localizedDescription
弹给用户,所以给的是一句正常说明而不是错误信息。
2) 拾忆页去掉右下角的 + 号
内容应当由助手/会议纪要自动落进来,手动新建不是当前需要的能力。
卡片点开编辑保留(TodoScopeList 里)——那是改助手记错的时间/内容用的,
与「手动新建」不是一回事。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
a9ce8bcdef
|
广播扩展改走 127.0.0.1 环回 TCP,去掉 App Group 依赖
上一版用 App Group + Unix domain socket 传音频,结果签不出包:
- com.yimai.eaimar 的 profile 不带 App Groups capability
- com.yimai.eaimar.BroadcastExtension 没有任何 profile
而 Xcode 又登录不上账号去申请(-1200)。实测原因是网络层:
developer.apple.com 通(200),appleid.apple.com 与
developerservices2.apple.com 都连不上(000)——后两个正是登录与签发 profile
要走的域名。
改成环回 TCP 之后不需要任何 entitlement:扩展和主 App 在同一台设备上,
127.0.0.1 天然互通,而且不触发 iOS 的「本地网络」隐私弹窗(那个只针对局域网,
环回是明确豁免的)。主 App 的 entitlements 回到原样,用已有的 profile 就能签,
扩展的 profile 由自动签名就地生成。已真机安装成功。
代价:没有 App Group 就没有地方发布「主 App 这次监听在哪个端口」,只能约定
一个固定端口(52781,两边手工同步,注释里写明了)。撞端口时主 App bind 会
失败并打日志,不是静默失败。
顺带补的两个 socket 选项:
- SO_REUSEADDR:上次进程被杀后端口停留在 TIME_WAIT,不开的话几十秒内 bind
都会 EADDRINUSE,表现是「刚重启 App 就用不了」。
- TCP_NODELAY:音频要低延迟,攒包没有意义。
- 只绑 127.0.0.1 而不是 INADDR_ANY,否则这个端口对局域网可见。
另外记两个这次踩到的构建坑:
- flutter clean 会删掉 ios/Flutter/ephemeral/,其中的 swift_sdk_local 软链由
Podfile 的补丁在 pod install 时创建、**flutter 不会自己重建**,于是后续
xcodebuild 直接卡在 "Could not resolve package dependencies"。清理之后要
补跑一次 pod install。
- 跑过 --no-codesign 之后再跑签名构建,会复用 build/ 里未签名的 framework,
装机时报 objective_c.framework "invalid signature"。中间做过 no-codesign
验证的话,出正式包前要先 flutter clean。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
8377745992
|
iOS 多媒体翻译接入 ReplayKit 广播扩展,真正拿系统音频
iOS 不允许一个 App 读另一个 App 的音频,Broadcast Upload Extension 是系统
留的唯一口子。链路:
其他 App 播放 → 系统录屏广播 → BroadcastExtension(独立进程)
收 RPSampleBufferType.audioApp → 转 16k/mono/Int16
→ App Group 容器下的 Unix domain socket
→ 主 App BroadcastAudioReceiver
→ azureAsrHelper.audioStream.saveAudioDataTo() ← 已有的 external 通路
→ ASR → 翻译 → 弹幕
新增:
- ios/BroadcastExtension/{SampleHandler,BroadcastAudioSocket}.swift、Info.plist、
entitlements、Extension.xcconfig
- azure_speech 插件的 BroadcastAudioReceiver.swift + 三个 method channel
(startBroadcastAudioReceiver / stopBroadcastAudioReceiver / presentBroadcastPicker)
- Runner.entitlements 加 App Group;xcodeproj 加 BroadcastExtension target
并在 Runner 里内嵌
对上层复用已有的 requestScreenCapture/stopScreenCapture 接缝(Android 是
MediaProjection,iOS 是广播扩展),控制器里两端走同一条分支。
几处踩过的坑,都写进了注释:
- SOCK_STREAM 不保留消息边界,帧必须带 4 字节长度前缀,否则喂给 ASR 的是
错位样本。
- CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer 不拷贝数据,只把
mData 指向 blockBuffer 的内存;blockBuffer 出作用域即释放,交出去就是野指针。
改成在 withExtendedLifetime 里 memcpy 出来。
- AudioBufferList 的 buffer 个数要按 interleaved 与否算,写死 1 会让非交错
立体声整帧拿不到。
- sun_path 只有 104 字节,socket 只能放在 App Group 容器根下,不能建子目录。
- 扩展侧 connect 失败要限流重试(1s),每帧重连会把扩展 CPU 打满,而它的
内存上限只有 50MB。
- SO_NOSIGPIPE:主 App 挂掉时写 socket 默认会直接杀死扩展进程。
- media 这个 ASR 模式的会话配置必须排在 audioSourceType 判断之前,且要
.mixWithOthers —— 否则我们一 setActive 就把正在播放的那个 App 掐了。
- Embed App Extensions 阶段必须插在 Thin Binary 之前。追加到最后会和
Flutter/CocoaPods 的脚本阶段构成依赖环,Xcode 报 Cycle inside Runner。
⚠️ 尚未打通签名:需要在苹果开发者后台登记 App Group group.com.yimai.eaimar、
给 com.yimai.eaimar 开 App Groups capability、并为
com.yimai.eaimar.BroadcastExtension 建 App ID。Xcode 当前还登录不上账号
(-1200)。在这之前 iOS 包签不出来。代码已用 --no-codesign 验证可编译、
appex 也正确内嵌进 PlugIns/。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
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 |
Rodger-Wang
|
24e17b3434
|
收拢积压改动:恒玄 OTA、发版配置拆表、实名认证、芯片厂商/代工厂管理等
把工作区里积压的多轮改动一次性入库。主要几块:
客户端
- 恒玄 OTA 打通:新增 local_plugins/bes_ota_manager(自包含 BES SDK,
Android Java + iOS ObjC,SPP OTA 2.0),路由切到 BesOtaUpgradeView /
BesOtaUpgradeController,配套下载服务;杰理那套文件保留但已不挂路由。
- 通话翻译新增阿里 3.5 档(alibaba35_language_config.dart,
qwen3.5-livetranslate-flash-realtime + 实时声音复刻)。
- 实名认证接入、游客登录改走服务端 api_sgin 的 Tourists 分支、
功能闸门由 GuestGuard 改为 IdVerifyGuard。
- 41 个语种文案同步(OTA 新增 28 个 key 等)。
- 默认主题改为浅色,不跟随系统。
服务端
- 发版类配置拆表:app_release(版本控制 / 游客显隐,一应用一行)与
channel_app(渠道级下载地址 / 上架状态 / 支付渠道)分家,
新增 comm/apprelease.go 与 console/api_apprelease.go。
- 授权码字符串整体移除:删除 utils/license 包,新增 utils/devcode
(MAC 规范化与 PID 反解),配套迁移 SQL 在 docs/migrations/。
- console 新增芯片厂商、代工厂、用户管理接口与对应 admin 页面。
- 身份证实名核验(sys/idverify)与第三方服务的服务端专用类别。
其它
- .gitignore 补挡 apps/services/lego/sys/gin/log-*.log:gin 子系统跑起来
会按时间戳滚动生成日志(多为 0 字节),与 apps/services/comm/log 同一
性质,不进版本管理。已跟踪的 log-2026-08-20 那个仍在库里,未动。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
2d1ee0b783
|
apps/client 首次入库(Flutter 移动端)
从 /Users/yunyanzhineng/work/eaimar_client-main 搬入,未带原 .git,历史留在原仓库。
3215 个文件 / 约 118MB,其中约 40MB 是构建必需的预编译 SDK(azure_speech 的
MicrosoftCognitiveServicesSpeech.xcframework、device_jieli 的 JL_BLEKit 等)。
build/ (5.2G)、ios/Pods/ (311M)、android/.gradle/ (120M) 已由 .gitignore 挡掉。
本次入库的代码包含两项刚做完的改动:
1) 游客登录入口接上服务端开关。此前 _checkGuestLoginConfig() 恒定写死 true,
入口根本不读配置。现改为取服务端算好的结论,并把入口所在的 Builder 改成 Obx
——配置是异步拿到的,Builder 只渲染 onInit 时的初值 false,入口再也不会出现。
拿不到配置时按隐藏处理:默认显示会在送审期把不该露的入口露给审核员。
⚠️ 已发布的老客户端关不掉(写死 true),只对本版及以后生效。
2) iOS 上架合规修复:
- 移除 ATT/IDFA:删掉 Info.plist 的 NSUserTrackingUsageDescription、
SKAdNetworkItems、NSAdvertisingAttributionReportEndpoint,删掉 splash 里的
授权请求与 app_tracking_transparency 依赖。本 App 从不读取 IDFA,也没有任何
广告/归因 SDK,弹框纯粹劝退用户,还要在 App Store Connect 申报追踪数据类型。
- ios/Podfile 里那段 config.build_settings.delete('NSUserTrackingUsageDescription')
是无效代码(该 key 在 Info.plist 里,不是 build setting),换成注释说明。
- 通讯录符号(ITMS-90683 被拒原因)随之一并解决:permission_handler 走 SPM 后
按 Info.plist 有无对应 key 决定编不编,判定结果被 SwiftPM 按内容哈希缓存,
改完 plist 必须清 DerivedData 与 manifests 缓存才会重新求值。
另附 GOOGLE_PLAY_RELEASE_CHECKLIST.md:Google Play 上架阻塞项的实测梳理
(targetSdk 36 与 Billing 8 的 2026-08-31 期限、16KB 页对齐不合规的两个 .so、
支付回退逻辑的下架风险)。
⚠️ android/app/sign/ 下的 eaimar.keystore 与 Eaimar.properties(含明文签名密码)
随本次提交入库,与本仓库"私有仓库下接受真实凭据"的既有取舍一致。
仓库若要公开,这两个必须先摘出去。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |