日期:2026-06-02
状态:待评审
范围:将一批已注册于海外站点的历史用户迁移为“国内主账号 + 海外同步从账号”模型,保留原登录方式和海外入口,不迁移历史日志,不考虑订阅迁移。
当前项目已经具备基础的 region_sync 主从同步能力,核心方向是:
synced user 消费额度后回写主站synced_quota本次需求不是新增长期双主能力,而是把一批“历史上注册在海外站的用户”迁移为与现有同步用户一致的模型:
synced user本次不考虑活动订阅用户,因为当前确认这批用户不存在订阅数据。
本次设计明确不做以下事情:
topups、subscription_orders、usage logs、quota logs推荐方案为:单独迁移界面 + 后端迁移任务内核。
source=syncedremote_user_id 指向国内主账号迁移完成后,国内站是该批用户唯一主账号归属地。之后额度主数据、用户主数据、后续运营动作都以国内站为准。
不删除海外账号,不替换海外用户主键,不强制切登录入口。迁移后只是将其降级为同步从账号。
对 username、email、OAuth 绑定冲突,不做隐式自动合并。允许自动推荐,但最终决策必须可审计。
整个迁移链路按“可能中断、可能重试、可能部分成功”的现实场景设计,所有关键步骤必须可安全重跑。
跨站点、带人工决策的迁移不适合整批一键回滚。系统只提供受限的单用户回退能力。
新增一个独立迁移模块,由以下部分组成:
职责分工如下:
syncedsynced 用户”本次不复用 quota_sync_logs,新增两张迁移专用表。
user_migration_batches表示一次迁移批次。
建议字段:
idnamesource_regiontarget_regionstatus
draftscannedreadyrunningcompletedfailedcancelledtotal_usersconflict_usersmigrated_usersfailed_usersoperator_idcreated_atupdated_atuser_migration_items表示一个海外用户在某个批次中的迁移决议和执行结果。
建议字段:
idbatch_idsource_user_idtarget_user_idsource_usernamesource_emailsource_quotaconflict_flags
username/email/oauth/aff_code 等冲突resolution_strategy
create_newmerge_into_existingskipstatus
pending_scanconflictreadymigratingmigratedskippedfailederror_messagesnapshot
migration_token
migrated_atcreated_atupdated_atuser_migration_items(batch_id, source_user_id) 唯一migration_token 保证不会重复做额度叠加draft:已创建,未扫描scanned:预检查完成,但仍可能存在冲突项ready:所有项都已变成 ready 或 skippedrunning:正在执行迁移completed:全部可迁移项执行成功failed:存在失败项,需要人工修正后重试cancelled:人工取消pending_scan:尚未预检查conflict:发现冲突,等待人工决策ready:策略已明确,可以执行迁移migrating:迁移进行中migrated:迁移成功skipped:人工跳过failed:执行失败,允许单项重试从海外站迁移以下主数据:
users 主字段display_nameemailstatusrolegroupcreated_atsettingaff_codegithub_iddiscord_idoidc_idwechat_idtelegram_idlinux_do_iduser_oauth_bindingstopupssubscription_ordersquota_sync_logs海外站为国内站提供用户快照接口,返回:
quotagroupstatusaff_codecreated_atsettingdisplay_name对每个海外用户,在国内检查:
username 是否已存在email 是否已存在aff_code 是否已存在不能直接按 provider_id 比较,必须先通过 custom_oauth_providers.slug 建立海外与国内的 provider 映射,再按:
provider_slugprovider_user_id进行冲突判断。
必须人工处理,不能自动迁移:
可以由界面推荐合并,但仍需要人工确认:
不阻塞迁移,可自动处理:
aff_code 冲突display_name 冲突create_newaff_code 冲突:推荐 create_newsynced 副本:推荐 merge_into_existingsynced 副本),该条目必须失败并要求重新扫描,不得静默沿用旧决议。执行顺序不能改变,必须先构建国内主账号,再将海外原账号降级。执行阶段读取的“最新快照”必须发生在冻结窗口内。
create_newInsert()user_oauth_bindingsaff_code 冲突user_migration_items.target_user_idusernamepasswordemaildisplay_namestatusrolegroupcreated_atsettinguser_oauth_bindings
slug 将海外 provider_id 映射为国内 provider_idaff_codecn_user_idsynced
source = syncedremote_user_id = cn_user_idsynced_quota = 国内提交后的最终额度last_sync_at = nowmerge_into_existingsynced 副本国内最终额度 = 国内原额度 + 执行时最新海外额度migration_token 保证每个迁移项最多叠加一次syncedsynced user 逻辑天然支持这种模型不建议国内直接操作海外数据库,建议由海外站提供内部接口执行切换。
GET /api/internal/migration/users
source=local 的海外用户GET /api/internal/migration/users/:id
GET /api/internal/migration/users/check-synced-copy?cn_user_id=:id
synced 副本POST /api/internal/migration/users/:id/convert-to-synced
synced接口需要做以下校验:
remote_user_id 不为空synced 但 remote_user_id 与请求不一致,返回冲突错误幂等语义:
source=syncedremote_user_id 与请求目标一致新增一个 root-only 页面:用户迁移中心。
页面建议拆为四个区域:
展示:
展示用户级迁移项列表:
对单个冲突用户允许操作:
create_newmerge_into_existingskip并展示推荐策略及依据。
提供以下操作:
新增一组管理端 API,不混入普通 region_sync。
POST /api/user-migrations/batches
GET /api/user-migrations/batches
GET /api/user-migrations/batches/:id
POST /api/user-migrations/batches/:id/scan
POST /api/user-migrations/items/:id/resolve
ready 或 skipped,批次自动转为 readyPOST /api/user-migrations/batches/:id/execute
POST /api/user-migrations/batches/:id/retry
POST /api/user-migrations/batches/:id/verify
幂等是本设计的核心要求。
scansource_user_idcreate_new 条目如已存在 target_user_id,不得再次创建账号merge_into_existing 额度叠加必须有一次性保护syncedremote_user_id 一致通过 migration_token 或同等级唯一标识保证:
本次迁移不要求全站停机,但 V1 版本将“冻结窗口”定义为执行前的强制运维前置条件,而不是可选建议。
冻结范围应仅限该批次用户:
执行要求:
目的:
本次设计不提供整批自动回滚,只提供有限的单用户回退能力。
create_new 的可回退场景如果国内主账号刚创建成功,但海外尚未转换为 synced:
如果海外已经完成降级:
source=localremote_user_idquotamerge_into_existing 的回退限制不建议自动回滚,因为:
这类只建议人工修正。
create_new 类型执行受限回退系统至少应提供以下自动校验:
source=syncedremote_user_id 指向正确国内账号synced_quota 与国内额度一致,或在允许误差范围内一致建议按以下顺序推进:
当前用户创建逻辑会对密码再次哈希。迁移实现必须提供专用导入路径,直接写入海外已有哈希值。
provider_id 迁移必须按 slug 对齐国内外 provider 后再复制绑定,否则绑定会错位。
SyncUserCreate现有 region_sync 的建用户请求字段不足,不能覆盖:
本次迁移需要单独实现迁移接口和迁移服务。
synced user 模型兼容本方案完成后,海外侧行为应回归现有同步用户路径:
synced_quota本次海外历史用户迁移的最佳路径是:
synced这条路径最符合当前代码库已有的 region_sync 架构,改动集中、风险可控、审计清晰,且能保留用户现有登录方式和海外站访问体验。