Cherry Studio 隐私政策确认机制深度解析:从强制弹窗到数据收集开关的完整实现

发布时间:2026/9/19 20:18:48
Cherry Studio 隐私政策确认机制深度解析:从强制弹窗到数据收集开关的完整实现 Cherry Studio 隐私政策确认机制深度解析从强制弹窗到数据收集开关的完整实现【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio导读Cherry Studio 在 2026-05-31 版本更新后引入了强制性的隐私政策确认机制新用户在 onboarding 流程中必须审阅隐私政策存量用户若缺少有效确认或确认版本早于20260531则必须确认更新后的政策才能继续使用应用。本文将基于 关联变更文档 的变更说明结合仓库源码深入剖析该机制的业务规则、判断逻辑、弹窗交互、v1 数据迁移处理与测试验证帮助你完整理解并正确处理这一破坏性变更。一、变更内容与核心规则本次变更的核心是一套版本化的隐私政策确认policy acknowledgement机制。变更文档明确了以下几条关键规则规则说明新用户在 onboarding首次引导流程中审阅隐私政策作为引导的一部分存量用户若存储的确认信息缺失或确认的版本早于20260531必须确认更新后的政策才能继续使用数据收集选择保留确认更新保留用户已有的匿名数据收集选择不会因确认动作被重置新安装默认值新安装仍默认启用匿名数据收集对话框不可跳过确认对话框无法关闭、无法点击遮罩或按 Esc 跳过分析功能闸门在确认最新政策之前Analytics匿名数据分析保持不活跃引导完成联动onboarding 完成时将政策确认与用户当前的数据收集选择一同存储需要特别说明的是变更文档记录的是20260531这一政策版本而从当前仓库源码 constants.ts 可以看到最新政策版本常量已演进为LATEST_PRIVACY_POLICY_VERSION 20260820。版本号本身是逐次递增的确认机制的逻辑始终是以最新版本为准后续每次政策更新只需更新该常量即可复用具同一套强制确认流程。二、触发判断useIsPrivacyUpdateRequired 的判定逻辑是否弹出确认对话框由 useIsPrivacyUpdateRequired.ts 这一 Hook 统一判定其逻辑非常简洁export function useIsPrivacyUpdateRequired(): boolean { const [policyVersion] usePreference(app.privacy.policy_version) const [dataCollectionEnabled] usePreference(app.privacy.data_collection.enabled) return dataCollectionEnabled policyVersion ! LATEST_PRIVACY_POLICY_VERSION }判定条件为两个条件的与ANDapp.privacy.data_collection.enabled为true匿名数据收集处于开启状态app.privacy.policy_version不等于最新政策版本LATEST_PRIVACY_POLICY_VERSION。由此可以推导出几个重要的行为分支政策版本已是最新无论数据收集开关状态如何→ 不弹窗用户直接进入应用数据收集已关闭无论政策版本为何→ 不弹窗。这符合关闭了数据收集即无需再确认的产品语义——因为只有启用数据收集的用户才真正受到新政策约束数据收集开启且政策版本落后或为空→ 强制弹窗且不可跳过。这意味着确认政策与数据收集开关这两个偏好项之间是强耦合的确认动作写入政策版本号而拒绝确认的出口则是关闭数据收集。两个偏好项都通过usePreference读取底层持久化于偏好数据库中键名分别为app.privacy.policy_version与app.privacy.data_collection.enabled。三、强制确认交互PrivacyPolicyUpdateGate 的实现细节弹窗的完整交互由 PrivacyPolicyUpdateGate.tsx 实现。该组件以拦截闸门Gate的形式挂载在应用主窗口渲染逻辑上位于应用入口与业务界面之间。3.1 不可跳过的对话框从源码可以看到对话框对用户的所有跳过路径都做了封堵Dialog open{open !showPolicy} DialogContent showCloseButton{false} closeOnOverlayClick{false} classNamesm:max-w-[460px] onEscapeKeyDown{(event) event.preventDefault()} onPointerDownOutside{(event) event.preventDefault()}showCloseButton{false}不显示右上角关闭按钮closeOnOverlayClick{false}点击遮罩层无法关闭onEscapeKeyDown与onPointerDownOutside均调用preventDefault()按 Esc 或点击外部均被拦截。这从实现层面印证了变更文档中对话框不可跳过的描述。3.2 两个操作出口对话框底部提供两个按钮对应两条唯一的出路出口一确认政策acknowledgeconst acknowledge useCallback(async () { setIsUpdatingPrivacy(true) try { await setPolicyVersion(LATEST_PRIVACY_POLICY_VERSION) } catch { toast.error(t(privacy_policy_update.acknowledge_failed)) } finally { setIsUpdatingPrivacy(false) } }, [setPolicyVersion, t])将app.privacy.policy_version写入最新版本号不触碰数据收集开关因此用户原有选择被完整保留。写偏好使用了PESSIMISTIC_PREFERENCE_OPTIONS { optimistic: false }即采用悲观更新策略——等待持久化成功后才更新 UI 状态避免界面先行切换而底层写入失败导致的状态不一致。出口二拒绝并关闭数据收集continueWithoutConsentconst continueWithoutConsent useCallback(async () { setIsUpdatingPrivacy(true) try { await setDataCollectionEnabled(false) setShowPolicy(false) } catch { toast.error(t(privacy_policy_update.acknowledge_failed)) } finally { setIsUpdatingPrivacy(false) } }, [setDataCollectionEnabled, t])选择拒绝Decline的用户应用会将app.privacy.data_collection.enabled置为false。由于触发条件dataCollectionEnabled policyVersion ! LATEST中的第一项变为false闸门随之放行。这一设计使得拒绝确认与停止匿名数据收集在语义和状态上严格绑定——拒绝即意味着关闭数据收集。两个出口在写入失败时都会通过 toast 提示privacy_policy_update.acknowledge_failed错误信息并且按钮在写入期间进入loading/disabled状态防止重复提交。3.3 政策全文查看对话框描述区域嵌入了一个查看政策链接按钮点击后切换到 PrivacyPolicyDialog.tsx 展示政策全文。该组件根据当前语言与主题动态加载资源export function getPrivacyPolicyAsset(language: string): privacy-en.html | privacy-zh.html { return language.toLowerCase().startsWith(zh) ? privacy-zh.html : privacy-en.html } export function buildPrivacyPolicyUrl(resourcesPath: string, language: string, theme: ThemeMode): string { const filePath AbsoluteFilePathSchema.parse( joinPath(resourcesPath, cherry-studio/${getPrivacyPolicyAsset(language)}) ) const themeName theme ThemeMode.dark ? dark : light return ${toFileUrl(filePath)}?theme${themeName} }政策全文以打包资源cherry-studio/privacy-en.html/privacy-zh.html的形式随应用分发按zh前缀匹配中文其余语言回退英文并通过?themedark|light查询参数适配暗色/亮色主题。四、v1 数据迁移存量用户如何被正确对待存量用户的隐私状态来自旧版v1数据由 v2 迁移器负责搬运到新的偏好键。迁移映射定义在 PreferencesMappings.ts 中第 78–80 行旧键enableDataCollection→ 新键app.privacy.data_collection.enabled第 455 行旧键privacyPolicyVersion→ 新键app.privacy.policy_version4.1 默认值在源数据缺失时迁移器回落到默认值。由 PreferencesMigrator.test.ts 的测试用例可以确认默认值const policyVersion await selectByKey(dbh.db, app.privacy.policy_version) expect(policyVersion[0]?.value).toBe() // 政策版本默认空字符串 const dataCollection await selectByKey(dbh.db, app.privacy.data_collection.enabled) expect(dataCollection[0]?.value).toBe(true) // 数据收集默认开启即新安装 / 无源数据的场景下政策版本为空、数据收集默认开启这正好会命中useIsPrivacyUpdateRequired的强制弹窗条件——与变更文档新安装默认启用匿名数据收集的描述一致。4.2 迁移保留旧确认版本测试用例it.each([20260531, 20240101])验证了旧版隐私状态的完整搬运// migrates v1 privacy policy version %s and data collection choice const policyVersion await selectByKey(dbh.db, app.privacy.policy_version) expect(policyVersion[0]?.value).toBe(version) // 政策版本原样迁移 const dataCollection await selectByKey(dbh.db, app.privacy.data_collection.enabled) expect(dataCollection[0]?.value).toBe(false) // 数据收集选择原样迁移这正是变更文档Users migrated from v1 withprivacyPolicyVersionalready set to20260531are not prompted again的实现基础迁移保留了旧确认版本若该版本与最新政策版本一致闸门便不会触发。反之若旧版本早于最新版本如示例中的20240101用户将在迁移后首次启动时被要求确认更新后的政策。4.3 旧弹窗标志不再生效变更文档特别指出旧的privacy-popup-acceptedlocalStorage 标志不被视为对当前政策版本的确认。迁移器对此有专门测试it(does not treat the legacy popup flag as privacy policy acknowledgement, async () { const ctx createTestContext({ localStorage: [{ key: privacy-popup-accepted, value: true }] }, dbh.db) await migrator.prepare(ctx) await migrator.execute(ctx) const policyVersion await selectByKey(dbh.db, app.privacy.policy_version) expect(policyVersion[0]?.value).toBe() // 政策版本仍为空 → 必须重新确认 })即使 v1 时代用户曾在弹窗中点击过接受迁移后policy_version依然为空字符串仍会被判定为未确认最新政策从而在启动时看到强制确认对话框。该旧标志在迁移完成后即失去语义相关读取代码保留在 legacyV1BrowserData.ts 供清理参考。五、设置页中的数据收集开关用户在设置界面仍可手动控制匿名数据收集。设置页 BasicDataSettings.tsx 通过同一偏好键读写const [enableDataCollection, setEnableDataCollection] usePreference(app.privacy.data_collection.enabled)这里改动的开关值与确认机制共享同一个存储键关闭开关会立即将useIsPrivacyUpdateRequired的第一个条件置为false反之若用户在此重新开启数据收集而政策版本仍未确认例如通过迁移保留了旧版本号、但低于最新版则下次启动时闸门会重新弹出。六、国际化文案该机制的全部 UI 文案均通过 i18n 管理相关 key 分布在 en-us.json 及全部 12 个语言文件zh-cn、zh-tw、ja-jp、de-de、fr-fr 等中i18n key用途privacy_policy_update.title弹窗标题privacy_policy_update.description_before_link链接前的说明文字privacy_policy_update.policy查看政策链接文案privacy_policy_update.acknowledge_failed写入失败时的 toast 提示onboarding.privacy.accept_and_continue确认按钮文案接受并继续common.decline拒绝按钮文案七、变更影响小结与处理建议综合变更文档与源码实现本次破坏性变更对各方的影响面如下用户侧升级到 2026-05-31 及之后版本后首次启动可能看到不可跳过的政策确认弹窗确认后数据收集选择保持不变拒绝则自动关闭匿名数据收集。已确认过最新政策当前仓库中为20260820的用户不受影响。迁移侧v1 用户的privacyPolicyVersion与enableDataCollection被原样迁移旧privacy-popup-accepted标志无效无源数据时默认政策版本为空 数据收集开启。版本演进后续政策更新只需在 constants.ts 提升LATEST_PRIVACY_POLICY_VERSION闸门逻辑无需改动即可对所有存量用户重新生效。对集成方或发行方而言务必在升级路径中向用户明确解释确认政策与数据收集开关的绑定关系并确保随应用分发包含对应语言的privacy-*.html政策资源任何绕过确认弹窗的方案如复用旧 localStorage 标志都无法通过迁移测试的校验。【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询