# AppStore 缓存 Key 升级对照 ## 结论 - 旧线上工程与重构版工程的主应用 Bundle ID 均为 `com.yuanzhixiang.suixinkan`。通过 App Store 覆盖升级时,系统会保留原应用容器,因此旧版 `UserDefaults` 会继续存在。 - 本次升级策略为:**强制重新登录、旧偏好全部重置、不迁移旧缓存**。 - 新 Key 只对齐 Android 的业务作用域;iOS 统一使用 `_` 作为分隔符,不复制 Android MMKV 历史实现中无分隔符拼接的写法。 - 本文中的“旧线上工程”为 `/Users/hanqiu/Desktop/android_project/suixinkan_ios`,“重构版工程”为 `/Users/hanqiu/Desktop/suixinkan`。 ## Key 占位符 | 占位符 | 含义 | |---|---| | `O` | 旧版将 `[accountType, userId, storeId, scenicId]` 中的非空值用 `_` 拼接,再清洗非字母数字字符后得到的 accountKey | | `P` | 本次实施前重构版使用的 `_` 账号前缀;用户 ID 为空时可能退化为 `guest` | | `A` | 本次实施后统一使用的 `_` 账号前缀 | | `S` | 景区 ID(scenicId) | | `T` | 排队点位 ID(spotId) | | `R` | 角色编码(roleCode) | > `suixinkan.session.v1` 是旧工程中真实存在的单个 `UserDefaults` Key,其值为 JSON Data。表格中的 `suixinkan.session.v1[token]` 等写法仅表示该 JSON 内的字段,不是独立的 `UserDefaults` Key。 ## 新旧工程统一业务 Key 对照 | 业务数据 | 旧线上工程 Key | 重构版实施前 Key | 统一后的 Key | 升级处理 | |---|---|---|---|---| | 登录会话 | `suixinkan.session.v1` JSON | 拆分为多个 `key_in_*` | 保持拆分结构 | 删除旧会话,强制登录 | | Token | `suixinkan.session.v1[token]` | `key_in_token` | `key_in_token` | 首次升级清空 | | 用户 ID | `suixinkan.session.v1[userId]` | `key_in_user_id` | `key_in_user_id` | 首次升级清空 | | 用户名 | `suixinkan.session.v1[userName]` | `key_in_user_name` | `key_in_user_name` | 首次升级清空 | | 头像 | `suixinkan.session.v1[avatar]` | `key_in_avatar` | `key_in_avatar` | 首次升级清空 | | 手机号 | `suixinkan.session.v1[phone]` | `key_in_phone` | `key_in_phone` | 首次升级清空 | | 账号类型 | `suixinkan.session.v1[accountType]` | `key_in_account_type` | `key_in_account_type` | 首次升级清空 | | 账号类型显示名称 | `suixinkan.session.v1[accountDisplayName]` | `key_in_account_display_name` | `key_in_account_display_name` | 首次升级清空 | | 角色 ID | `suixinkan.session.v1[roleId]` | `P_key_in_role_id` | `A_key_in_role_id` | 不迁移;当前仅用于 legacy 权限匹配兜底 | | 角色编码 | 无独立 Key | `key_in_role_code` | `A_key_in_role_code` | 改为账号作用域 | | 角色名称 | `suixinkan.session.v1[roleName]` | `key_in_role_name` | `A_key_in_role_name` | 改为账号作用域 | | 权限列表 | 无 | `P_key_role_permission_list` | `A_key_role_permission_list` | 不迁移预发布数据 | | 当前权限 | 无 | `P_key_in_permission` | `A_key_in_permission` | 不迁移预发布数据 | | 角色景区列表 | 无 | `P_key_current_role_scenic_list` | `A_key_current_role_scenic_list` | 不迁移预发布数据 | | 当前景区 ID | `suixinkan.session.v1[scenicId]` | `key_in_current_scenic_id` | `A_key_in_current_scenic_id` | 改为账号作用域 | | 当前景区名称 | `suixinkan.session.v1[scenicName]` | `key_in_current_scenic_name` | `A_key_in_current_scenic_name` | 改为账号作用域 | | 当前门店 ID | `suixinkan.session.v1[storeId]` | `key_in_current_store_id` | `A_scenic_S_scenic_store_id` | 改为账号+景区作用域 | | 当前门店名称 | `suixinkan.session.v1[storeName]` | 无 | 不再持久化 | 删除旧值 | | 常用菜单 | `home.common.menu.uris` | `P_role_R_common_uris` | `A_role_R_common_uris` | 不迁移旧菜单;仅统一作用域,Android 精确后缀不同 | | 收款语音 | `payment.voice.broadcast.enabled` | `P_key_is_open_receive_voice` | `A_key_is_open_receive_voice` | 重置为默认值 | | 在线状态 | 无 | `P_key_online_status` | `A_key_online_status` | 改为账号作用域 | | 上次位置上报时间 | 无 | `P_key_last_location_report_time` | `A_key_last_location_report_time` | 改为账号作用域 | | 定位提醒分钟数 | 无 | `P_key_location_reminder_minutes` | `A_key_location_reminder_minutes` | 改为账号作用域 | | 当前排队点位 ID | `account_O_scenic_S_scenic_queue_selected_spot_id` | `P__scenic_queue_punch_spot_id` | `A_scenic_S_scenic_queue_punch_spot_id` | 不迁移 | | 当前排队点位名称 | `account_O_scenic_S_scenic_queue_selected_spot_name` | `P__scenic_queue_punch_spot_name` | `A_scenic_S_spot_T_scenic_queue_punch_spot_name` | 不迁移 | | 排队播报备注 | `account_O_scenic_S_scenic_queue_custom_tts_text_spot_T` | `P__scenic_queue_remark` | `A_scenic_S_spot_T_scenic_queue_remark` | 不迁移 | | 排队设置快照 | `account_O_scenic_S_scenic_queue_settings_snapshot_spot_T` | `P__scenic_queue_settings_snapshot` | `A_scenic_S_spot_T_scenic_queue_settings_snapshot` | 不迁移 | | 排队预设语音 | `account_O_scenic_S_scenic_queue_preset_voices_spot_T` | `P__scenic_queue_preset_voices_v1` | `A_scenic_S_spot_T_scenic_queue_preset_voices_v1` | 不迁移 | | 离线 TTS | 无 | `P__scenic_queue_offline_tts_v1` | `A_scenic_S_scenic_queue_offline_tts_v1` | 默认值对齐 Android 为 `true` | ## 首次升级需要清理的旧 Key 已新增整型版本标记 `suixinkan.cache.schema.version`。当版本小于目标缓存版本时执行一次清理,全部清理完成后再写入目标版本,避免每次启动重复清空用户重新登录后产生的数据。 ### 会话与推送 - `suixinkan.session.v1` - `apns_device_token` - `apns_uploaded_token` 同时清空重构版预发布阶段可能存在的登录与账号字段,以确保升级后强制重新登录: - `key_in_token` - `key_last_login_username` - `key_in_user_id` - `key_in_user_name` - `key_in_real_name` - `key_in_avatar` - `key_in_phone` - `key_in_account_type` - `key_in_account_display_name` - `key_in_role_id` - `key_in_role_code` - `key_in_role_name` - `key_in_current_scenic_id` - `key_in_current_scenic_name` - `key_in_current_store_id` `key_privacy_agreement_accepted` 是否清理应服从“旧偏好全部重置”的既定策略,因此本次也应清理。 同时清理早期重构实现可能写入的以下未分域业务字段: - `key_role_permission_list` - `key_in_permission` - `key_current_role_scenic_list` - `key_online_status` - `key_last_location_report_time` - `key_location_reminder_minutes` - `key_is_open_receive_voice` ### 首页与收款 - `home.common.menu.uris` - `home.common.menu.android.baseline.v2` - `home.routing.unknown.records` - `payment.voice.broadcast.enabled` ### 排队全局偏好 - `scenic_queue_tts_enabled` - `scenic_queue_background_poll_enabled` - `scenic_queue_selected_spot_id` - `scenic_queue_selected_spot_name` - `scenic_queue_custom_tts_text` - `scenic_queue_photo_estimate_seconds` - `scenic_queue_broadcast_interval_seconds` - `scenic_queue_countdown_threshold_seconds` - `scenic_queue_show_start_shooting_button` - `scenic_queue_auto_call_ahead_count` - `scenic_queue_quick_call_button_enabled` - `scenic_queue_prepare_call_button_enabled` - `scenic_queue_config_logs` - `scenic_queue_settings_snapshot` - `scenic_queue_preset_voices` ### 排队动态缓存 清理符合以下已知格式的旧 Key,不应扩大为删除整个 `UserDefaults` domain: - `account_O_scenic_S_scenic_queue_selected_spot_id` - `account_O_scenic_S_scenic_queue_selected_spot_name` - `account_O_scenic_S_scenic_queue_custom_tts_text_spot_T` - `account_O_scenic_S_scenic_queue_settings_snapshot_spot_T` - `account_O_scenic_S_scenic_queue_preset_voices_spot_T` - `scenic_S_spot_T_scenic_queue_settings_snapshot` - `scenic_S_scenic_queue_settings_snapshot` - `scenic_S_spot_T_scenic_queue_preset_voices` ### 重构版预发布缓存 清理使用旧 `P` 前缀的以下 Key 家族: - `P_key_in_role_id` - `P_key_role_permission_list` - `P_key_in_permission` - `P_key_current_role_scenic_list` - `P_key_online_status` - `P_key_last_location_report_time` - `P_key_location_reminder_minutes` - `P_key_is_open_receive_voice` - `P_role_R_common_uris` - `P__scenic_queue_punch_spot_id` - `P__scenic_queue_punch_spot_name` - `P__scenic_queue_remark` - `P__scenic_queue_settings_snapshot` - `P__scenic_queue_offline_tts_v1` - `P__scenic_queue_preset_voices_v1` 其中 `P__scenic_queue_*` 的双下划线来自当前 Key 生成器和自带前导下划线的后缀叠加,属于需要淘汰的错误格式。 ## 不应清理的内容 - 禁止调用 `removePersistentDomain(forName:)` 清空应用的整个 `UserDefaults` domain,应只删除明确列出的 Key 和严格匹配的动态 Key。 - 不删除 `Documents/CloudDownloads`。该目录可能包含用户主动下载的文件,不属于普通缓存。 - 不删除 SDK 缓存、图片缓存或其他未列入本次升级范围的用户文件。 - 不做旧 `suixinkan.session.v1`、排队设置、常用菜单或收款偏好的解码与迁移。 ## 发布前备注 - 旧线上版本为 `1.0.1 (2)`;当前覆盖升级版本已设置为 `1.1.4 (1010401)`。 - 重构版当前未保留旧工程的 APNs entitlement、远程通知后台能力、回调和设备 Token 上传流程。该问题不是缓存 Key 修改,但若线上仍需要推送,发布前必须单独恢复并验证。清理 `apns_device_token` 与 `apns_uploaded_token` 后,恢复推送时应重新绑定设备 Token。 - `Application Support/suixinkan/cloud_transfer_tasks.json` 当前为全账号共享文件,存在账号切换后读取其他账号任务的风险。该问题不是 `UserDefaults` Key 迁移的一部分,应作为独立发布风险处理。 - 本方案已在重构版中实施:应用首次创建 `AppStore` 时按 `suixinkan.cache.schema.version` 执行一次定向清理,并切换到表格中的统一 Key;不删除未列入范围的 `UserDefaults` 或用户文件。