7.5 KiB
7.5 KiB
PRD: Do Not Sell My Personal Information 开关
1. 功能需求概述
实现 CCPA/CPRA 合规的 "Do Not Sell My Personal Information" 功能:
- 隐私政策页添加 Do Not Sell 开关(胶囊状态栏切换)
- 设置 → 隐私页添加相同开关
- 后端存储用户隐私偏好
- 开关默认开启(用户选择不被销售)
2. 背景与合规要求
CCPA/CPRA 要求
- 如果企业"销售"或"共享"个人信息用于广告,必须提供退出选项
- 即使声明不卖,仍需提供退出机制作为合规保险
- App Store 审核会检查此功能的存在
业务现状
- 隐私政策已声明
WE DO NOT SELL YOUR PERSONAL INFORMATION - 但缺少用户端开关实现
3. 技术方案
3.1 后端实现
存储位置: profiles.settings.privacy (JSONB)
设计决策: 将 privacy 字段升级为结构化的 PrivacySettings 对象
新增字段:
# backend/src/schemas/shared/user.py
class PrivacySettings(BaseModel):
"""隐私相关设置统一管理"""
do_not_sell: bool = True # 本次需求:Do Not Sell(默认 True)
profile_visibility: str = "public" # 未来扩展:个人资料可见性
# ProfileSettingsV1 中替换
class ProfileSettingsV1(BaseModel):
privacy: PrivacySettings = Field(default_factory=PrivacySettings) # 替换 dict
API 变更: 复用现有 PATCH /users/me/settings 接口,无需新增 endpoint
数据库迁移: 无需 Schema 变更(privacy 是 JSONB,可自由扩展)
向后兼容:
- 现有
privacy: dict数据会自动转换为PrivacySettings - 新字段使用默认值,不影响现有用户
3.2 Flutter 前端实现
数据层:
- 新增
PrivacySettingsclass(对应后端) ProfileSettingsV1.privacy类型从Map<String, Object?>改为PrivacySettings- 默认值:
doNotSell = true(默认开启)
UI 实现:
-
主开关:隐私通知设置页 (
PrivacyNotificationSettingsScreen)- 在"隐私" section 下(最前面)添加 "Do Not Sell" 开关
- 复用
SettingsSwitchTile组件(已有) - 文案: "Limit Use of My Personal Information"
- 默认值: True(开启)
- 开关状态实时保存
- "Profile Visibility" 保持现状(占位符,显示 Coming Soon)
-
快捷入口:法律中心列表页 (
LegalCenterScreen)- 在"隐私" section 下(法律文档列表之后)添加独立按钮
- 不是开关,而是快捷入口(SettingsMenuTile)
- 显示当前状态:已开启 / 已关闭
- 点击跳转到
PrivacyNotificationSettingsScreen - 满足 CCPA/CPRA 隐私政策中提供"Do Not Sell"入口的要求
-
隐私政策详情页 (
LegalDocumentScreen)- 保持现状:只显示静态 Markdown 内容
- 不添加开关(避免重复功能,保持文档阅读纯粹性)
代码结构:
// apps/lib/features/settings/data/models/profile_settings.dart
class PrivacySettings {
const PrivacySettings({
this.doNotSell = true,
this.profileVisibility = 'public',
this.personalization = false,
this.historyVisibility = 'private',
});
final bool doNotSell;
final String profileVisibility;
final bool personalization;
final String historyVisibility;
PrivacySettings copyWith({...});
}
class ProfileSettingsV1 {
final PrivacySettings privacy; // 替换 Map<String, Object?>
// ...
}
3.3 国际化
需要新增以下 key(示例):
settingsDoNotSellTitle: "Do Not Sell My Personal Information"settingsDoNotSellDescription: "Limit the use of your personal information"settingsDoNotSellHint: "When enabled, we will not sell or share your data"
4. 实现步骤
Phase 1: 后端数据模型
- Backend:
schemas/shared/user.py- 新增PrivacySettingsclass- 添加
do_not_sell: bool = True(本次需求) - 添加未来扩展字段:
profile_visibility,personalization,history_visibility
- 添加
- Backend:
schemas/shared/user.py- 更新ProfileSettingsV1- 将
privacy: dict[str, object]改为privacy: PrivacySettings - 添加
parse_profile_settings兼容性逻辑(dict → PrivacySettings)
- 将
Phase 2: 前端数据模型
- Flutter:
settings/data/models/profile_settings.dart- 新增PrivacySettingsclass- 包含
doNotSell,profileVisibility,personalization,historyVisibility - 实现
copyWith方法
- 包含
- Flutter:
settings/data/models/profile_settings.dart- 更新ProfileSettingsV1- 将
privacy类型从Map<String, Object?>改为PrivacySettings
- 将
Phase 3: UI 实现
- Flutter: 扩展
PrivacyNotificationSettingsScreen- 在"隐私" section 最前面添加 Do Not Sell 开关(SettingsSwitchTile)
- 保持 Profile Visibility 占位符不变
- Flutter: 扩展
LegalCenterScreen- 添加快捷入口按钮(SettingsMenuTile)
- 显示当前状态:已开启 / 已关闭
- 实现导航到
PrivacyNotificationSettingsScreen
Phase 4: 业务逻辑
- 开关状态持久化(通过
PATCH /users/me/settings) - 法律中心快捷按钮显示当前状态(从 settings.privacy.doNotSell 读取)
Phase 5: 国际化
- 添加中/英/繁三语 ARB keys
settingsDoNotSellTitle: Do Not Sell My Personal InformationsettingsDoNotSellDescription: Limit use of your personal informationsettingsDoNotSellEnabled: EnabledsettingsDoNotSellDisabled: Disabled
- 执行
flutter gen-l10n
5. 相关代码文件
Backend
| 文件 | 说明 | 变更类型 |
|---|---|---|
backend/src/schemas/shared/user.py |
新增 PrivacySettings class,ProfileSettingsV1.privacy 改为 PrivacySettings 类型 | 修改 |
backend/src/v1/users/router.py |
用户设置 API(复用 PATCH /me/settings) | 无变更 |
backend/src/v1/users/service.py |
设置更新逻辑 | 无变更 |
Frontend
| 文件 | 说明 | 优先级 |
|---|---|---|
apps/lib/features/settings/data/models/profile_settings.dart |
新增 PrivacySettings class,ProfileSettingsV1.privacy 类型修改 | P0 |
apps/lib/features/settings/presentation/screens/privacy_notification_settings_screen.dart |
添加 Do Not Sell 开关(主开关),保持 Profile Visibility 占位符 | P0 |
apps/lib/features/settings/presentation/screens/legal_center_screen.dart |
添加快捷入口按钮 | P1 |
apps/lib/features/settings/presentation/widgets/settings_section_widgets.dart |
复用 SettingsSwitchTile(已有) | - |
6. 测试计划
- 开关默认状态为开启
- 开关切换后值正确保存到后端
- App 重启后设置正确恢复
- 中/英/繁语言正确显示
7. 注意事项
- 数据模型升级:
privacy从Map升级为结构化PrivacySettings对象 - 向后兼容: 后端需要兼容现有
privacy: dict数据的读取 - 合规文案: 使用 "Limit Use" 而非 "We Don't Sell",避免直接承诺
- 默认值: 默认开启(opt-out),符合隐私保护趋势
- 开关位置: 主开关在隐私设置页,法律中心只提供快捷入口(避免重复)
- 开关形式: 使用
SettingsSwitchTile组件(已有) - 法律中心实现: 添加快捷按钮(SettingsMenuTile),显示当前状态,点击跳转
- 后端兼容性: 由于使用 JSONB 扩展,无需数据库迁移
- 状态同步: 确保法律中心快捷按钮显示的状态与实际设置一致
- Profile Visibility: 保持现状(占位符 + Coming Soon),不影响本次需求