feat: add puzzle clear template runtime

This commit is contained in:
2026-06-03 22:11:46 +08:00
parent 6e74cf5add
commit 1b5e098225
148 changed files with 19588 additions and 241 deletions

View File

@@ -455,19 +455,42 @@ fn export_auth_store_snapshot_from_tables_tx(
.find(&AUTH_STORE_PROJECTION_META_ID.to_string())
.map(|row| row.updated_at.to_micros_since_unix_epoch());
let snapshot = build_auth_store_snapshot_from_rows(users, identities, sessions)?;
if let Some(updated_at_micros) = updated_at_micros {
upsert_auth_store_snapshot_rows(ctx, &snapshot, updated_at_micros)?;
}
let snapshot_json = serde_json::to_string_pretty(&snapshot)
.map_err(|error| format!("序列化认证快照失败:{error}"))?;
Ok(AuthStoreSnapshotRecord {
snapshot_json: Some(snapshot_json),
updated_at_micros,
})
}
fn build_auth_store_snapshot_from_rows(
users: Vec<UserAccount>,
identities: Vec<AuthIdentity>,
sessions: Vec<RefreshSession>,
) -> Result<PersistentAuthStoreSnapshot, String> {
let valid_user_ids = users
.iter()
.map(|user| user.user_id.clone())
.collect::<std::collections::HashSet<_>>();
let mut phone_identity_by_user_id = std::collections::HashMap::new();
let mut phone_to_user_id = std::collections::HashMap::new();
let mut wechat_identity_by_provider_uid = std::collections::HashMap::new();
let mut user_id_by_provider_union_id = std::collections::HashMap::new();
for identity in identities {
if !valid_user_ids.contains(&identity.user_id) {
continue;
}
match identity.provider.as_str() {
"phone" => {
let phone_number = identity
.phone_e164
.clone()
.unwrap_or_else(|| identity.provider_uid.clone());
phone_to_user_id.insert(phone_number.clone(), identity.user_id.clone());
phone_identity_by_user_id.insert(identity.user_id, phone_number);
}
"wechat" => {
@@ -490,6 +513,7 @@ fn export_auth_store_snapshot_from_tables_tx(
}
let mut next_user_id = 1_u64;
let mut phone_to_user_id = std::collections::HashMap::new();
let mut users_by_username = std::collections::HashMap::new();
for user in users {
if let Some(numeric_id) = user
@@ -499,6 +523,13 @@ fn export_auth_store_snapshot_from_tables_tx(
{
next_user_id = next_user_id.max(numeric_id.saturating_add(1));
}
let phone_number = user
.phone_number_e164
.clone()
.or_else(|| phone_identity_by_user_id.remove(&user.user_id));
if let Some(phone_number) = phone_number.clone() {
phone_to_user_id.insert(phone_number, user.user_id.clone());
}
let auth_user = AuthUserSnapshot {
id: user.user_id.clone(),
public_user_code: user.public_user_code,
@@ -519,9 +550,7 @@ fn export_auth_store_snapshot_from_tables_tx(
user: auth_user,
password_hash: user.password_hash,
password_login_enabled: user.password_login_enabled,
phone_number: user
.phone_number_e164
.or_else(|| phone_identity_by_user_id.remove(&user.user_id)),
phone_number,
},
);
}
@@ -554,7 +583,7 @@ fn export_auth_store_snapshot_from_tables_tx(
);
}
let snapshot = PersistentAuthStoreSnapshot {
Ok(PersistentAuthStoreSnapshot {
next_user_id,
users_by_username,
phone_to_user_id,
@@ -562,16 +591,6 @@ fn export_auth_store_snapshot_from_tables_tx(
session_id_by_refresh_token_hash,
wechat_identity_by_provider_uid,
user_id_by_provider_union_id,
};
if let Some(updated_at_micros) = updated_at_micros {
upsert_auth_store_snapshot_rows(ctx, &snapshot, updated_at_micros)?;
}
let snapshot_json = serde_json::to_string_pretty(&snapshot)
.map_err(|error| format!("序列化认证快照失败:{error}"))?;
Ok(AuthStoreSnapshotRecord {
snapshot_json: Some(snapshot_json),
updated_at_micros,
})
}
@@ -710,4 +729,47 @@ mod tests {
auth_store_snapshot_row_ids(&after)
);
}
#[test]
fn auth_export_ignores_phone_identity_without_user_account() {
let live_user = UserAccount {
user_id: "user_live".to_string(),
public_user_code: "SY-00000001".to_string(),
username: "phone_live".to_string(),
display_name: "测试玩家".to_string(),
avatar_url: None,
phone_number_masked: Some("138****8000".to_string()),
phone_number_e164: Some("+8613800008000".to_string()),
login_method: "phone".to_string(),
binding_status: "active".to_string(),
wechat_bound: false,
password_hash: "hash-live".to_string(),
password_login_enabled: true,
token_version: 1,
user_tags: Some(vec![]),
};
let orphan_identity = AuthIdentity {
identity_id: "authi_phone_orphan".to_string(),
user_id: "user_deleted".to_string(),
provider: "phone".to_string(),
provider_uid: "+8613900009999".to_string(),
provider_union_id: None,
phone_e164: Some("+8613900009999".to_string()),
display_name: None,
avatar_url: None,
};
let snapshot =
build_auth_store_snapshot_from_rows(vec![live_user], vec![orphan_identity], vec![])
.expect("auth rows should export");
assert_eq!(
snapshot.phone_to_user_id,
std::collections::HashMap::from([(
"+8613800008000".to_string(),
"user_live".to_string()
)])
);
assert!(!snapshot.phone_to_user_id.contains_key("+8613900009999"));
}
}